基于 Go 的高并发内容社区平台,采用 Gin + GORM + MySQL + Redis + Kafka 技术栈,前端使用 React + Ant Design。
| 层级 | 技术 |
|---|---|
| Web 框架 | Gin |
| ORM | GORM |
| 数据库 | MySQL |
| 缓存 | Redis |
| 消息队列 | Kafka |
| 依赖注入 | Wire |
| 前端 | React + TypeScript + Ant Design |
| 部署 | Docker Compose |
graph TB
subgraph Frontend
React[React + Ant Design]
end
subgraph Web Layer
Gin[Gin Router]
JWT[JWT 认证]
RateLimit[滑动窗口限流]
VO[VO 转换]
end
subgraph Service Layer
BizLogic[业务逻辑]
Validation[参数校验]
end
subgraph Event Layer
Producer[Kafka Producer]
Consumer[Kafka Consumer<br/>批量消费]
end
subgraph Repository Layer
RepoLogic[缓存策略 · 数据组装 · 降级处理]
end
subgraph Storage
Cache[Redis<br/>Lua 脚本 · Pipeline]
DAO[MySQL<br/>批量 SQL · 复合索引]
LocalCache[本地缓存<br/>atomic 无锁读]
end
React -->|HTTP/JSON| Gin
Gin --> JWT --> RateLimit --> VO --> BizLogic
BizLogic --> Validation
BizLogic --> Producer
Producer --> Consumer
Consumer --> RepoLogic
BizLogic --> RepoLogic
RepoLogic --> Cache
RepoLogic --> DAO
RepoLogic --> LocalCache
项目严格遵循六层架构,各层职责清晰,通过接口解耦:
backend/internal/
├── domain/ # 领域模型:纯数据结构 + 业务错误定义
├── repository/
│ ├── dao/ # 数据访问:GORM 操作,SQL 构建
│ └── cache/ # 缓存访问:Redis 操作,Lua 脚本
├── repository/ # 仓储层:组合 Cache + DAO,缓存策略,数据转换
├── service/ # 业务层:核心业务逻辑,参数校验
├── web/ # 接口层:路由,请求/响应 VO,中间件
├── events/ # 事件层:Kafka Producer/Consumer
├── job/ # 定时任务:热榜计算,分布式锁
└── ioc/ # 依赖注入:Wire 初始化
- 注册、登录(手机验证码 / 邮箱密码)
- JWT 双 Token 认证(Access Token + Refresh Token)
- 微信 OAuth2 第三方登录
- 文章创作、编辑、发布、撤回
- 制作库 / 线上库分离(读写分离预备)
- 富文本编辑器(WangEditor)
- 点赞、收藏、阅读计数
- Kafka 异步削峰(详见下方亮点)
- Redis 缓存互动数据
- 一级评论 + 嵌套回复(楼中楼)
- 回复预览 + 分页加载
- 批量查询解决 N+1 问题(41 次 SQL → 3 次)
- 软删除,防御性校验
- 定时任务 + 分布式锁(防止多实例重复计算)
- 时间衰减热度算法(Hacker News 风格)
- 游标分页遍历全量文章 + 小顶堆 Top N
- Redis 缓存 + 本地缓存(atomic 无锁读)+ ForceGet 兜底
- 装饰器模式:限流、重试、failover、监控可自由组合
- 超时 failover 自动切换服务商(atomic CAS)
- DB 抢占式异步重试(SELECT FOR UPDATE + 重试上限)
- JWT 模板鉴权,防止调用方使用任意模板
- Redis Lua 脚本保证原子性
- 滑动窗口限流(按用户 ID)
- Prometheus 监控指标
将阅读计数、点赞、收藏等高频写操作从同步写 DB 改造为 Kafka 异步处理。
flowchart LR
A[用户操作] --> B[Service<br/>发送 Kafka 消息]
B --> C[立即返回]
B --> D[Kafka Broker]
D --> E[Consumer<br/>批量消费 100条/批]
E --> F[Redis Pipeline<br/>批量去重]
F --> G[批量写入 MySQL<br/>ON DUPLICATE KEY UPDATE]
G --> H[异步更新<br/>Redis 缓存]
关键设计:
- 自定义
BatchHandler框架,支持批量消费,将 100 次 DB 交互合并为 1 次 - 阅读计数使用 Redis Pipeline + SetNX 实现分布式去重,100 次网络往返合并为 1 次
- 点赞/收藏使用
ON CONFLICT DO UPDATE SET status=?实现幂等 upsert - 事务包装双表更新(用户行为表 + 互动统计表),保证数据一致性
- 按 bizId 聚合计数,减少 Interactive 表的更新次数
性能收益: 接口响应时间大幅降低,数据库写入压力从峰值 QPS 平滑到平均 QPS。
从定时触发到结果展示的完整热榜链路:
flowchart TB
Cron[Cron 定时触发] --> Lock{抢分布式锁<br/>redis-lock}
Lock -->|成功| Refresh[AutoRefresh<br/>自动续约]
Lock -->|失败| Skip[跳过本轮]
Refresh --> Cursor[游标分页遍历<br/>WHERE id > lastId<br/>+ 7天时间过滤]
Cursor --> Score[热度计算<br/>score = likeCnt-1 / pow duration+2 1.5]
Score --> Heap[小顶堆<br/>维护 Top 100]
Heap --> Redis[(Redis 缓存<br/>TTL 3min)]
Redis --> Local[(本地缓存<br/>atomic 无锁读)]
Local --> API[GetTopN API]
Redis -.->|Redis 不可用| ForceGet[ForceGet<br/>忽略过期时间<br/>兜底返回]
关键设计:
- 分布式锁 + AutoRefresh 续约:多实例部署时只有一个节点执行计算,锁持有期间自动续约防止超时释放
- 游标分页:
WHERE id > lastId ORDER BY id ASC,避免 OFFSET 深度分页的性能退化 - SQL 层 7 天时间过滤:
WHERE utime >= 7天前,将候选集从百万级缩减到万级 - 复合索引
(status, utime, id):覆盖 WHERE + ORDER BY,避免回表 - 小顶堆 Top N:内存中维护固定大小的优先队列,O(N log K) 时间复杂度
- 多级缓存:Redis(3 分钟 TTL)→ 本地缓存(atomic 无锁读,零 GC 压力)→ ForceGet 兜底(Redis 故障时忽略过期时间返回旧数据)
- 用 Lua 脚本将 HSet + Expire 合并为原子操作,解决进程崩溃导致缓存 key 永久残留的问题
- 实现滑动窗口限流算法(ZSet 存储请求时间戳),为互动接口提供按用户 ID 的防刷保护
- 限流中间件在 IOC 层初始化,Handler 层通过依赖注入接收,符合分层架构
基于装饰器模式实现可组合的短信服务,各层职责独立,自由嵌套:
flowchart TB
Caller[业务调用方] --> OTel[OpenTelemetry<br/>链路追踪]
OTel --> Prom[Prometheus<br/>发送耗时监控]
Prom --> Auth[JWT Auth<br/>模板 ID 鉴权]
Auth --> Limit{滑动窗口限流}
Limit -->|未限流| Async[异步重试服务]
Limit -->|触发限流| Reject[拒绝发送]
Async -->|同步发送| Failover[Timeout Failover]
Async -->|异步入库| DB[(MySQL<br/>async_sms 表)]
DB -->|抢占式调度| Failover
Failover -->|atomic CAS<br/>切换服务商| Provider1[腾讯云 SMS]
Failover --> Provider2[备用服务商]
关键设计:
- 装饰器模式:所有组件实现同一个
sms.Service接口,限流、重试、failover、监控可自由组合嵌套 - 超时 Failover:用
atomic.CompareAndSwapInt32追踪连续超时次数,超过阈值自动切换到下一个服务商,无锁实现 - DB 抢占式异步重试:
SELECT ... FOR UPDATE抢占待发送记录,utime间隔 1 分钟防止重复抢占,retry_cnt达到上限后标记失败 - JWT 模板鉴权:调用方传入的
tplId实际是 JWT Token,服务端解析后提取真实模板 ID,防止调用方使用任意模板
linkbook/
├── backend/ # Go 后端
│ ├── main.go # 入口
│ ├── wire.go # Wire 依赖注入定义
│ ├── internal/
│ │ ├── domain/ # 领域模型
│ │ ├── repository/
│ │ │ ├── dao/ # MySQL 数据访问
│ │ │ └── cache/ # Redis 缓存
│ │ │ └── lua/ # Lua 脚本
│ │ ├── service/ # 业务逻辑
│ │ ├── web/ # HTTP 接口 + VO
│ │ ├── events/ # Kafka 事件处理
│ │ └── job/ # 定时任务
│ ├── ioc/ # IOC 初始化(DB/Redis/Kafka/Web)
│ ├── pkg/ # 通用工具包
│ │ ├── ginx/ # Gin 扩展(统一响应、中间件)
│ │ ├── limiter/ # 限流器(滑动窗口)
│ │ ├── samarax/ # Kafka 批量消费框架
│ │ └── logger/ # 日志抽象
│ └── docker-compose.yaml # 本地开发环境
│
└── frontend/ # React 前端
└── src/
├── components/ # 通用组件(评论、编辑器、布局)
├── pages/ # 页面(首页、文章、用户)
└── types/ # TypeScript 类型定义
- Go 1.21+
- Node.js 18+
- Docker & Docker Compose
# 1. 启动基础设施(MySQL、Redis、Kafka)
cd backend
docker-compose up -d
# 2. 启动后端
go run main.go
# 3. 启动前端
cd ../frontend
npm install
npm run dev