如何将Django项目性能提升4倍,改用Granian ASGI服务器?

摘要:前言 最近我又继续在开发 DjangoStarter 的新版本了。 之前为了实现 docker 部署,我把 Nginx 打包进了 DjangoStarter 的 compose 配置里了,不过这带来了配置的复杂度,特别是还要搭配框架实现 U
前言 最近我又继续在开发 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 显示帮助