1.4 技术选型表:Go 框架与云组件对齐

边界划好、成本算清,接下来就是选型。创业团队选型的核心矛盾是:既要 fast,又怕被小众框架绑架。我们的选型原则只有三条——团队里至少有一个人用过、官方文档完整能查到排障指南、社区活跃且两年内不会消失。这一节给出我们最终的选型对照表,以及每一条选择背后的取舍。

Go 通信框架:从 HTTP 到 gRPC

服务间通信是第一个要拍板的。候选有三个:裸 net/http 加 JSON、gin 加 JSON、gRPC 加 protobuf。我们最后选了 gRPC 做服务间通信,同时保留一个 gin 层做对外的 HTTP 接口(小程序和老后台的存量调用)。

选 gRPC 的直接原因是性能和契约:protobuf 二进制序列化比 JSON 快两到三倍,更重要的是 proto 文件本身就是契约文档,谁改接口谁改 proto,CI 里跑一下编译就知道消费方有没有炸。

proto 定义长这样,所有跨域接口必须先评审 proto 再写实现:

syntax = "proto3";

package order;

service Order {
  // 创建订单:商品域提供快照价,用户域提供等级折扣
  rpc CreateOrder(CreateOrderReq) returns (CreateOrderResp);
}

message CreateOrderReq {
  string user_id = 1;
  repeated OrderItem items = 2;  // 下单瞬间冻结的库存明细
}
候选方案 开发效率 性能 契约约束 我们的结论
net/http + JSON 高 低 无 只适合内部临时接口
gin + JSON 高 中 无 用作对外 HTTP 网关层
gRPC + protobuf 中 高 强制 proto 服务间通信最终选择
kratos 等全家桶 中 中 强 团队没人熟,放弃

没选 kratos 这类微服务全家桶框架,纯粹是人手问题:五个人没人有 kratos 生产经验,引入全家桶意味着先花两周学框架。标准库底座加 grpc-go 足够覆盖我们九成的场景,等团队有余力再考虑。

全家桶框架还有一个隐性代价:升级。框架自身的破坏性版本变更往往会裹挟业务代码跟着强行迁移,对五人团队是致命的时间黑洞。相反裸 grpc-go 的升级频率低,且每次只动通信这一层。

底层框架:每服务一个 main

每个服务的骨架非常标准:grpc server 跑业务,http server 暴露健康检查和 pprof:

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("listen failed: %v", err)
    }
    s := grpc.NewServer(
        grpc.UnaryInterceptor(recoverInterceptor), // 兜底 panic
    )
    pb.RegisterOrderServer(s, &order.Server{})
    go serveHTTP() // /healthz、/debug/pprof
    log.Println("order service on :50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("serve failed: %v", err)
    }
}

go.mod 的依赖刻意保持少,三个服务的依赖合起来不到 20 个 package。少一个依赖就少一次连锁升级的痛苦。核心几行长这样:

module shop/order

go 1.21

require (
    github.com/grpc/grpc-go v1.62.0   // 固定到上一个 minor
    google.golang.org/protobuf v1.33.0
    github.com/redis/go-redis/v9 v9.5.0
    go.uber.org/zap v1.27.0            // 日志库,后面接 CLS
)

云组件对齐:五件套

云组件的选择相对省心,因为要解决的问题很明确,选型问题最终落到五件套对应关系上:

需求场景 腾讯云组件 替代自建方案 不自建的理由
服务运行 云服务器 CVM 自建机房 五人团队没有运维硬件人力
削峰解耦 消息队列 RocketMQ 自建 Kafka Kafka 运维成本高
统一日志 日志服务 CLS ELK ELK 每月机器费更高
监控告警 云监控 Prometheus 已有 CVM 内置指标
对外入口 API 网关 nginx 自研 限流鉴权开箱即用

这套组合的核心思路是:业务团队只写业务代码,所有“管道”类的能力能买就买。自建 Kafka 或 ELK 的隐性成本不是机器费,是那一个会修它的工程师的青春。团队里没有专职运维,这条约束决定了所有选型。

五件套之间还有一个隐性收益:账号体系、计费、权限、监控大盘天然打通。CVM 的机器指标直接进云监控,触发告警后能联动策略;CLS 的日志可以在云监控里配关键词告警;API 网关的访问日志也能落到同一个 CLS 主题。如果自建这些组件,每一项都要单独搭监控、单独写告警脚本,五个人根本不够分。

为什么不上容器和 K8s

这是被问得最多的一个决定。道理很简单:K8s 需要至少一个专职 SRE 心智,我们的服务只有三个,用 systemd 加 CVM 镜像挨个部署完全 hold 得住。等到服务超过十个、或者有了专门的运维同学,再迁容器不迟。CVM 的另一好处是计费直观,新购、升配、退款的账单一目了然,做成本治理时有据可查,这也是后来成本治理一篇能落地的前提。第一批三台机器在腾讯云 CVM 特惠期间购入,预算只占当月云支出的零头。

踩坑:版本选择别太追新

选型定下后的第一周就踩了版本坑。grpc-go 用了当时最新的 1.6x,结果 etcd 客户端的依赖有 conflict,编译过不去又回退了一个 minor 版本。此后我们定了一条铁律:核心依赖一律用上一个 minor 版本而非最新版,所有版本固定在 go.mod 里,升级必须有一个人专门 review。protobuf 工具链的版本也一并固定,否则 proto 生成代码格式天天变,diff 里全是噪音。

另一个坑是客户端兼容。移动端老版本用的是 JSON 接口,gRPC 上线时存量调用不可能一夜全切。我们的做法是网关侧同时提供 gRPC 与 HTTP 两种入口,新老流量按端版本灰度切换,老版本逐步淘汰。这要求网关能做协议转换,API 网关一节里展开了具体做法。

小结

选型的出发点不是够不够先进,而是你团队能 hold 住什么。我们的组合是 gRPC 加 protobuf 做服务间通信、gin 做对外 HTTP、CVM 做算力底座、RocketMQ 削峰、CLS 统一日志、云监控加 API 网关补齐治理能力。核心依赖不追新版本,固定且专人 review。下一章开始进入真正的上云实操:先在本地用 docker-compose 跑通最小集群,再一台台把服务搬上 CVM。

选型表不是一次定终身的合同。我们约定每半年 review 一次:哪一项变成了瓶颈、哪一项可以下掉。第二年的复盘里唯一调整的是把日志库从自封装换成了 CLS 官方采集器,其余全部沿用至今。一张选型表能撑两年,靠的不是眼光,是前面那三条朴素的原则。

延伸阅读