2019独角兽企业重金招聘Python工程师标准>>>

介绍

使用python做web开发面临的一个最大的问题就是性能,在解决C10K问题上显的有点吃力。有些异步框架Tornado、Twisted、Gevent 等就是为了解决性能问题。这些框架在性能上有些提升,但是也出现了各种古怪的问题难以解决。

在python3.6中,官方的异步协程库asyncio正式成为标准。在保留便捷性的同时对性能有了很大的提升,已经出现许多的异步框架使用asyncio。

使用较早的异步框架是aiohttp,它提供了server端和client端,对asyncio做了很好的封装。但是开发方式和最流行的微框架flask不同,flask开发简单,轻量,高效。

微服务是最近最火开发模式,它解决了复杂性问题,提高开发效率,便于部署等优点。

正是结合这些优点, 以Sanic为基础,集成多个流行的库来搭建微服务。 Sanic框架是和Flask相似的异步协程框架,简单轻量,并且性能很高。

本项目就是以Sanic为基础搭建的微服务框架。

特点

  • 使用sanic异步框架,简单,轻量,高效。
  • 使用uvloop为核心引擎,使sanic在很多情况下单机并发甚至不亚于Golang。
  • 使用asyncpg为数据库驱动,进行数据库连接,执行sql语句执行。
  • 使用aiohttp为Client,对其他微服务进行访问。
  • 使用peewee为ORM,但是只是用来做模型设计和migration。
  • 使用opentracing为分布式追踪系统。
  • 使用unittest做单元测试,并且使用mock来避免访问其他微服务。
  • 使用swagger做API标准,能自动生成API文档。

使用

项目地址

sanic-ms

Example

Swagger API

Zipkin Server

服务端

使用sanic异步框架,有较高的性能,但是使用不当会造成blocking, 对于有IO请求的都要选用异步库。添加库要慎重。 sanic使用uvloop异步驱动,uvloop基于libuv使用Cython编写,性能比nodejs还要高。

功能说明:

启动前

@app.listener('before_server_start')
async def before_srver_start(app, loop):queue = asyncio.Queue()app.queue = queueloop.create_task(consume(queue, app.config.ZIPKIN_SERVER))reporter = AioReporter(queue=queue)tracer = BasicTracer(recorder=reporter)tracer.register_required_propagators()opentracing.tracer = tracerapp.db = await ConnectionPool(loop=loop).init(DB_CONFIG)
  • 创建DB连接池
  • 创建Client连接
  • 创建queue, 消耗span,用于日志追踪
  • 创建opentracing.tracer进行日志追踪

中间件

@app.middleware('request')
async def cros(request):if request.method == 'POST' or request.method == 'PUT':request['data'] = request.jsonspan = before_request(request)request['span'] = span@app.middleware('response')
async def cors_res(request, response):span = request['span'] if 'span' in request else Noneif response is None:return responseresult = {'code': 0}if not isinstance(response, HTTPResponse):if isinstance(response, tuple) and len(response) == 2:result.update({'data': response[0],'pagination': response[1]})else:result.update({'data': response})response = json(result)if span:span.set_tag('http.status_code', "200")if span:span.set_tag('component', request.app.name)span.finish()return response
  • 创建span, 用于日志追踪
  • 对response进行封装,统一格式

异常处理

对抛出的异常进行处理,返回统一格式

任务

创建task消费queue中对span,用于日志追踪

异步处理

由于使用的是异步框架,可以将一些IO请求并行处理

Example:

async def async_request(datas):# async handler requestresults = await asyncio.gather(*[data[2] for data in datas])for index, obj in enumerate(results):data = datas[index]data[0][data[1]] = results[index]@user_bp.get('/<id:int>')
@doc.summary("get user info")
@doc.description("get user info by id")
@doc.produces(Users)
async def get_users_list(request, id):async with request.app.db.acquire(request) as cur:record = await cur.fetch(""" SELECT * FROM users WHERE id = $1 """, id)datas = [[record, 'city_id', get_city_by_id(request, record['city_id'])][record, 'role_id', get_role_by_id(request, record['role_id'])]]await async_request(datas)return record

get_city_by_id, get_role_by_id是并行处理。

相关连接

sanic

模型设计 & ORM

Peewee is a simple and small ORM. It has few (but expressive) concepts, making it easy to learn and intuitive to use。

