Files
chunyu_project/docs/ARCHITECTURE.md
T

11 KiB
Raw Blame History

可乐平台 异步化架构(ADRF + Granian + PostgreSQL + Nginx)

架构总览

┌──────────┐   :8080    ┌─────────────────────┐
│  浏览器   │──────────▶│  Nginx (frontend)    │
└──────────┘            │  ├─ /            → React SPA 静态产物 (build/, 动静分离)
                        │  ├─ /api 等前缀  ─┐
                        │  └─ /ws/        ─┤ 反向代理
                        └──────────────────┼───┘
                                           ▼
                        ┌───────────────────────────────┐
                        │  Granian (Rust) :8000         │
                        │  Django 5.2 ASGI + ADRF       │
                        │  ├─ 异步视图 (async def)       │
                        │  ├─ 异步 ORM (aget/afilter)    │
                        │  └─ Channels (WebSocket)      │
                        └───────┬──────────────┬────────┘
                                ▼              ▼
                     ┌──────────────┐  ┌──────────────┐
                     │ PostgreSQL 16 │  │   Redis 7    │
                     │ psycopg3 异步 │  │ cache/session│
                     │   ORM 驱动    │  │ celery/ws层  │
                     └──────────────┘  └──────────────┘

技术选型

层 技术 说明
Web 框架 Django 5.2 + DRF 3.17 原生异步能力
异步视图 ADRF 0.1.14 所有 API 视图跑在异步事件循环上
应用服务器 Granian 2.8.2 (Rust) 替代 Daphne,ASGI + WebSocket,吞吐量显著提升
数据库 PostgreSQL 16 Django 5 异步 ORM 必备(MySQL 后端不支持异步)
DB 驱动 psycopg3 (binary) 3.2.13 支持异步连接
缓存/队列 Redis 7 缓存、Session、Celery broker、Channels layer
前端 React 19 + Vite 8 独立 SPA,与 Django 模板层完全解耦
反代/静态 Nginx SPA 静态资源直出 + API 反向代理

关键设计决策

  1. 全链路异步:视图 async def → ORM aget/afirst/aexists/acount/acreate/asave/aupdate/adelete → PostgreSQL(psycopg3)。CONN_MAX_AGE=0(异步 ORM 不支持持久连接)。
  2. ADRF 兼容性:view_is_async 自动检测处理器;纯 CPU 视图(图形验证码、图像压缩、文本 diff)保持同步 def,ADRF 自动 sync_to_async 线程池兜底。
  3. 外部 HTTP 调用 aiohttp 化:weather / air_quality / currency / getIpData 全部改为 aiohttp.ClientSession,无阻塞 I/O。
  4. Redis 缓存 / Celery / SMTP / 密码哈希:同步客户端用 sync_to_async 包裹(不阻塞事件循环)。
  5. 事务兜底:transaction.atomic + select_for_update 段(钱包、任务领取)抽成同步闭包整体 sync_to_async 执行,保住行锁语义。
  6. 数据库可切换:DB_ENGINE 环境变量支持 django.db.backends.postgresql(默认)与 django.db.backends.mysql(本地过渡)。

