1. 什么时候该拆
单体跑得好好的别拆。团队大了、模块各自迭代节奏不同、某个模块要独立扩容(如推送服务)、技术栈要分叉,才拆。过早拆微服务是给自己找分布式系统的麻烦。
2. 按业务能力划边界
别按技术层拆(用户-api、用户-service、用户-dao 三个服务),要按业务域拆(用户服务、订单服务、商品服务)。每个服务自己持有数据,对外只暴露接口。康威定律:团队结构决定服务边界。
3. 服务粒度
一个服务一个小团队能全负责(开发+上线+值班)。拆太细(一个接口一个服务)运维成本爆炸,拆太粗又回到单体。订单服务包含创建、支付回调、查询,是合理的。
4. API 网关统一入口
前端不直接连 N 个服务,统一走网关。网关做鉴权、限流、路由、聚合:
r.Any("/api/*path", func(c *gin.Context) { svc := routeTo(c.Param("path")) // 按路径前缀分流到服务 proxy(c, svc) })
鉴权在第 6 篇讲过,放网关一次做完,各服务信任网关注入的 uid(第 12 篇的 X-Trace-Id / X-User-Id 头)。
5. 拆之前先做的准备
- 数据库先按服务分库(第 4 篇多租户的思路反向:不是租户隔离,是服务隔离)
- 接口契约先定清楚(OpenAPI 或 gRPC proto)
- 可观测先就位(第 12 篇日志、链路、第 6 篇指标),不然分布式排查瞎眼
- 配置中心、注册发现先搭(本季第 6 篇)
6. 通信方式选择
对外(前端/第三方)用 HTTP/JSON 或 GraphQL(第 1 篇)。服务内部用 gRPC(下一篇)或消息(第 3 篇)。同步调用用 gRPC,异步解耦用消息。
7. 上线清单
没到团队/扩容痛点不拆。按业务域拆,不按技术层拆。每个服务自拥数据,不共享库。网关收口鉴权路由。拆前先把可观测、配置中心、契约定好。先分库再分服务,别反过来。
下一篇讲 gRPC,服务内部怎么高效说话。