ORM使用peewee, 只是用来做模型设计和migration, 数据库操作使用asyncpg。

Example:

# models.pyclass Users(Model):id = PrimaryKeyField()create_time = DateTimeField(verbose_name='create time',default=datetime.datetime.utcnow)name = CharField(max_length=128, verbose_name="user's name")age = IntegerField(null=False, verbose_name="user's age")sex = CharField(max_length=32, verbose_name="user's sex")city_id = IntegerField(verbose_name='city for user', help_text=CityApi)role_id = IntegerField(verbose_name='role for user', help_text=RoleApi)class Meta:db_table = 'users'# migrations.pyfrom sanic_ms.migrations import MigrationModel, info, dbclass UserMigration(MigrationModel):_model = Users# @info(version="v1")# def migrate_v1(self):#     migrate(self.add_column('sex'))def migrations():try:um = UserMigration()with db.transaction():um.auto_migrate()print("Success Migration")except Exception as e:raise eif __name__ == '__main__':migrations()
  • 运行命令 python migrations.py
  • migrate_v1函数添加字段sex, 在BaseModel中要先添加name字段
  • info装饰器会创建表migrate_record来记录migrate,version每个model中必须唯一,使用version来记录是否执行过,还可以记录author,datetime
  • migrate函数必须以**migrate_**开头

相关连接

peewee

数据库操作

asyncpg is the fastest driver among common Python, NodeJS and Go implementations

使用asyncpg为数据库驱动, 对数据库连接进行封装, 执行数据库操作。

不使用ORM做数据库操作,一个原因是性能,ORM会有性能的损耗,并且无法使用asyncpg高性能库。另一个是单个微服务是很简单的,表结构不会很复杂,简单的SQL语句就可以处理来,没必要引入ORM。使用peewee只是做模型设计

Example:

sql = "SELECT * FROM users WHERE name=$1"
name = "test"
async with request.app.db.acquire(request) as cur:data = await cur.fetchrow(sql, name)async with request.app.db.transaction(request) as cur:data = await cur.fetchrow(sql, name)
  • acquire() 函数为非事务, 对于只涉及到查询的使用非事务,可以提高查询效率
  • tansaction() 函数为事务操作,对于增删改必须使用事务操作
  • 传入request参数是为了获取到span,用于日志追踪
  • TODO 数据库读写分离

相关连接

asyncpg benchmarks

客户端

使用aiohttp中的client,对客户端进行了简单的封装,用于微服务之间访问。

Don’t create a session per request. Most likely you need a session per application which performs all requests altogether. A session contains a connection pool inside, connection reusage and keep-alives (both are on by default) may speed up total performance.

Example:

@app.listener('before_server_start')
async def before_srver_start(app, loop):app.client =  Client(loop, url='http://host:port')async def get_role_by_id(request, id):cli = request.app.client.cli(request)async with cli.get('/cities/{}'.format(id)) as res:return await res.json()@app.listener('before_server_stop')
async def before_server_stop(app, loop):app.client.close()

对于访问不同的微服务可以创建多个不同的client,这样每个client都会keep-alives

相关连接

aiohttp

日志 & 分布式追踪系统

使用官方logging, 配置文件为logging.yml, sanic版本要0.6.0及以上。JsonFormatter将日志转成json格式,用于输入到ES

Enter OpenTracing: by offering consistent, expressive, vendor-neutral APIs for popular platforms, OpenTracing makes it easy for developers to add (or switch) tracing implementations with an O(1) configuration change. OpenTracing also offers a lingua franca for OSS instrumentation and platform-specific tracing helper libraries. Please refer to the Semantic Specification.

装饰器logger

@logger(type='method', category='test', detail='detail', description="des", tracing=True, level=logging.INFO)
async def get_city_by_id(request, id):cli = request.app.client.cli(request)
  • type: 日志类型,如 method, route
  • category: 日志类别,默认为app的name
  • detail: 日志详细信息
  • description: 日志描述,默认为函数的注释
  • tracing: 日志追踪,默认为True
  • level: 日志级别,默认为INFO

