Go 語(yǔ)言微服務(wù)框架 Kratos 是怎么讓 gRPC 自動(dòng)變成 REST 接口的?
介紹
Kratos 官方提供了 protoc-gen-go-http 插件,該插件根據(jù) .proto 文件中的 google.api.http option,生成 HTTP handler、路由注冊(cè)代碼、參數(shù)綁定邏輯。
與 grpc-gateway 不同,protoc-gen-go-http 并不需要額外的“網(wǎng)關(guān)”層。它直接在服務(wù)端生成原生的 http.Handler,讓 gRPC 服務(wù)天然具備 REST 能力。
google.api.http option 詳解
google.api.http option 是 Google API 的一個(gè) Protobuf 擴(kuò)展,用來(lái)把 gRPC 方法映射為 HTTP REST 接口。
syntax = "proto3";
package helloworld.v1;
import"google/api/annotations.proto";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/helloworld/{name}"
      // 可以添加附加接口
      additional_bindings {
          // 定義一個(gè) POST 接口,并且把 body 映射到 HelloRequest
          post: "/v1/greeter/say_hello",
          body: "*",
      }
    };
  }
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}閱讀上面這段代碼,我們可以在 .proto 文件中添加 google.api.http option,其中,get: "/helloworld/{name}" 是把 name 映射到 URL 路徑參數(shù),即 /helloworld/frank;
post: "/v1/greeter/say_hello" 定義一個(gè) POST 請(qǐng)求方式的 API,body: "*" 代表整個(gè)請(qǐng)求體(JSON)映射到 HelloRequest;
additional_bindings 可以讓同一個(gè) RPC 方法綁定多個(gè) HTTP endpoint(GET/POST/PUT等)。
需要注意的是,如果希望通過(guò) gRPC 服務(wù)自動(dòng)生成 REST 接口,可以使用 Google 官方的 grpc-gateway 插件 protoc-gen-grpc-gateway,它會(huì)在 gRPC 服務(wù)前增加一層 HTTP 轉(zhuǎn)發(fā)網(wǎng)關(guān),將 HTTP 請(qǐng)求映射并轉(zhuǎn)發(fā)到 gRPC 服務(wù)端。
相比之下,grpc-gateway 適合已有 gRPC 服務(wù)、希望兼容 REST 訪問的場(chǎng)景;而 protoc-gen-go-http 則更偏向在 Kratos 中統(tǒng)一生成原生 gRPC + HTTP 服務(wù)代碼,部署更輕量。
關(guān)于 grpc-gateway 的使用,我們?cè)谥暗奈恼隆窯olang 語(yǔ)言 gRPC 服務(wù)怎么同時(shí)支持 gRPC 和 HTTP 客戶端調(diào)用?」中介紹過(guò),本文不再贅述。
在 Kratos 中使用 protoc-gen-go-http 插件
protoc-gen-go-http 插件是 Kratos 的一部分,它是 Kratos 官方開發(fā)和維護(hù)的,讓我們可以通過(guò) Protobuf IDL 同時(shí)生成 gRPC 服務(wù)和原生 HTTP 服務(wù)(基于 net/http),無(wú)需使用額外的 “網(wǎng)關(guān)”。
該插件的作用是根據(jù) .proto 文件中的 google.api.http option,生成相應(yīng)的 HTTP 適配層代碼。具體來(lái)說(shuō),它會(huì)生成:
- 一個(gè) RegisterXXXHTTPServer(mux, server) 函數(shù)(用于把 gRPC 風(fēng)格的服務(wù)注冊(cè)為 HTTP handler)。
 - 一個(gè)接口 XXXHTTPServer,定義 HTTP 層方法簽名。
 - 自動(dòng)路由注冊(cè)表(根據(jù) google.api.http 選項(xiàng)里的路徑映射)。
 
這些代碼都是純 Go 的,在生成的代碼文件 api/helloworld/v1/greeter_http.pb.go 中,不依賴 grpc-gateway 或任何中間層。
理論講完了,接下來(lái)我們進(jìn)入實(shí)戰(zhàn)環(huán)節(jié):如何用 protoc-gen-go-http 生成可直接運(yùn)行的 HTTP 服務(wù)代碼。
安裝插件:
在使用 protoc-gen-go-http 插件之前,我們先通過(guò)命令安裝插件:
go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest生成代碼:
在 Part 02 中,我們定義的 .proto 文件,可以使用 protoc,或者 make 命令,或者 Kratos 提供的 CLI 工具 kratos,生成 Client 和 Service 代碼。
我們以 kratos cli 為例,代碼如下:
生成 Client 代碼:
kratos proto client api/helloworld/v1/greeter.proto生成代碼文件:
api/helloworld/v1/greeter.pb.go
api/helloworld/v1/greeter_grpc.pb.go
# 注意 http 代碼只會(huì)在 proto 文件中聲明了 http 時(shí)才會(huì)生成
api/helloworld/v1/greeter_http.pb.go其中,api/helloworld/v1/greeter_http.pb.go 文件就是提供 REST API 的相關(guān)代碼。
// ...
type GreeterHTTPServer interface {
 // SayHello Sends a greeting
 SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
func RegisterGreeterHTTPServer(s *http.Server, srv GreeterHTTPServer) {
 r := s.Route("/")
 r.POST("/v1/greeter/say_hello", _Greeter_SayHello0_HTTP_Handler(srv))
 r.GET("/helloworld/{name}", _Greeter_SayHello1_HTTP_Handler(srv))
}
// ...總結(jié)
protoc-gen-go-http 是 Kratos 在 gRPC 生態(tài)上的一次重要增強(qiáng)。
它讓開發(fā)者可以在一份 .proto 文件中同時(shí)定義 gRPC 與 HTTP 接口,并自動(dòng)生成服務(wù)代碼,避免維護(hù)多套路由邏輯。
對(duì)于希望使用 Kratos 構(gòu)建同時(shí)支持 gRPC + REST 的 Go 服務(wù),這個(gè)插件幾乎是“必備工具”。















 
 
 
















 
 
 
 