性能提升4倍!使用Granian作为Django项目的ASGI服务器
Granian是Rust写的ASGI服务器,性能比Daphne强点,但离uWSGI还差着远?静态文件支持倒是省事。开发环境够用,生产环境还是得看情况。
目录17 个章节
前言
最近我又继续在开发 DjangoStarter 的新版本了。
之前为了实现 docker 部署,我把 Nginx 打包进了 DjangoStarter 的 compose 配置里了,不过这带来了配置的复杂度,特别是还要搭配框架实现 URL prefix 之类的功能。
从 v3.2.x 版本开始,我就启动了减法计划,简化代码和功能,减少心智负担,这个 Nginx 容器也是我一直想去掉的。
最近发现 Granian 这个 Rust 开发的 ASGI 服务器,性能高,而且还可以支持静态文件,正好完美符合我的需求,于是这次正好拿来替换原本使用的 Daphne
Granian
项目主页: https://github.com/emmett-framework/granian
Granian 是一款高性能 Python Web Server,支持 ASGI、WSGI、RSGI,基于 Rust 编写,启动速度快、并发能力强,非常适合 Django / FastAPI / Starlette。
官方描述为 “A Rust HTTP server for Python applications built on top of Hyper/Tokio”。
特点:
- 支持 ASGI 3、RSGI(Rust-Server-Gateway Interface)和 WSGI 接口。
- 支持 HTTP/1 和 HTTP/2(未来计划 HTTP/3)协议。
- 支持静态文件直出 (“Direct static files serving”)。
使用方式
很简单,不需要修改代码,只需要修改启动命令。
目前搭配 DjangoStarter 使用的启动命令是这样的:
granian --interface asgi --host 0.0.0.0 --port 8000 --static-path-route /static --static-path-mount ./static-dist config.asgi:application
类似 uvicorn,这个 granian 也支持热重载,加个 --reload 参数就行了
性能测试
本次用 wrk 进行性能测试
测试数据
以下数据在腾讯云 2 cores CPU + 2G 内存的服务器上测得。
django 5.x + ninja + daphne
$ wrk -t4 -c200 -d30s http://127.0.0.1:9876/api/django-starter/monitoring/health
Running 30s test @ http://127.0.0.1:9876/api/django-starter/monitoring/health
4 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.97s 0.00us 1.97s 100.00%
Req/Sec 38.55 55.33 343.00 90.23%
1343 requests in 30.04s, 760.03KB read
Socket errors: connect 0, read 0, write 0, timeout 1342
Requests/sec: 44.70
Transfer/sec: 25.30KB
django 5.x + ninja + granian
$ wrk -t4 -c200 -d30s http://127.0.0.1:9875/api/django-starter/monitoring/hea
lth
Running 30s test @ http://127.0.0.1:9875/api/django-starter/monitoring/health
4 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.20s 93.05ms 1.72s 82.69%
Req/Sec 83.13 105.81 460.00 83.72%
4980 requests in 30.04s, 2.85MB read
Requests/sec: 165.76
Transfer/sec: 97.31KB
因为好奇,我还找到之前一个很老的项目,使用WSGI部署的进行对比。
以下数据在私有云的 4 cores CPU + 2G 内存服务器上测得。
因为是完全不同的服务器硬件,数据仅供参考。
Django 3.x + drf + uwsgi + nginx
$ wrk -t4 -c200 -d30s http://127.0.0.1:9001/api/health/
Running 30s test @ http://127.0.0.1:9001/api/health/
4 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 295.30ms 373.95ms 1.96s 85.14%
Req/Sec 205.54 68.42 380.00 73.73%
19583 requests in 30.09s, 5.83MB read
Socket errors: connect 0, read 0, write 0, timeout 474
Non-2xx or 3xx responses: 12
Requests/sec: 650.89
Transfer/sec: 198.32KB
Django 3.x + ninja + uwsgi + nginx
$ wrk -t4 -c200 -d30s http://127.0.0.1:9001/api2/health
Running 30s test @ http://127.0.0.1:9001/api2/health
4 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 203.75ms 280.30ms 1.95s 88.44%
Req/Sec 250.81 108.59 500.00 61.21%
22542 requests in 30.06s, 5.68MB read
Socket errors: connect 0, read 0, write 0, timeout 420
Non-2xx or 3xx responses: 12
Requests/sec: 749.88
Transfer/sec: 193.35KB
结论
以下是使用 AI 对上面的测试数据进行分析的结论,不过 ASGI 和 uWSGI 不在同一台服务器进行测试,其实很难直接对比。目前看来切换到 Granian 确实可以提高4倍的性能。
① Daphne 在 Django + ASGI 下的性能表现非常差
- 延迟直接飙到 1.9 秒级别,
- wrk 200 并发几乎把它压扁,
- 1343 请求里 1342 超时,等于完全顶不住。
说白了:Daphne 更像是开发环境服务器,不推荐上生产高并发。
② Granian 性能比 Daphne 好 3–4 倍,但仍然有限
- 每秒处理 165 req/s(同机房、同代码)。
- 延迟仍然在 1.2s 左右,远不算理想(在高并发下仍吃力)。
- 优点是 ASGI 原生 + Rust 实现,比 Daphne 强太多。
直观感受:Granian 能用,但你别指望它像 uWSGI 那样扛流量。
③ uWSGI(WSGI)表现碾压:单机可达 650–750 req/s 级别
- 性能直接是 granian 的 4~5 倍。
- 虽然是老架构(WSGI),但调优成熟、稳定、分配机制强,抗压能力远强于同类 ASGI 服务。
简而言之:如果不用异步,WSGI 依旧是 Django 的最强部署方式(性能层面)。
docker-compose
这是精简后的 compose 配置
services:
app:
image: ${APP_IMAGE_NAME}:${APP_IMAGE_TAG}
container_name: $APP_NAME-app
command:
- granian
- --interface
- asgi
- --host
- 0.0.0.0
- --port
- "${APP_INTERNAL_PORT:-8000}"
- --static-path-route
- /static
- --static-path-mount
- /project/static-dist
- config.asgi:application
命令行参数
它的命令行结构很简单:
granian [OPTIONS] APP
其中:
- APP 是入口,例如:
config.asgi:application - OPTIONS 是各种配置参数
下面按分类整理所有参数(附带说明与建议)。
🧩 基础参数(启动必要项)
| 参数 | 说明 | 默认值 |
|---|---|---|
APP | 要启动的应用入口(如 mysite.asgi:application) | 必填 |
--interface | 接口类型:asgi / asginl / rsgi / wsgi | rsgi |
--host | 监听地址 | 127.0.0.1 |
--port | 端口 | 8000 |
--uds | 使用 Unix Domain Socket | 无 |
--http | HTTP 版本:1、2、auto | auto |
--workers | Worker 进程数 | 1 |
👉 Django、FastAPI 用户一般写:
--interface asgi
🗂️ 静态文件服务(Django 专用配置)
Granian 内置静态文件服务:
| 参数 | 说明 | 默认值 |
|---|---|---|
--static-path-route | URL 路由前缀,例如 /static | /static |
--static-path-mount | 文件目录,例如 /project/static-dist | 无 |
--static-path-expires | 缓存时间(秒) | 86400 |
示例:
--static-path-route /static --static-path-mount /project/static-dist
⚙️ 多进程、线程、事件循环选项
Worker / Thread
| 参数 | 说明 |
|---|---|
--workers | Worker 数量 |
--blocking-threads | 阻塞线程数 |
--runtime-threads | Runtime 线程数 |
--runtime-blocking-threads | Runtime I/O 阻塞线程 |
建议:
- CPU × 2 左右的 worker 容量通常够用
- 大部分 ASGI 项目不需要调 thread 参数
🔁 事件循环 & Runtime
| 参数 | 说明 |
|---|---|
--loop | 事件循环:auto、asyncio、rloop、uvloop |
--task-impl | task 执行器:asyncio / rust |
--runtime-mode | 单线程 st / 多线程 mt |
适用建议:
- 普通项目:用默认即可
- 高并发:
--task-impl rust性能更强
🔒 HTTP/1 与 HTTP/2 相关参数
| 类别 | 常用配置 |
|---|---|
| HTTP/1 | --http1-buffer-size、--http1-keep-alive |
| HTTP/2 | --http2-* 一系列参数控制 flow control、窗口、keepalive、stream 数量等 |
大部分项目无需调整,默认即可。
📜 日志(Logging)
| 参数 | 说明 | 默认 |
|---|---|---|
--log / --no-log | 启用日志 | enabled |
--log-level | 日志等级 | info |
--log-config | 使用 JSON 配置文件 | 无 |
--access-log | 开启 access log | disabled |
--access-log-fmt | Access log 格式 | 无 |
如果你希望生产环境有 Nginx 样式的 access log:
--access-log --access-log-fmt "%a %r %s %b"
🔁 热重载(开发环境用)
| 参数 | 说明 |
|---|---|
--reload | 开启自动重载 |
--reload-paths | 指定监控目录 |
--reload-ignore-* | 忽略目录、路径、pattern |
例如:
--reload --reload-paths src/
🔐 HTTPS
| 参数 | 说明 |
|---|---|
--ssl-certificate | 证书文件 |
--ssl-keyfile | 密钥文件 |
--ssl-ca | CA |
--ssl-client-verify | 客户端证书验证 |
(一般反向代理交给 Nginx 做 HTTPS)
🧰 其他有用但不常改的参数
| 参数 | 说明 |
|---|---|
--working-dir | 切换 WorkDir |
--env-files | 加载环境变量文件 |
--factory | APP 是 factory function 时使用 |
--url-path-prefix | 应用挂载前缀 |
--process-name | 自定义进程名 |
--pid-file | 写入 PID 文件 |
--version | 显示版本 |
--help | 显示帮助 |
_EOF_
性能提升4倍!使用Granian作为Django项目的ASGI服务器
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
给Django的Admin添加自定义Action-并移除需要选择对象的限制
前言 不得不说,Django的Admin真的给开发带来很多便利,这不,我又来折腾了,这次是添加自定义的action 这个自定义的Action可以看看官方文档的介绍,很详细,不再赘述。 https://docs.djangoproject.com/en/3.0/ref/contrib/admin/actions/ 界面效果 Django默认的添加完action是要选择下面的对象才能提交的,不过我不需
2022-08-26 · Django · 约 1 分钟
Django数据导入导出神器django-import-export使用
前言 Django以快速开发闻名,但是如果处理数据的导出导入还需要自己写脚本,那就有违“Python之禅”了…… 而且导数据通常需要不同的格式,Excel、csv、json等,每种格式的数据就要写一个脚本太麻烦了,这时直接祭出django-import-export这个神器,官方一句话介绍:django-import-export is a Django application and libra
2021-12-18 · Django · 约 1 分钟
如何优雅地在Django项目里生成不重复的ID?
在生成唯一 ID 这件事上,Django 和其他后端框架没啥不同的,思路都是类似的,只不过可以借助 Python 生态偷懒一下…
2024-12-08 · Django · 约 1 分钟
Django+Taro项目实现企业微信登录
前言 还是最近在做的一个小项目,后端用的是Django搭配RestFramework做接口,前端第一次尝试用京东开源的Taro框架来做多端(目前需要做用于企业微信的H5端和微信小程序) 本文记录一下企业微信登录的流程,上周看文档看得头晕晕的,其实很简单,封装好了就几行代码的事~ 两种方式 一种是先拼接好登录要用的地址authorize_url,回调地址设置成h5应用的网页入口,然后把authori
2022-04-08 · Django · 约 1 分钟
讨论
共 0 条加载评论中…