python hypercorn
### 从Python开发角度聊聊Uvicorn一个异步服务器的自我修养1. 他是什么先别急着把Uvicorn当作一个普通的Web服务器它更像是给Python异步生态设计的一个高速引擎。这么说吧如果你把Django或Flask这样的框架看作一辆家用轿车那Uvicorn就是那个能把这辆车改造成赛车的改装师。它本质上是一个ASGI服务器而不是传统的WSGI服务器。ASGI异步服务器网关接口这个规范说白了就是让Python程序能像Node.js那样处理高并发IO操作。Uvicorn的名字其实挺直白的——它来自UV代表超高速度的uvloop事件循环和corn可能来自unicorn。它最初是作为Starlette框架的子项目诞生的但很快就独立出来因为大家发现它不仅仅是配套工具更是整个Python异步生态的连接器。它依赖uvloop这个用Cython重写的事件循环库所以能在某些场景下比纯Python的asyncio快2-4倍。一个有意思的细节是Uvicorn内部运行时会自动检测你的系统是否支持HTTP/2协议甚至能原生处理WebSocket连接这些都是传统WSGI服务器做不到的。2. 他能做什么最直观的用途当然是跑异步Web框架。比如你用FastAPI写了个API服务直接用python main.py这种老办法跑是不行的因为FastAPI是异步框架需要ASGI服务器来驱动它。Uvicorn就是那个让FastAPI真正跑起来的东西。而且它不仅支持FastAPI只要是符合ASGI规范的框架比如Starlette、Quart甚至通过适配器层的Django Channels它都能跑。但它的能耐不止于此。很多人不知道的是Uvicorn还可以当作异步脚本的执行器。比如你写了个需要做并发网络请求的爬虫脚本可以用uvicorn my_script:app的方式把它包装成一个能接受HTTP请求的服务然后通过服务调用的方式去触发它这样比直接用crontab运行定时任务要灵活得多。更实用的是它的热重载能力。开发的时候加上--reload参数代码一改服务就自动重启配合上代码编辑器的自动保存体验非常接近前端开发中的webpack-dev-server那种即时反馈感。而且Uvicorn的热重载不会丢失连接状态——这一点比用Gunicorn配合Django的时候要舒服不少。还有个小众但好用的功能你可以用Uvicorn搭建一个本地文件服务器。只需要写个简短的ASGI应用就能快速分享文件比开个Nginx方便多了。3. 怎么使用安装特别简单常规操作就是pip install uvicorn。如果你要跑FastAPI应用通常要加上[standard]扩展包它会帮你把uvloop、httptools这些性能组件都装上。跑起来最简单的方式是这样# main.pyfromfastapiimportFastAPI appFastAPI()app.get(/)defread_root():return{Hello:World}然后在终端里执行uvicorn main:app --reload --port 8000。这里main:app的意思是导入main.py文件里的app变量。你可能会觉得奇怪为什么不是直接运行文件这是因为Uvicorn的设计哲学特别强调模块导入——这种机制能让它在热重载时正确追踪代码变更。如果想让服务跑得更稳通常会配上进程管理。很多人喜欢用Gunicorn来管理Uvicorn的工作进程比如gunicorn-w4-kuvicorn.workers.UvicornWorker main:app这里的关键是-k参数指定了worker类为UvicornWorker这样Gunicorn会启动4个Uvicorn进程来分摊负载。但说实话对于大多数中小规模的业务直接用Uvicorn的多worker模式就够了uvicorn main:app --workers 4。配置定制化方面Uvicorn支持通过环境变量或配置文件来调整。比如你想限制请求体大小可以设置UVICORN_LIMIT_CONCURRENCY环境变量。更优雅的方式是写个YAML配置文件# uvicorn.yamlhost:0.0.0.0port:8000workers:2limit_concurrency:100timeout_keep_alive:30然后uvicorn main:app --config uvicorn.yaml就能加载这些配置。4. 最佳实践头一条永远别在生产环境用--reload。这个参数虽然开发方便但会禁用掉很多性能优化而且在生产环境中频繁重启服务会导致连接颠簸。一定要用进程管理工具比如supervisor、systemd或者容器编排系统来管理Uvicorn的生命周期。关于worker数量有个简单的经验法则对于CPU密集型任务worker数等于CPU核心数对于IO密集型任务可以设为核心数的2-4倍。但要注意别设太多因为Python的GIL会让多worker之间的竞争特别激烈。另一个细节是当设置多个workers时每个worker都会创建一个独立的事件循环这意味着你写的全局变量要格外小心——各个worker之间是完全隔离的。日志配置经常被忽略。Uvicorn默认的日志格式是线性的在日志量大的时候很难区分不同请求的时间线。建议自定义日志格式importuvicorn LOG_FORMAT%(asctime)s - %(levelname)s - [%(name)s] - %(message)suvicorn.run(main:app,log_config{version:1,formatters:{default:{format:LOG_FORMAT,},},})另一个容易被忽略的是Uvicorn的优雅关闭机制。当收到SIGTERM信号时Uvicorn会等待所有请求处理完成后再关闭。但如果你写的异步函数里有长时间运行的协程它可能不会主动中断它们。正确做法是在应用层用asyncio.shield保护关键操作或者设置合理的超时时间。部署到生产环境时Uvicorn前面通常会放一个反向代理。很多人喜欢用Nginx但其实Caddy或Traefik对HTTPS证书的管理更省心。Uvicorn本身不处理SSL终止因为它的底层性能优化跟加密通道设计上有冲突所以反向代理是必须的。最后说下性能调优。Uvicorn默认的最大并发连接数是1000如果预估流量超过这个数记得调大--limit-concurrency。另外可以通过--backlog 2048来增大连接队列大小应对突发流量。对于文件上传的场景记得调整--limit-max-requests避免长时间占用worker导致其他请求排队。5. 和同类技术对比先看Gunicorn。Gunicorn是经典的WSGI服务器用pre-fork模型处理请求。如果拿Uvicorn和Gunicorn跑同步框架比如FlaskGunicorn还是有不少优势——它的worker管理更成熟信号处理更完善。但一旦遇到异步框架或者高并发的WebSocket连接Gunicorn就显得力不从心了因为它不支持ASGI。你可以用Gunicorn配合UvicornWorker来跑异步应用但本质上还是多了一层封装效率上比原生Uvicorn略差。再看Daphne。这是Django Channels官方推荐的ASGI服务器但它的开发速度明显慢于Uvicorn而且Daphne的HTTP协议解析用的是纯Python实现性能比Uvicorn差一大截。如果你用Django ChannelsDaphne是必选项但如果用的是FastAPI或Starlette完全没必要碰Daphne。Hypercorn也是一个ASGI服务器它支持HTTP/3和TRIO事件循环这是Uvicorn目前没有的。但Hypercorn的性能测试普遍不如Uvicorn尤其在高并发下差距明显。如果你特别需要HTTP/3支持比如用于实时视频流Hypercorn值得尝试但日常REST API开发还是Uvicorn更靠谱。还有些人在用AIOHTTP直接启动应用这其实就是自己手撸ASGI服务器虽然灵活但很容易在连接管理、超时处理这些细节上出问题。Uvicorn在这些边界情况上已经踩过很多坑直接用现成的工具更省心。最后说下Node.js的服务器比如ExpressKoa和Uvicorn的对比。这俩其实各自有天然优势Node.js的单线程事件循环在处理大量短连接时非常高效而Uvicorn的多进程异步模式在处理长连接和流式数据时有更好的表现。两者在WebSocket支持上都很成熟但Python的生态在数据处理和机器学习的领域要比Node.js强太多。如果要给个结论如果你的应# # 聊聊Hypercorn一个被低估的Python异步服务器它到底是什么Hypercorn这个名字可能让不少人觉得陌生。简单说它是一个支持ASGI和WSGI协议的Python应用服务器负责把Python写的Web应用跑起来处理来自客户端的请求。很多人第一次接触它往往是因为要用Quart或Sanic这样的异步框架发现默认的生产环境服务器就是Hypercorn。和大多数人的直觉不同Hypercorn不仅仅是一个跑起来就行的工具。它实际上是基于uvloop和httptools构建的这两个库都是高性能的Python实现。uvloop把asyncio的事件循环替换成了libuv的实现性能提升非常显著——类比的话就像是把普通汽车的发动机换成了赛车引擎。它能解决什么问题Hypercorn最主要的使用场景就是运行异步Web应用。比如用Quart写的一个REST API或者用Sanic写的实时服务Hypercorn都能把它们跑起来。举个更具体的例子。假设写了一个文件上传服务用户能同时上传多个大文件。用Flask的话因为同步阻塞的特性第二个上传请求得等第一个处理完才能开始。用Quart配合Hypercorn就能同时处理几十个上传任务而且每个任务不会互相阻塞。这就像是收银台从单通道变成了多通道顾客不用排队等了。另一个典型的场景是WebSocket服务。比如做个实时聊天室用户之间的消息要即时推送。Hypercorn原生支持WebSocket协议不需要额外配置就能用。Gunicorn处理WebSocket就麻烦得多要额外配worker class还有各种坑。还有就是Server-Sent Events也就是服务端推送。比如做个股票价格实时更新的页面用Hypercorn跑起来很顺畅。这种场景下Hypercorn的异步特性特别有优势——不需要为每个连接开一个线程或进程一个Event Loop就能管理成千上万个连接。怎么上手用起来安装没什么特别的pipinstallhypercorn简单的启动命令hypercorn myapp:app这里myapp:app的意思是从myapp.py模块里导入app对象。这跟Gunicorn的写法类似。如果用的是Quart框架代码大概长这样fromquartimportQuart appQuart(__name__)app.route(/)asyncdefhello():returnhello world然后hypercorn myapp:app就能跑了。Hypercorn支持不少启动参数有几个比较常用# 绑定到特定端口hypercorn myapp:app-b0.0.0.0:8000# 使用多个worker进程hypercorn myapp:app-w4# 指定日志级别hypercorn myapp:app --log-level debug# 开启reload开发时很方便hypercorn myapp:app--reload要说到配置文件Hypercorn支持用TOML格式。创建一个hypercorn.tomlbind [0.0.0.0:8000] workers 4 log_level warning然后直接hypercorn myapp:app -c hypercorn.toml就行。一些实践经验用Hypercorn跑了几个项目后总结了几条经验。Worker数量需要小心配置。很多人以为把worker设得越多越好但实际上Hypercorn的每个worker都是一个独立的进程有自己的Event Loop。如果服务器是4核CPU设4个worker通常就足够了。设太多反而会因为进程切换开销导致性能下降。再就是关于超时的设置。默认的超时时间可能不太适合所有场景。比如处理大文件上传的任务可能几分钟都处理不完。需要调大--worker-timeouthypercorn myapp:app --worker-timeout300还有个容易被忽略的点Hypercorn在处理长连接时表现得比Gunicorn好很多。因为Gunicorn默认用prefork模式每个worker只能同时处理一个请求。而Hypercorn的异步特性让一个worker能同时处理成百上千个长连接。不过有意思的是Hypercorn在CPU密集型的任务上表现反而一般。因为Python的GIL限制了多线程CPU计算密集的任务放到异步里跑也不会变快。这种时候可能需要配合进程池来处理。和同类工具的比较说到Python的WSGI/ASGI服务器市面上有不少选择。Gunicorn是使用最广泛的WSGI服务器但只支持WSGI。Uvicorn是另一个ASGI服务器跟Hypercorn经常被放在一起比较。Gunicorn的优势是稳定社区大坑少。但要跑异步应用就得额外装uvicorn worker配置起来麻烦一些。Hypercorn开箱就能支持同步和异步应用部署时少了一层配置。Uvicorn和Hypercorn的主要区别在于底层实现。Uvicorn用trio作为底层的事件循环Hypercorn用uvloop。trio在取消操作方面做得更细致uvloop则在吞吐量上更优。实际测试下来在大并发连接的情况下Hypercorn的吞吐量略高一些。但如果是复杂的高频中断场景Uvicorn可能表现更好。还有一点值得提Hypercorn对HTTP/2和WebSocket的支持更完善。Uvicorn虽然也支持但配置起来需要额外的证书处理。Hypercorn在这方面做得更顺手。不过Hypercorn的缺点也很明显。文档不够详细遇到问题经常得翻源码。社区活跃度不如Gunicorn有些bug修复得比较慢。而且对于纯WSGI应用Hypercorn的优势并不明显那还不如直接用Gunicorn稳。总的来说如果项目已经用了异步框架或者需要处理大量的长连接Hypercorn是个值得试的选择。要是项目本来就用Flask或者Django暂时没什么异步需求Gunicorn可能是更稳妥的选项。用是同步的、长时间运行的比如后台任务Gunicorn更适合如果是异步的、需要处理大量短连接的Uvicorn是更优解对于像Django这样的老框架如果能迁移到新版支持的ASGI模式Uvicorn也能带来显著的性能提升。选型没有银弹关键看你的业务场景对实时性、并发量和部署复杂度的要求。