Python自从在python3.5正式将协程做为底层技术引入之后,关于如何构建标准的异步web框架的讨论就源源不绝。最终等来了asgi草案的发布。APIStar是一个基于Python3.5的微框架,支持asgi和wsgi两种协议。主要特性如下:
- Schema generation - Support for automatically generating OpenAPI schemas.
- Expressive - Type annotated views, that make for expressive, testable code.
- Performance - Dynamic behaviour for determining how to run each view makes API Star incredibly efficient.
- Throughput - Support for asyncio allows for building high-throughput non-blocking applications.
官方的介绍还是比较抽象的。大致翻译一下就是说:
- 可以生成一个openapi风格的Schema,构建好web服务之后,通过访问http://127.0.0.1:8000/schema返回json格式的全部api,其中会包含一些api的注释说明,一些参数的注释(这个查看源码后发现只留下了接口,并没有实现)等等。
- 第二点我感觉是最能吸引我的,框架大量使用了python3.5之后的新特性,泛型注释。姑且这么翻译吧,就是说可以对一个函数参数声明及返回值加上类型注释,这对IDE的实现是一件好事情,虽然传入非法类型不会出错,但是可以起到提醒的作用。APIStar使用这个特性,对所有web handler的参数进行了校验,使参数校验这件事变的更加优雅。
- 我感觉这点涉及到的主要特性应该是依赖注入吧,对,你没有看错,的确与java spring的依赖注入有相似之处。框架通过参数声明上的类型注释,动态的从容器中获取参数,并注入其中。通过继承Component实现resolve方法,其提供的返回值会存储到容器中。可以想象,对于公司etcd中注册的服务,通过依赖注入的方式传递会变的更加优雅。
- 吞吐量这个不用说了,python官方之所以不惜增加两个关键字和7个魔法方法力推协程,说明了python试图通过协程甩掉性能差的帽子,当然,一切都是值得的,在搭配asyncio event loop 接口的C实现uvloop之后,python协程框架如sanic性能直接超过nodejs。sanic作为python3.5之后最火的异步框架,因其在实现上毫无亮点,且不支持asgi协议,本文不再赘述。
学习APIStar,最有效的方式是直接查看源码,但需要对python特性有较深的认识,如若不然,退而求其次,查看其官方文档也是极好的。
APIStar 文档地址:
API Star - DocsAPIStar github:
encode/apistar那么本文到此就结束了吗?No, no, no。
针对如何优雅的构建多模块架构这一问题,我特意花了一天的时间,对APIStar进行了简单的二次封装,并制定一些脚手架,以方便构建与开发。
项目地址:
ShichaoMa/star_builder安装方法:pip install star-builder
一般来说公司在进行web开发时会将不同的业务分成多个业务模块,APIStar同样也可以,通过在routes中添加Include,来包装一个模块下的所有routes。类似于flask的blueprint。
如官方文档给出的demo
myproject/users.py
from apistar import App, Route, exceptions
USERS = {1: 'hazel', 2: 'james', 3: 'ana'}
def list_users(app: App) -> list:
return [
{
'username': username,
'url': app.reverse_url('users:get_user', user_id=user_id)
} for user_id, username in USERS.items()
]
def get_user(app: App, user_id: int) -> dict:
if user_id not in USERS:
raise exceptions.NotFound()
return {
'username': USERS[user_id],
'url': app.reverse_url('users:get_user', user_id=user_id)
}
routes = [
Route('/', method='GET', handler=list_users),
Route('/{user_id}', method='GET', handler=get_user),
]
app.py:
from apistar import App, Include
from myproject import users
routes = [
Include('/users', name='users', routes=users.routes),
...
]
app = App(routes=routes)
为了降低程序员的开发难度,我针对上面的步骤进行了抽象。通过一个类就可以达到上述效果
from apistar import App, http, exceptions
from star_builder import Service, route, get, post
@route("/user")
class UserService(Service):
USERS = {1: 'hazel', 2: 'james', 3: 'ana'}
@get("/")
def list_users(self: Service, app: App) -> list:
return [
{
'username': username,
'url': app.reverse_url('users:get_user', user_id=user_id)
} for user_id, username in self.USERS.items()
]
@get('/{user_id}')
def get_user(self: Service, app: App, user_id: int) -> dict:
if user_id not in self.USERS:
raise exceptions.NotFound()
return {
'username': self.USERS[user_id],
'url': app.reverse_url('users:get_user', user_id=user_id)
}
通过继承star_builder中的Service类,该服务模块被加入到了服务发现的计划中,在app启动时,会自动发现该服务,并作为一个Include加入到上级Service的routes中。同时,服务还支持嵌套。你可以在User包下面的子包中编写
from apistar import App, http
from star_builder import Service, route, get, post
from .. import UserService
@route("/idcard")
class IdcardService(UserService):
@get("/get")
async def get_id(self: Service):
return {"id": 1234}
只需要继承自UserService,就可以得到一个子Service,最终的url会为/user/idcard/get。在定义好Service之后,如何启动app呢?请继续往下看。在根目录创建一个start.py,输入
import logging
from uvicorn import run
from star_builder import Application, ServiceComponent
def main():
logging.basicConfig(level=logging.DEBUG)
app = Application(components=[ServiceComponent()],
static_dir="static",
template_dir="templates")
run(app)
if __name__ == "__main__":
main()
uvicorn是APIStar官方提供的一个ASGI server实现,底层使用了uvloop。如上所示,可以选择直接启动。也可以选择使用gunicorn启动,在命令行输入uvicorn start:app。start为可以python搜索路径上找到的模块,app为其中一ASGI application实例。
这就完了?No, no, no。
刚才介绍了Service的写法,感觉麻烦吗,好像还行,能不能再简单一点?能!
star_builder提供了脚手架来自动生成项目的Service。

来,小试牛刀一把!


目录结构如上。每个Service都是一个包,之后在我们可以在包里面编写model以及持久层的类。
以上就是今天的全部内容。谢谢阅读。