一个微服务架构的直播后端系统,包含用户管理、房间管理、弹幕推送、礼物打赏等功能模块。
通过分布式设计研究系统在高并发、强一致性场景下的问题和解决方案。
本项目采用微服务架构,包含 5 个核心服务 和 1 个共享库:
| 服务 | 端口 | 职责 |
|---|---|---|
| api-service | 8080 | HTTP 网关,请求路由聚合 |
| user-service | 9094 | 用户鉴权、账户管理 |
| room-service | 9095 | 房间创建、用户角色管理 |
| gift-service | 9096 | 礼物/打赏业务、钱包管理、交易记录 |
| danmaku-service | 9093 | 弹幕实时推送、WebSocket 连接管理 |
┌─────────────────────────────────────────────────────────┐
│ HTTP 客户端 (Web / Mobile) │
└────────────────┬────────────────────────────────────────┘
│
┌────────▼────────┐
│ API Service │ (HTTP 入口点)
│ :8080 │
└────────┬────────┘
│
┌───────────┼───────────┬─────────────┐
│ │ │ │
┌──▼──┐ ┌─────▼──┐ ┌────▼────┐ ┌────▼────┐
│User │ │ Room │ │ Gift │ │Danmaku │
│:9094│ │ :9095 │ │ :9096 │ │ :9093 │
└─────┘ └────────┘ └─────────┘ └─────────┘
│ │ │ │
└─────────┼───────────┼─────────────┘
│ │
┌─────▼───────────▼──────┐
│ PostgreSQL (Database) │
└────────────────────────┘
┌─────────────────────────┐
│ Redis (Cache) │
├─────────────────────────┤
│ RabbitMQ (EventBus) │
└─────────────────────────┘
- 统一的 HTTP 入口点
- 路由转发至各微服务的 gRPC 接口
- 请求验证、错误处理、响应聚合
- 用户注册、登录、认证
- Token 生成和验证 (JWT)
- 用户信息管理
- 房间的创建与管理
- 用户房间角色控制
- 房间状态维护
- 核心业务:礼物定义、用户充值、礼物打赏
- 钱包管理:用户余额存储、交易记录持久化
- 并发控制:处理高并发的打赏场景
- WebSocket 长连接管理
- 实时消息广播
- 房间级别的连接池管理
- 错误码统一定义
- 日志工具、加密工具
- JWT 认证模块
- gRPC Protocol Buffer 定义
- 可观测性工具 (追踪、指标)
| 分类 | 技术 |
|---|---|
| 编程语言 | Go 1.22+ |
| Web 框架 | Gin (HTTP), gRPC (RPC) |
| 数据库 | PostgreSQL |
| 缓存 | Redis |
| 消息队列 | RabbitMQ |
| 实时通信 | WebSocket |
| ORM | Ent Framework |
| 日志 | Uber Zap |
live-interact-engine/
├── api/ # API 文档
├── build/ # 编译产物
├── docs/ # 文档
├── infra/ # 基础设施配置
│ ├── development/ # 开发环境配置
│ ├── postgres/ # 数据库初始化脚本
│ └── production/ # 生产环境配置
├── proto/ # Protocol Buffer 定义
├── services/ # 微服务目录
│ ├── api-service/ # HTTP 网关
│ ├── danmaku-service/ # 弹幕服务
│ ├── gift-service/ # 打赏/钱包服务
│ ├── room-service/ # 房间服务
│ └── user-service/ # 用户服务
├── shared/ # 共享库
│ ├── crypto/ # 加密工具
│ ├── env/ # 环境变量
│ ├── jwt/ # JWT 认证
│ ├── logger/ # 日志工具
│ ├── proto/ # gRPC Protocol Buffer
│ ├── svcerr/ # 错误处理
│ └── telemetry/ # 链路追踪、指标
├── docker-compose.yml # 本地开发环境
├── Dockerfile* # 服务容器镜像
└── Makefile # 构建脚本
Gift Service 是整个系统中最复杂的部分,需要在高并发场景下保证用户钱包余额的绝对准确,零资金丢失。为此采用了多层递进的并发控制策略:
用户发送礼物时,需要从 Redis 中原子地扣减余额:
问题:普通 Redis 命令无法保证原子性,分布式环境下容易出现竞态条件
解决方案:使用 Redis Lua 脚本在单个原子操作中完成:
- 检查余额是否充足
- 扣减余额
- 记录幂等性密钥(防止重复扣款)
效果:单个用户在同一时刻发起多次打赏操作,余额扣减绝对正确,不会超支
RabbitMQ 消费消息时可能因网络或处理过程中出现重试,导致同一条消息被处理多次:
问题:消息重复消费会导致余额被重复加减,造成数据混乱
解决方案:
- 统一的
wallet_transaction流水表记录每笔交易 - 每条消息使用唯一的
idempotency_key(幂等性密钥) - 流水表对
idempotency_key设置唯一约束 - 重复消息到达时会触发约束冲突,自动去重
效果:即使消息被投递多次,流水表也只会保存一条记录,余额只会更新一次
消费者在处理事件时需要更新用户钱包余额,多个消费者可能同时处理同一用户的多条事件:
问题:普通 UPDATE 会导致后来的更新覆盖前面的更新,导致余额错误
解决方案:
- 钱包表添加
version_number字段 - 每次更新时检查版本号是否匹配
- 版本号不匹配说明有其他消费者已更新,立即返回冲突错误
- 收到冲突错误后,使用指数退避重试(50ms × 2^n,最多10次)重新读取最新余额再尝试
效果:并发消费多条事件时,每条事件都能准确更新余额,不会丢失任何一次更新
为了减少并发冲突的概率,采用保序策略:
设置:RabbitMQ QoS=1,单个消费者一次只处理一条消息,处理完毕才 ACK 确认
效果:同一用户的消息按顺序处理,减少版本冲突频率
流水表插入和钱包余额更新在同一数据库事务内执行:
- 开启事务 → 插入流水 → 更新钱包 → 提交
- 任何步骤失败都回滚,保证原子性
- 使用
domain.Tx接口解耦 ORM 框架
效果:流水记录与余额始终保持一致
这五层递进的并发控制策略共同作用,实现了:
| 场景 | 保证机制 |
|---|---|
| 单用户多次打赏 | Redis Lua 原子操作 |
| 消息重复投递 | 幂等性密钥 + 唯一约束 |
| 多消费者并发更新 | 版本号乐观锁 + 指数退避重试 |
| 流水记录与余额不一致 | 数据库事务 |
| 消息丢失或顺序错乱 | RabbitMQ QoS 保序 |
测试工具:k6 · 测试脚本:
test/· 环境:Docker Compose,宿主机 2 CPU
| 指标 | 数值 |
|---|---|
| 并发 WebSocket 连接 | 1000 |
| 弹幕接收吞吐 | 30,597 条/秒 |
| WebSocket 错误率 | 0.00% |
| 连接建立 p(95) | 97ms |
| 所有会话完整生命周期 | ✓ 无提前断开 |
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 稳定吞吐 | 348 RPS | 1097 RPS (+215%) |
| p(95) 延迟 | 2.24s | 593ms (-73%) |
| 错误率 | 2.11% | 0.00% |
| max 延迟 | 10.55s | 1.09s |
瓶颈分析:单主播场景下所有观众的 Redis Lua 扣款串行排队,是当前 2 CPU Docker 环境下的核心瓶颈。真实生产 4~8 核独立部署预计还有 3~5x 提升空间。 详细报告见 test/STRESS_TEST_REPORT.md。
每个服务遵循四层架构:
- Domain Layer: 业务域模型和接口定义
- Service Layer: 业务逻辑实现
- Infrastructure Layer: 外部依赖实现 (数据库、消息队列、gRPC)
- Handler/Adapter Layer: HTTP/gRPC 请求处理
- 服务间: gRPC (同步)
- 事件通知: RabbitMQ (异步)
- 客户端: HTTP
- 持久化: PostgreSQL
- 缓存: Redis
- Go 1.22+
- Docker & Docker Compose
- PostgreSQL 15+
- Redis 7+
- RabbitMQ 3.12+
# 编译 + 构建镜像 + 启动所有服务
make rebuild
# 初始化礼物数据(首次启动后执行)
make seed-giftsmake up # 启动所有服务
make down # 停止所有服务
make rebuild # 重新构建并启动
make seed-gifts # 初始化礼物 seed 数据
make logs # 查看 api-service 日志
make clean # 完全清理(含数据卷)| 服务 | 地址 |
|---|---|
| API | http://localhost:8080 |
| Swagger 文档 | http://localhost:8080/swagger/index.html |
| Jaeger 链路追踪 | http://localhost:16686 |
| Prometheus 指标 | http://localhost:9091 |
| Grafana 面板 | http://localhost:3000 |
| RabbitMQ 管理 | http://localhost:15672 |
# 礼物打赏压测
k6 run -e BASE_URL=http://localhost:8080 test/gift_stress_test.js
# 弹幕服务压测
k6 run -e BASE_URL=http://localhost:8080 test/danmaku_stress_test.jsmake rebuild参考 infra/production/k8s/ 目录的配置文件。
提交 PR 时请确保:
- 代码风格符合项目规范
- 新增功能附带单元测试
- Commit 消息清晰(Conventional Commits)
MIT License
Copyright (c) 2026 lirous587