kratos — Go 微服务一锅出 HTTP 和 gRPC 两份服务
是什么
kratos 是一个 Go 语言的微服务框架——你写一份业务逻辑,它同时给你生成 HTTP(给手机 App / 浏览器调)和 gRPC(给别的微服务调)两套服务。日常类比:像一家餐厅同一份菜谱,既能做堂食(HTTP/JSON 容易看懂)又能做外卖(gRPC/Protobuf 打包紧凑),不用厨师写两份。
先把两个新词说清楚:gRPC = 谷歌做的二进制 RPC(远程调用)协议,服务间互调首选;Protobuf = 用 .proto 文件描述接口的紧凑数据格式,比 JSON 小一半。类比的局限是:HTTP 和 gRPC 性能、调试体验差不少,类比里两份菜成本一样,实际两套服务的运行成本不一样。
它是 Go 中文社区最常用的微服务框架之一(GitHub 24k+ star),把过去要自己拼的几样东西打包到一起:服务注册发现、配置中心、中间件、日志、链路追踪、错误码。
kratos new helloworld # 一行命令生成项目骨架cd helloworld && kratos run# :8000 HTTP 和 :9000 gRPC 同时跑起来这套思路核心是 API 优先:你先写一份 .proto 文件描述接口(参数、返回、错误码),CLI 工具自动把骨架代码生成出来,剩下你只填业务逻辑。HTTP/JSON 还是 gRPC/Protobuf 都是从这份 proto 派生的。
为什么重要
不理解 kratos,下面这些事都没法解释:
- 为什么写一份
.proto文件就能同时跑 HTTP 和 gRPC,不用自己写两套 handler - 为什么 Go 微服务框架那么多(gin / echo / chi / fiber / go-zero / hertz),还需要 kratos 这一个
- 为什么有的中间件(鉴权、限流)写一次两边都生效,有的(CORS)就只能写在 HTTP 一侧
- 为什么 v2 升 v3 不是改个 import 路径就行,要逐处审 New() 调用
核心要点
kratos 把”微服务一堆套件”拆成 三层抽象:
-
Transport 层——HTTP Server 和 gRPC Server 都包成同一个
app.Server接口。类比:插座面板,HTTP 和 gRPC 都是”插头”,业务逻辑只认面板不认插头形状。 -
Service 层(CLI 生成)——CLI 工具读你的
.proto文件,调 protoc 生成 service 接口骨架;你只填业务实现,HTTP 和 gRPC 的路由解析它都帮你写好。 -
可插拔组件——注册中心(etcd / consul / nacos)、配置中心、日志后端都是接口,运行时决定用哪个。类比:电脑的 USB 口,鼠标键盘 U 盘随便插。
三层加起来,一个项目要做的事是:定义 proto → CLI 生成骨架 → 填业务 → 选组件接线。
实践案例
案例 1:用 CLI 生成第一个项目
go install github.com/go-kratos/kratos/cmd/kratos/v2@latestkratos new helloworldcd helloworldgo mod tidykratos run跑起来看终端,会同时打两行:HTTP 服务启动 :8000 和 gRPC 服务启动 :9000。
打开 cmd/helloworld/main.go 你能看到三个东西:httpSrv / grpcSrv 各创建一次,最后 kratos.New(kratos.Server(httpSrv, grpcSrv)) 把它们一起塞进同一个 app。两个端口、一份业务,靠的就是 Transport 抽象。
目录结构里 api/(proto 定义)/ internal/biz(业务核心)/ internal/data(数据访问)/ internal/service(接口实现)的分层,是 kratos 推荐的约定,照着填能省下纠结”代码放哪”的时间。
注意:kratos run 自身不依赖 protoc,但案例 2 用到的 kratos proto add 会调 protoc 生成代码,要先 go install 四个插件:protoc-gen-go / protoc-gen-go-grpc / protoc-gen-go-http / protoc-gen-openapi。第一次跑前装好,案例 2 才不会报 program not found。
案例 2:定义一份 proto,两套服务自动出来
kratos proto add api/helloworld/helloworld.proto# 编辑 proto 加一个 GetUser rpckratos proto server api/helloworld/helloworld.proto -t internal/serviceinternal/service/helloworld.go 里会冒出一个 GetUser(ctx, *GetUserRequest) (*GetUserReply, error) 方法骨架。你只填里面的查数据库逻辑,剩下的事——HTTP 路由 /v1/users/{id}、gRPC 方法 helloworld.Greeter/GetUser——CLI 都生成了。一份业务实现,两套客户端都能调。
// api/helloworld/helloworld.proto 片段service Greeter { rpc GetUser (GetUserRequest) returns (GetUserReply) { option (google.api.http) = { get: "/v1/users/{id}" }; }}google.api.http 那行注解是关键:它告诉 CLI”这个 rpc 同时暴露成 GET /v1/users/{id}“。没这行就只生成 gRPC,加上就两套都出。
案例 3:写一个中间件,HTTP 和 gRPC 都生效
import "github.com/go-kratos/kratos/v2/log"import "github.com/go-kratos/kratos/v2/middleware/logging"
logger := log.NewStdLogger(os.Stdout) // 用标准库 stdout 当日志后端app := kratos.New( kratos.Name("helloworld"), kratos.Server(httpSrv, grpcSrv),)// 给两个 server 都装上日志中间件httpSrv.Use(logging.Server(logger))grpcSrv.Use(logging.Server(logger))调用 HTTP 接口和 gRPC 接口,日志里都能看到 trace_id(trace_id = 一次请求穿过多个微服务时,用于把所有日志串起来的唯一标识,类似快递单号)。同一个中间件函数(来自 middleware/logging)对两边都生效——这就是 Transport 抽象的好处,不用为 HTTP 和 gRPC 分别写两个版本。
中间件本身签名是 func(Handler) Handler,Handler 收 ctx 和 req、返 reply, error,HTTP 和 gRPC 都被框架包成这个统一形态后再喂给中间件。所以鉴权 / 限流 / 链路追踪只写一次。
踩过的坑
-
v2 升 v3 不是平滑升级——v3 把隐式行为(自动注入 logger、默认中间件顺序)改成显式,老项目得逐处审
kratos.New(...)调用,不是改 import 路径就完事,跨大版本前先看 CHANGELOG。 -
CLI 缺 protoc 插件——第一次跑
kratos proto add经常报protoc-gen-go-http: program not found,要先go installprotoc-gen-go / protoc-gen-go-grpc / protoc-gen-go-http / protoc-gen-openapi 四个,README 里那段经常被新人跳过。 -
HTTP-only 中间件污染 gRPC——CORS 这类只对浏览器有意义的中间件如果直接
app.Use()装到通用层,gRPC 调用会被白白过一遍。要用selector.Server(...).Match(...)按路径或方法过滤,新人容易忘。 -
本地开发不连 etcd 启动 panic——默认配置
discovery: etcd://...是给生产用的,本机调试要把 endpoint 改成direct://localhost:9000,否则启动时连不上注册中心直接 panic,很多人第一次跑 example 卡这。
适用 vs 不适用场景
适用:
- 一个服务同时要被 App / 浏览器(HTTP)和别的微服务(gRPC)调用——一份 proto 出两套
- 团队需要统一的日志 / 错误码 / 链路追踪规范,不想每个项目各搞一套
- 接口频繁变更,靠 Protobuf 做契约比手写 OpenAPI 维护成本低
不适用:
- 只对外提供 RESTful HTTP 的小服务——直接用 gin / echo / chi / fiber,不用扛 protoc 工具链
- 团队没人写过 Protobuf 也不打算学——CLI 全靠 .proto 驱动,绕不开
- 性能敏感的 RPC 密集型场景且只用 gRPC——可以考虑更轻的纯 gRPC 方案(hertz / grpc-go 直接用)
历史小故事(可跳过)
- 2019 年:起源于一家国内视频网站的内部 Go 微服务框架,开源到 GitHub
- 2020 年:受 go-kit / go-micro / google/go-cloud / go-zero 几个项目影响,逐步形成”Transport + Middleware + 可插拔组件”风格
- 2021 年:发布 v2,重写了 Transport 抽象,把 HTTP 和 gRPC 真正拉到同一层
- 2023 年:发布 v3,砍核心依赖、把隐式行为改显式(“explicit is better than implicit”),要求 Go 1.25+
- 现在:社区 maintainer 来自多家公司,是中文 Go 社区最活跃的微服务项目之一,star 数 24k+
学到什么
- 抽象的价值不是”多包一层”,是”省一份重复代码”——Transport 抽象让你写一遍中间件 HTTP+gRPC 都生效
- CLI + 代码生成 是 Go 微服务降低样板代码的主流路线,写 proto 比手写 handler 节省的不只是字数
- 可插拔组件 让框架不绑定具体基础设施,etcd / consul / nacos 换一个不用改业务代码
- 大版本升级要看显式 vs 隐式的取舍——v3 选了显式,代价是迁移成本,收益是调试时不再”猜默认行为”
延伸阅读
- 官方文档:go-kratos.dev(中英文都有,例子从 helloworld 一路到完整电商)
- GitHub examples:go-kratos/examples(按场景分目录,找最像自己业务的抄)
- 视频教程:B 站搜 “kratos 微服务”,社区 maintainer 自己讲过几场,重点看 v3 那场
- etcd —— kratos 默认推荐的注册中心
- jaeger —— OpenTelemetry 链路追踪后端,kratos 自带集成
关联
- gin —— 单一 HTTP 框架,比 kratos 轻;不需要 gRPC 时首选
- echo —— 另一个 HTTP-only 选项,性能和 gin 接近
- chi —— 标准库风格 router,比 kratos 更裸;适合微服务里只挑路由功能用
- fiber —— Express 风格 HTTP 框架,迁移自 Node 团队友好
- etcd —— kratos 注册中心默认选项
- jaeger —— 链路追踪后端,kratos OpenTelemetry 集成默认对接
- prometheus —— 监控指标,kratos middleware/metrics 默认输出 prometheus 格式