前后端分离

  • 开发:pnpm dev(Vite HMR :5173),BACKEND_URL 环境变量指定后端(默认 http://127.0.0.1:8002,Docker 后端用 http://127.0.0.1:8000)。API 全部走 Vite proxy,无 CORS 负担。
  • 生产:pnpm build 产物由 Nginx 容器直出(try_files → index.html SPA 回退),带指纹资源 1 年 immutable 缓存,index.html no-cache。
  • 前后端仅通过 RESTful API + WebSocket 交互。

快速开始

一键部署(生产形态)

docker compose up -d --build
# 前端       http://localhost:8080
# 后端 API   http://localhost:8000
# Swagger    http://localhost:8080/swagger/

本地开发

# 1. 启动数据层
docker compose up -d postgres redis

# 2. 后端(异步栈)
cd chunyu_project
export DB_ENGINE=django.db.backends.postgresql DB_HOST=localhost DB_PORT=5432 \
       DB_NAME=chunyu DB_USER=chunyu DB_PASSWORD=chunyu_pg_pass \
       REDIS_HOST=localhost
python manage.py migrate
granian --interface asgi --host 0.0.0.0 --port 8000 chunyu_project.asgi:application

# 3. 前端(Vite HMR)
cd chunyu_project_react
BACKEND_URL=http://127.0.0.1:8000 pnpm dev   # http://localhost:5173

异步化转换统计

Phase 1:视图层全异步(ADRF 事件循环)

  • 覆盖 16 个应用、45 个视图文件:api user article learn chat message history search bug tool weather air_quality currency shorturl logs apidirectory app
  • 处理器全部 async def(约 90+ 个);ORM 调用全部异步化
  • 转换规范见 chunyu_project/docs/ADRF_CONVERSION_GUIDE.md

Phase 2:serializer 层原生异步(100% 异步化收口)

  • 10 个 serializer 文件全部迁移至 adrf.serializers(原生异步 ModelSerializer/Serializer): article learn bug chat message history tool apidirectory user/serializers/user_serializers user/serializers/region_serializers
  • 全部 SerializerMethodField 的 getter 改为 async def + 异步 ORM(含嵌套外键改 id 访问/select_related 消懒加载)
  • 写路径 create/update → acreate/aupdate,视图统一 await serializer.asave(**kwargs)(adrf 原生,kwargs 合入 validated_data)
  • 序列化输出统一 await serializer.adata(替代同步 .data 与 sync_to_async 兜底)
  • async_cache 基建(utils/async_cache.py):redis.asyncio 直连 + msgpack 序列化 + 同步缓存降级,验证码/缓存场景(注册/登录/改密/邮箱/手机号/IP 定位)全部切换
  • 外键懒加载治理:黑名单列表 select_related('blocked_user')、聊天消息 select_related('sender')、评论父作者异步加载等

终态核验(服务器实跑 grep)

  • serializer 层裸同步 ORM getter:0
  • 视图层 serializer.data 同步残留:0
  • sync_to_async(serializer…) 残留:仅 8 处 is_valid(设计边界:Django validate 钩子同步调用,且 user 系列校验器含 DB 查询/argon2 哈希)+ 已清零的 save
  • async def 总量:310+
  • 剩余 sync_to_async(约 160 处)全部为无异步 API 的库:SMTP/Celery .delay/密码哈希/文件存储/DRF 分页器求值/transaction.atomic 事务段(Django 5.2 限制,行锁语义必须同步闭包)

验证记录

  • ✅ python manage.py check 0 错误
  • ✅ URLCONF 全量导入成功(所有视图模块可加载)
  • ✅ 205 个 py 文件 py_compile 0 失败
  • ✅ Granian 启动 + 冒烟:/、/article/articles/、/learn/courses/、/api/apidirectory/categories/、/api/weather/?city=Beijing、/api/air-quality/?city=Shanghai、/api/currency/rates/?base=USD、/api/image-captcha/、/api/captcha/generate/、/swagger.json(331KB) 全部 200
  • ✅ docker compose config 校验通过
  • ✅ Nginx 配置语法 nginx -t 通过
  • ✅ Phase-2 服务器回归:7/9 端点组全绿、写路径(短链/评论)异步落库、并发 240/240

服务器部署(192.168.5.7 生产/联调环境)

本地 Windows 宿主虚拟化暂时不可用(Hypervisor 未加载,见下),已将整套异步架构部署至本地 Linux 服务器:

项 值
服务器 Debian,16 核(lan-19216857 SSH profile)
部署目录 /mnt/sda/chunyu
访问地址 http://192.168.5.7:18080(前端 SPA + API 反代)
后端直连 http://192.168.5.7:18000(Granian)
PostgreSQL 192.168.5.7:15432(容器内 5432)
Redis 192.168.5.7:16379(容器内 6379)

端口重映射原因:服务器既有服务占用 5432/6379/8080,覆盖文件 docker-compose.server.yml 将宿主端口改为 15432/16379/18000/18080,容器间通信仍走内部网络不受影响。

部署命令:

cd /mnt/sda/chunyu
docker compose -f docker-compose.yml -f docker-compose.server.yml up -d --build

服务器端到端验证记录(全部通过):

  • ✅ 四容器运行:chunyu-backend(healthy) / chunyu-frontend / chunyu-postgres(healthy) / chunyu-redis(healthy)
  • ✅ SPA 静态资源 Nginx 直出 200;/api/apidirectory/categories/、/article/articles/、/swagger.json 经反代 200
  • ✅ aiohttp 外部 API(天气)经反代 200
  • ✅ 异步 ORM 写路径:短链创建成功落库(PostgreSQL 66 张迁移表)
  • ✅ Redis PONG(缓存/Session 层)
  • ✅ WebSocket:Granian + Channels,JWT 认证后 accept 成功(匿名拒绝为原有业务设计,ChatConsumer 强制认证)
  • ✅ 并发冒烟:80 并发 × 5 轮 = 400 请求,400/400 成功,187.9 RPS(穿透 Nginx → Granian → PostgreSQL 全链路)
  • ✅ Windows 局域网访问 http://192.168.5.7:18080/* 全部 200

说明:DJANGO_DEBUG 插值默认 True(局域网无 TLS;False 会触发 SECURE_SSL_REDIRECT 301)。正式上 TLS 后设 DJANGO_DEBUG=False。 本地 Windows 宿主:2026-09-05 22:22 重启后 HypervisorPresent=False(BCD hypervisorlaunchtype 被关),Docker Desktop 不可用;管理员执行 bcdedit /set "{current}" hypervisorlaunchtype auto 重启后可恢复本地 Docker。

环境变量

见 docker.env.example。核心项:

变量 默认 说明
DB_ENGINE postgresql django.db.backends.postgresql / mysql
DB_HOST/DB_PORT/DB_NAME/DB_USER/DB_PASSWORD postgres/5432/chunyu/chunyu 数据库连接
REDIS_HOST/REDIS_PORT/REDIS_PASSWORD redis/6379 Redis 连接
POSTGRES_PASSWORD chunyu_pg_pass PostgreSQL root 密码
DJANGO_DEBUG False 生产必须 False
CORS_ALLOWED_ORIGINS localhost:8080/5173 前端来源

已知限制

  1. serializer 层仍有同步 ORM(经 sync_to_async 兜底,功能正确但该段走线程池):chat/serializers.py ConversationSerializer 的 MethodField、bug 列表 get_images_count、若干 serializer.create()。后续可对 serializer 原生异步化。
  2. Celery worker 不在默认编排内(views 已适配:同步调用失败自动降级 submit_task 异步队列)。如需启用:docker compose up -d 后手动 docker compose run backend celery -A chunyu_project worker -l info。
  3. Channels ASGI Lifespan 在 Granian 下有 warning(asginl 接口可消除),不影响 WebSocket 功能。
  4. 纯 CPU 视图(验证码/压缩/diff)保持同步,ADRF 自动线程池兜底。