分布式追踪系统

  • OpenTracing是以Dapper,Zipkin等分布式追踪系统为依据, 建立了统一的标准。
  • Opentracing跟踪每一个请求,记录请求所经过的每一个微服务,以链条的方式串联起来,对分析微服务的性能瓶颈至关重要。
  • 使用opentracing框架,但是在输出时转换成zipkin格式。 因为大多数分布式追踪系统考虑到性能问题,都是使用的thrift进行通信的,本着简单,Restful风格的精神,没有使用RPC通信。以日志的方式输出, 可以使用fluentd, logstash等日志收集再输入到Zipkin。Zipkin是支持HTTP输入的。
  • 生成的span先无阻塞的放入queue中,在task中消费队列的span。后期可以添加上采样频率。
  • 对于DB,Client都加上了tracing

相关连接

opentracing zipkin jaeger

API接口

api文档使用swagger标准。

Example:

from sanic_ms import doc@user_bp.post('/')
@doc.summary('create user')
@doc.description('create user info')
@doc.consumes(Users)
@doc.produces({'id': int})
async def create_user(request):data = request['data']async with request.app.db.transaction(request) as cur:record = await cur.fetchrow(""" INSERT INTO users(name, age, city_id, role_id)VALUES($1, $2, $3, $4, $5)RETURNING id""", data['name'], data['age'], data['city_id'], data['role_id'])return {'id': record['id']}
  • summary: api概要
  • description: 详细描述
  • consumes: request的body数据
  • produces: response的返回数据
  • tag: API标签
  • 在consumes和produces中传入的参数可以是peewee的model,会解析model生成API数据, 在field字段的help_text参数来表示引用对象
  • http://host:ip/openapi/spec.json 获取生成的json数据

相关连接

swagger

Response 数据

在返回时,不要返回sanic的response,直接返回原始数据,会在Middleware中对返回的数据进行处理,返回统一的格式,具体的格式可以[查看]

单元测试

单元测试使用unittest。 mock是自己创建了MockClient,因为unittest还没有asyncio的mock,并且sanic的测试接口也是发送request请求,所以比较麻烦. 后期可以使用pytest。

Example:

from sanic_ms.tests import APITestCase
from server import appclass TestCase(APITestCase):_app = app_blueprint = 'visit'def setUp(self):super(TestCase, self).setUp()self._mock.get('/cities/1',payload={'id': 1, 'name': 'shanghai'})self._mock.get('/roles/1',payload={'id': 1, 'name': 'shanghai'})def test_create_user(self):data = {'name': 'test','age': 2,'city_id': 1,'role_id': 1,}res = self.client.create_user(data=data)body = ujson.loads(res.text)self.assertEqual(res.status, 200)
  • 其中_blueprint为blueprint名称
  • 在setUp函数中,使用_mock来注册mock信息, 这样就不会访问真实的服务器, payload为返回的body信息
  • 使用client变量调用各个函数, data为body信息,params为路径的参数信息,其他参数是route的参数

代码覆盖

coverage erase
coverage run --source . -m sanic_ms tests
coverage xml -o reports/coverage.xml
coverage2clover -i reports/coverage.xml -o reports/clover.xml
coverage html -d reports
  • coverage2colver 是将coverage.xml 转换成 clover.xml,bamboo需要的格式是clover的。

相关连接

unittest coverage

异常处理

使用 app.error_handler = CustomHander() 对抛出的异常进行处理

Example:

from sanic_ms.exception import ServerError@visit_bp.delete('/users/<id:int>')
async def del_user(request, id):raise ServerError(error='内部错误',code=10500, message="msg")
  • code: 错误码,无异常时为0,其余值都为异常
  • message: 状态码信息
  • error: 自定义错误信息
  • status_code: http状态码,使用标准的http状态码

转载于:https://my.oschina.net/songcser/blog/1600616

