经过大概几年的使用,我深深感受到 Python 的 asyncio 生态还是跟闹着玩的一样,于是打算 CURD 业务部分还是迁回同步比较好,使用 gevent 包装,性能也并不弱。
而 ASGI 迁回 WSGI,面临最大的问题就是如何把 ASGI Lifespan 在 WSGI 项目里实现了。WSGI 协议的设计里并没有给出对应的功能,于是只能在 WSGI Server 和 WSGI Framework 里想办法了。大致来说,有如下两种实现。
- 模块加载时,执行启动时需要执行的代码,并且记录状态,避免重复执行。
- 使用服务器程序提供的接口,由服务器程序保证执行。
第一种就是 Django 启动项目时一次性执行代码的实现,Django App ready,但这玩意对我来说没有任何意义。因为我用的是 hintapi。
于是只能用第二种办法。而我查了几种常见的 WSGI Server,发现只有 Gunicorn 支持纯 Python 的 Server Hook。之所以强调纯 Python,是因为 uWSGI 支持 C 写的 Hooks。
Gunicorn 提供了许多接口,覆盖了整个 Service 的各个生命周期。一般 CURD 项目会用到的 Hook 就是 post_worker_init
,其参数由启动时指定的 worker-class
决定,默认的 sync
函数签名如下。
# gunicorn.conf.py
from gunicorn.workers.sync import SyncWorker as Worker
def post_worker_init(worker: Worker) -> None:
pass
时隔两年之后,我自己写了一个 WSGI Server——Zī Bái。兹白提供了三个 hook 函数,通过这些 hook 就能解决 Lifespan 的问题。
before_serve
在服务启动前被调用before_graceful_exit
在服务收到优雅退出的信号后、执行退出代码前执行before_died
在服务被彻底关闭前执行