前言
做通用后台系统最大的挑战,不是技术有多难,而是如何在"灵活性"和"标准化"之间找到平衡。
时间管理 App 要求极低延迟的用户行为记录;深度检测系统需要处理大量非结构化数据和 AI 模型调用;运营后台要求快速迭代、权限精细、报表丰富。三种场景,一套架构,怎么做?
本文是对这套通用后台架构的完整梳理,涵盖技术选型的原因、各层之间的协作关系,以及一些踩坑后的实践心得。
技术栈全景
| 分类 | 技术选型 |
|---|
| 网关 | Kong |
| 开发语言 | Go |
| Web 框架 | Gin |
| 后台管理系统 | Refine |
| RPC | Go-zero |
| ORM | GORM |
| 参数校验 | Go-zero Validator |
| 本地配置 | YAML |
| 中心化配置 | Etcd |
| 消息队列 | Kafka |
| NoSQL | MongoDB + Redis |
| 关系型数据库 | PostgreSQL |
| 数据库连接池 | PgBouncer |
| 全文搜索 | Elasticsearch |
| 异步任务 | Asynq |
| 日志 | Zap |
| 链路追踪 | OpenTelemetry + Jaeger |
| 监控告警 | Prometheus + Grafana |
| 开发者中心 | Backstage |
| 多语言扩展 | Python + Rust |
整体架构分层
架构整体分为五层:接入层、业务层、服务层、存储层和基础设施层。
graph TB
subgraph 客户端
AppClient[移动 App]
WebClient[Web 前端]
AdminClient[运营后台 Refine]
end
subgraph 接入层
Kong[Kong 网关
限流 / 鉴权 / 路由]
end
subgraph 业务层
GinAPI[Gin HTTP API
REST / WebSocket]
AdminAPI[Admin API
Refine BFF]
end
subgraph 服务层
RPC[Go-zero RPC 服务群
用户服务 / 任务服务 / 检测服务 ...]
Worker[Asynq Worker
异步任务处理]
PythonSvc[Python 服务
AI 推理 / 数据分析]
RustLib[Rust 模块
高性能计算 / 加密]
end
subgraph 存储层
Postgres[(PostgreSQL
核心业务数据)]
PgBouncer[PgBouncer
连接池]
Redis[(Redis
缓存 / 锁 / 任务队列)]
MongoDB[(MongoDB
日志 / 事件 / 灵活数据)]
ES[(Elasticsearch
全文搜索)]
Kafka[Kafka
事件流]
end
subgraph 基础设施
Etcd[Etcd
配置中心]
OTel[OpenTelemetry
Trace 采集]
Jaeger[Jaeger
Trace 可视化]
Prometheus[Prometheus
Metrics]
Grafana[Grafana
监控面板]
Backstage[Backstage
开发者门户]
end
AppClient --> Kong
WebClient --> Kong
AdminClient --> Kong
Kong --> GinAPI
Kong --> AdminAPI
GinAPI --> RPC
AdminAPI --> RPC
RPC --> PgBouncer --> Postgres
RPC --> Redis
RPC --> MongoDB
RPC --> ES
RPC --> Kafka
Kafka --> Worker
Kafka --> PythonSvc
Worker --> Redis
RPC -.-> RustLib
RPC -.-> OTel
GinAPI -.-> OTel
OTel --> Jaeger
RPC -.-> Prometheus --> Grafana
Etcd -.-> RPC
Etcd -.-> GinAPI
接入层:Kong 网关
Kong 是整个系统的"大门",承担了请求生命周期的第一道处理:鉴权、限流、路由、CORS、日志采集。
为什么不自己在 Gin 里写这些?
Kong 的核心价值在于关注点分离。把鉴权和限流放在网关,业务服务就可以假设"到我这里的请求都是合法的",代码更纯粹。同时 Kong 支持插件热插拔,不用重新部署服务就能调整策略。
graph LR
Request[外部请求] --> Kong
subgraph Kong 处理链
Auth[JWT 鉴权
key-auth 插件]
RateLimit[限流
rate-limiting 插件]
Log[请求日志
file-log 插件]
Proxy[反向代理
路由转发]
end
Kong --> Auth --> RateLimit --> Log --> Proxy
Proxy --> UpstreamA[Gin API 集群]
Proxy --> UpstreamB[Admin API]
一个重要实践:Kong 的插件配置要纳入 GitOps,用 deck 做声明式管理,禁止在 Admin UI 上手动修改。手动改的东西,两周后没人知道为什么那样配。
业务层:Gin + Refine 的双轨设计
面向外部用户的 API 和运营后台走两条独立的链路,这是一个很关键的设计决策。
为什么要分开?
运营后台的查询往往是复杂的多表 JOIN、大范围扫描、导出报表,这类操作放到用户 API 的服务里,一个慢查询就能拖垮整个用户侧响应。分开之后,运营人员把数据库查崩了,用户完全感知不到。
graph TB
subgraph 用户侧 API Gin
UserRouter[路由注册]
Middleware[中间件链
Trace / Auth / Recovery]
Handler[Handler 层
参数绑定 + 校验]
Service[Service 层
业务逻辑]
end
subgraph 运营后台 BFF
AdminRouter[Admin 路由]
AdminMiddleware[管理员鉴权
RBAC]
AdminHandler[Admin Handler
报表 / 操作接口]
AdminService[Admin Service
查询密集型逻辑]
end
UserRouter --> Middleware --> Handler --> Service
AdminRouter --> AdminMiddleware --> AdminHandler --> AdminService
Refine 作为前端框架,本质上是一个 React 的 CRUD 框架,它消费 BFF 提供的 REST 接口,大幅减少后台管理页面的开发工作量。对于"快速上线运营"这个目标,Refine + BFF 的组合非常高效。
服务层:Go-zero RPC 服务群
RPC 层是业务逻辑的核心载体,基于 Go-zero 做服务治理。
graph LR
Gin[Gin API] -->|gRPC| UserSvc[用户服务]
Gin -->|gRPC| TaskSvc[任务服务
时间管理 App 核心]
Gin -->|gRPC| DetectSvc[检测服务
深度检测系统]
Gin -->|gRPC| NotifySvc[通知服务]
UserSvc --> Postgres
TaskSvc --> Postgres
TaskSvc --> Redis
DetectSvc --> MongoDB
DetectSvc --> PythonSvc[Python AI 服务]
NotifySvc --> Kafka
Go-zero 的价值主要体现在两点:一是 goctl 代码生成减少了大量模板代码;二是内置了熔断、限流、服务发现,这些基础设施不用自己造。
一个需要注意的边界:Go-zero 的 Validator 适合做接口层的格式校验(字段非空、长度限制),但业务规则校验(比如"任务时长不能超过用户套餐限制")要放在 Service 层用纯 Go 代码写,不要和框架耦合。
多语言扩展:Python 和 Rust 的定位
引入多语言是为了解决 Go 在特定场景下不是最优解的问题,但要有明确的边界,不能让技术栈变成"什么语言都有"的大杂烩。
graph TB
subgraph Go 核心服务
GoSvc[业务逻辑
99% 的代码在这里]
end
subgraph Python 场景
AIInfer[AI 模型推理
深度检测 / 分类]
DataAnalysis[数据分析
用户行为报表]
RuleEngine[规则引擎
灵活配置的业务规则]
end
subgraph Rust 场景
Crypto[加密计算
高性能签名验证]
DataProcess[数据处理
超大文件解析]
end
GoSvc -->|HTTP / gRPC| AIInfer
GoSvc -->|HTTP / gRPC| DataAnalysis
GoSvc -->|FFI / CGO| Crypto
GoSvc -->|进程调用| DataProcess
核心原则:Go 服务不直接依赖 Python 的包,通过 HTTP 或 gRPC 通信。这样 Python 服务可以独立部署、独立扩缩容,一个 AI 推理服务挂了不会影响整个系统。Rust 模块通过 CGO 或独立进程调用,视场景而定。
存储层:各司其职
多个存储组件并存,最重要的是把每个组件的职责边界说清楚。边界模糊是系统腐化的开始。
graph TB
subgraph 业务数据 PostgreSQL + PgBouncer
UserData[用户账号 / 权限]
TaskData[任务记录 / 统计]
BizConfig[业务配置]
end
subgraph 高速缓存 Redis
SessionCache[会话 / Token]
HotData[热点数据缓存]
DistLock[分布式锁]
AsyncQueue[Asynq 任务队列]
end
subgraph 非结构化数据 MongoDB
EventLog[用户行为事件]
AuditLog[操作审计日志]
DetectResult[检测结果
灵活 Schema]
end
subgraph 全文搜索 ES
SearchIndex[全文搜索索引]
LogSearch[日志检索]
end
连接池说明:PostgreSQL 的连接是重资源,高并发下不加 PgBouncer 直接把连接数打满是迟早的事。PgBouncer 的 Transaction 模式对大多数场景够用,但要注意它不支持 SET 语句的会话级状态,使用前检查 ORM 是否有依赖会话状态的行为。
异步任务:Asynq 的使用边界
Kafka 和 Asynq 都能做"异步处理",但定位不同,要避免混用。
graph LR
subgraph 用 Kafka 的场景
EventDriven[事件驱动
用户注册事件 → 多个消费者]
DataPipeline[数据管道
行为数据 → 分析 / ES 同步]
CrossService[跨服务通信
检测完成 → 通知服务]
end
subgraph 用 Asynq 的场景
DelayTask[延迟任务
30分钟后发提醒]
RetryTask[可重试任务
发送推送 / 邮件]
ScheduleTask[定时任务
每日报告生成]
end
Kafka -->|生产者| EventDriven
Redis -->|Asynq| DelayTask
简单来说:事件驱动、多消费者、数据管道用 Kafka;延迟、重试、定时的后台任务用 Asynq。
可观测性:三件套缺一不可
可观测性是系统的"神经系统",出了问题靠它定位,日常运营靠它感知。
graph TB
subgraph 数据采集
OTelSDK[OpenTelemetry SDK
埋点在各服务中]
end
subgraph Trace 链路
OTelSDK -->|Span 数据| JaegerCollector[Jaeger Collector]
JaegerCollector --> JaegerUI[Jaeger UI
请求链路可视化]
end
subgraph Metrics 监控
OTelSDK -->|Metrics| PrometheusServer[Prometheus
指标存储]
PrometheusServer --> GrafanaDash[Grafana Dashboard
业务 / 系统监控面板]
GrafanaDash -->|触发| AlertManager[AlertManager
告警通知]
end
subgraph 日志
Zap[Zap 结构化日志] -->|TraceID 关联| LogAgg[日志聚合
ES / Loki]
end
TraceID 是三者的纽带。一次请求进来,TraceID 通过 Context 传递,日志里带上 TraceID,Metrics 的 exemplar 里带上 TraceID。出了问题,从 Grafana 的异常 Metrics 点进去,能直接跳到 Jaeger 的 Trace,再关联到具体的日志行。没有这个关联,排查问题就是三个孤岛之间反复横跳。
配置管理:YAML + Etcd 的两层模型
graph LR
subgraph 静态配置 YAML
ServicePort[服务端口]
DBConn[数据库连接串
从环境变量注入密钥]
EtcdAddr[Etcd 地址]
end
subgraph 动态配置 Etcd
FeatureFlag[功能开关
灰度发布]
RateConfig[限流阈值
运行时调整]
BizParam[业务参数
无需重启热更新]
end
Service[各微服务] -->|启动时读取| YAML
Service -->|Watch 监听| Etcd
YAML 管启动必须的静态信息,Etcd 管运行时可以热更新的动态配置。需要强调的是:数据库密码、API 密钥这类敏感信息不要放在 YAML 文件里明文存储,应该通过环境变量或 Vault 注入。
开发者中心:Backstage 的价值
当服务数量超过 10 个,"这个服务的 API 文档在哪里""这个服务是谁维护的""这个服务依赖哪些下游"这些问题就开始让人抓狂。
Backstage 解决的是内部技术资产的可发现性问题:
- 服务目录:每个 RPC 服务、Kafka Topic、数据库都登记在册
- API 文档:OpenAPI / gRPC proto 集中展示
- 技术雷达:团队技术栈决策记录
- Runbook:故障处理手册入口
但要控制维护成本:Backstage 的插件生态很丰富,但插件版本碎片化是真实的痛点。建议先用好核心的 Software Catalog 和 TechDocs,不要一上来就装一堆插件。
典型业务场景走读
场景一:时间管理 App 用户记录一条任务
sequenceDiagram
participant App as 移动 App
participant Kong as Kong 网关
participant Gin as Gin API
participant TaskRPC as Task RPC 服务
participant PG as PostgreSQL
participant Redis as Redis
participant Kafka as Kafka
App->>Kong: POST /api/tasks {title, duration}
Kong->>Kong: JWT 鉴权 + 限流
Kong->>Gin: 转发请求
Gin->>TaskRPC: gRPC CreateTask
TaskRPC->>PG: INSERT task record
TaskRPC->>Redis: 更新用户今日统计缓存
TaskRPC->>Kafka: 发布 task.created 事件
TaskRPC-->>Gin: 返回任务 ID
Gin-->>App: 200 OK {taskId}
Note over Kafka: 异步消费
Kafka->>Asynq: 触发成就检查任务
Kafka->>ES: 同步搜索索引
场景二:深度检测系统提交一次检测任务
sequenceDiagram
participant Web as Web 前端
participant Kong as Kong 网关
participant Gin as Gin API
participant DetectRPC as Detect RPC 服务
participant Kafka as Kafka
participant Worker as Asynq Worker
participant Python as Python AI 服务
participant Mongo as MongoDB
Web->>Kong: POST /api/detect {imageUrl}
Kong->>Gin: 转发
Gin->>DetectRPC: gRPC SubmitDetect
DetectRPC->>Mongo: 创建检测记录 status=pending
DetectRPC->>Kafka: 发布 detect.submitted 事件
DetectRPC-->>Web: 202 Accepted {detectId}
Kafka->>Worker: 消费事件
Worker->>Python: HTTP 调用 AI 推理接口
Python-->>Worker: 返回检测结果
Worker->>Mongo: 更新检测记录 status=done + 结果
Worker->>Kafka: 发布 detect.completed 事件
Kafka->>Web: WebSocket 推送结果通知
架构演进建议
这套架构设计时考虑了快速上线的诉求,所以有一些地方在初期可以简化,随着业务发展再补齐。
| 阶段 | 重点 | 可以先跳过的部分 |
|---|
| 0→1 冷启动 | Gin + PostgreSQL + Redis + Asynq | Kafka、MongoDB、ES、Backstage |
| 1→10 增长期 | 引入 Kafka 解耦、ES 搜索、完善 Tracing | Rust 扩展、PgBouncer(并发不高时) |
| 10→N 规模期 | 全套上线、服务拆分、多语言扩展 | 按需演进 |
核心原则:用到才加,不要提前过度设计。Kafka 的学习和运维成本不低,在日活万级以下,用 Asynq + PostgreSQL 的 LISTEN/NOTIFY 完全可以替代大部分场景。
总结
这套架构的核心设计思路是:
稳定的主干 + 灵活的扩展点
主干是 Kong → Gin → Go-zero RPC → PostgreSQL 这条链路,覆盖 80% 的业务场景,技术栈统一,团队维护成本低。扩展点是 Kafka 的事件驱动、Python 的 AI 能力、Rust 的性能计算,按需接入,不强制所有业务都走同一条路。
对于快速上线的诉求,关键在于标准化:统一的代码生成(goctl)、统一的配置模型(YAML + Etcd)、统一的可观测性(OTel + Jaeger + Prometheus)。新业务接入时,脚手架拉出来,配置填一填,监控面板已经有数据了——这才是"快速"的真正来源。
架构不是越复杂越好,而是刚好够用、且能随业务生长的那个。