基于Sanic的微服务基础架构相关推荐

  1. python sanic orm_基于sanic的微服务框架 - 架构分析

    感谢@songcser分享的<基于sanic的微服务基础架构>https://github.com/songcser/sanic-ms 最近正在学习微服务,发现这个repo不错,但不完整, ...

  2. Aooms_基于SpringCloud的微服务基础开发平台实战_002_工程构建

    为什么80%的码农都做不了架构师?>>>    一.关于框架更名的一点说明 最近在做年终总结.明年规划.还有几个项目需要了结.出解决方案,事情还比较多,死了不少脑细胞,距离上一篇文章 ...

  3. 微服务基础架构的5个关键问题

    作者介绍: 杨波,微服务技术专家,曾主导了拍拍贷微服务架构体系建设.任职携程技术研发总监期间,主导了携程大规模SOP体系建设,也是极客时间「微服务架构实战160讲」课程讲师. 一.OAuth2的四种模 ...

  4. GIAC 2020 全球互联网架构大会演讲实录:基于TarsGo的微服务技术架构实践

    2020年8月14日-15日,GIAC 2020 全球互联网架构大会于上周五正式在深圳开幕. GIAC(GLOBAL INTERNET ARCHITECTURE CONFERENCE)是长期关注互联网 ...

  5. 【微服务】微服务安全 - 如何保护您的微服务基础架构?

    在当今行业使用各种软件架构和应用程序的市场中,几乎不可能感觉到您的数据是完全安全的.因此,在使用微服务架构构建应用程序时,安全问题变得更加重要,因为各个服务相互之间以及客户端之间进行通信.因此,在这篇 ...

  6. 日10亿级处理,基于云的微服务架构

    德比软件:基于云的微服务架构 作者:朱攀,德比软件架构师,同济大学研究生,2007 年 2 月加入德比软件(DerbySoft),拥有 10 年以上的软件架构和开发经验.目前主要负责公司数据对接平台的 ...

  7. 基于 Docker 的微服务架构实践

    http://dockone.io/article/4887 前言 基于 Docker 的容器技术是在2015年的时候开始接触的,两年多的时间,作为一名 Docker 的 DevOps,也见证了 Do ...

  8. docker容器 eureka 集成_微服务:基于 Docker 的微服务架构之分布式企业级实践参考...

    编者按:本文分享自CSDN技术博客,作者为 FlyWine,所有权归原著者.若有不妥,联系本头条号以做必要处理. 目录 Microservice 和 Docker 服务发现模式 客户端发现模式 Net ...

  9. javaweb k8s_K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程...

    K8S微服务核心架构学习指南 ASP.NET Core微服务基于K8S 架构师必备Kubernetes教程 课程内容是关于Kubernetes微服务架构学习课程,基于K8S开展ASP.NET核心进行微 ...

最新文章

  1. “冗余”的参数(变量) —— 提升访问的效率
  2. CoFun 1612 单词分组(容斥)
  3. JMetro版本11.5.11和8.5.11发布
  4. mongodb lbs java_LBS JAVA Spring mongoDB
  5. matlab三位画图_matlab 3D绘图详解(示例代码)
  6. 怎么导出oracle库,【DG】怎么从Oracle备库导出数据
  7. 这操作厉害:怎么样发布你的 Python模块给别人 “pip install”
  8. python特性有什么_举例介绍Python中的25个隐藏特性
  9. java txt 分段读取_Java 读取TXT文件的多种方式
  10. Git(1)——初始版本控制工具
  11. Kali Linux 暴力破解学校办公室WiFi 总结
  12. EasyGBS国标视频云服务平台可以获取录像却无法播放是什么原因?
  13. Neo4j CQL基础
  14. 计算机电源直接连接哪两根线才能开机,电脑电源开关线是哪两根
  15. JS事件绑定的几种方式
  16. List 列表的用法
  17. 网络规划师上午备考知识点(一)(万能协议篇)
  18. 全新深度学习,机器学习,模式识别,软件编程,面经资料汇总
  19. 2023年租房投影仪推荐,出租屋投影仪值得买吗?又该怎么选择?
  20. OV7670摄像头的相关参数及时序分析

热门文章

  1. Python3 casefold() 方法
  2. Swift - UITableView里的cell底部分割线左侧靠边
  3. 交叉编译Python-2.7.13到ARM(aarch32)平台
  4. SQL join中on与where区别
  5. Linux课程实践一:Linux基础实践(SSH)
  6. 取消chrome浏览器下input和textarea的默认样式
  7. 格林威治时间(Tue Jan 01 00:00:00 CST 2019)[ Date ]转化 为 [ 2019-01-01 10:10:10 ]
  8. idea部署项目com.intellij.javaee.oss.admin.jmx.JmxAdminException-未使用最新版本的war包
  9. HDOJ 2030-汉字统计
  10. 微信小程序 全局共享数据