$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 堆代码 duidaima.com syntax = "proto3"; package ecommerce; import "google/protobuf/wrappers.proto"; option go_package = "/ecommerce"; service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order); rpc addOrder(Order) returns (google.protobuf.StringValue); } message Order { string id = 1; repeated string items = 2; string description = 3; float price = 4; google.protobuf.StringValue destination = 5; }目前有三种方式可以反向代理服务器:
3.使用外部配置,比较适用于不能修改源protobuf的情况下
syntax = "proto3"; package ecommerce; option go_package = "ecommerce/"; import "google/protobuf/wrappers.proto"; import "google/api/annotations.proto"; service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order){ option(google.api.http) = { get: "/v1/getOrder" }; } } message Order { string id = 1; repeated string items = 2; string description = 3; float price = 4; string destination = 5; }
pb ├── google │ └── api │ ├── annotations.proto │ └── http.proto └── product.proto之后执行protoc来生成代码。
protoc -I ./pb \ --go_out ./ecommerce --go_opt paths=source_relative \ --go-grpc_out ./ecommerce --go-grpc_opt paths=source_relative \ --grpc-gateway_out ./ecommerce --grpc-gateway_opt paths=source_relative \ ./pb/product.proto生成出来的代码,对比非gRPC-Gateway的版本会多出了一个*.gw.pb.go文件。
buf mod initbuf命令会创建buf.yaml文件,在此文件中添加依赖buf.build/googleapis/googleapis
version: v1 breaking: use: - FILE lint: use: - DEFAULT ## add deps: - buf.build/googleapis/googleapis2. 更新依赖
buf mod updatebuf命令从Buf Schema Registry (BSR)[2]中获取依赖,把你所有的 deps 更新到最新版。并且会生成 buf.lock 来固定版本
pb ├── buf.lock ├── buf.yaml └── product.proto3.创建一个buf.gen.yaml
version: v1 plugins: - plugin: go out: ecommerce opt: - paths=source_relative - plugin: go-grpc out: ecommerce opt: - paths=source_relative - name: grpc-gateway out: ecommerce opt: - paths=source_relative - generate_unbound_methods=true4.生成代码
buf generate pb执行的效果和上文中protoc命令一样
package main import ( "context" "net" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" pb "github.com/liangwt/note/grpc/ecosystem/grpc-gateway/ecommerce" "google.golang.org/grpc" ) func main() { grpcPort, gwPort := ":8009", ":8010" go func() { lis, err := net.Listen("tcp", grpcPort) if err != nil { panic(err) } s := grpc.NewServer() pb.RegisterOrderManagementServer(s, &OrderManagementImpl{}) if err := s.Serve(lis); err != nil { panic(err) } }() // 建立一个到gRPC Port的连接 conn, err := grpc.DialContext( context.Background(), "127.0.0.1"+grpcPort, grpc.WithBlock(), grpc.WithInsecure(), ) if err != nil { panic(err) } gwmux := runtime.NewServeMux() err = pb.RegisterOrderManagementHandler(context.Background(), gwmux, conn) if err != nil { panic(err) } http.ListenAndServe(gwPort, gwmux) // 以下和http.ListenAndServe(gwPort, gwmux)等价 // gwServer := &http.Server{ // Addr: gwPort, // Handler: gwmux, // } // if err := gwServer.ListenAndServe(); err != nil { // panic(err) // } }2.还有一种方式不依赖grpc服务,以本地函数调用的方式实现。
package main import ( "context" "net/http" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" pb "github.com/liangwt/note/grpc/ecosystem/grpc-gateway/ecommerce" ) func main() { gwmux := runtime.NewServeMux() err := pb.RegisterOrderManagementHandlerServer(context.Background(), gwmux, &OrderManagementImpl{}) if err != nil { panic(err) } http.ListenAndServe(":8010", gwmux) }
$ curl -s -X GET \ '127.0.0.1:8010/v1/getOrder?value=101' \ --header 'Accept: */*' | jq { "id": "101", "items": [ "Google", "Baidu" ], "description": "example", "price": 0, "destination": "example" }这里有个细节需要注意,google.protobuf.StringValue在映射到HTTP的时候默认参数名为value,所以访问时请求参数写成value=101
service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order){ option(google.api.http) = { get: "/v1/getOrder" }; } }
service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order){ option(google.api.http) = { get: "/v1/getOrder/{value}" }; } } curl -X GET \ '127.0.0.1:8010/v1/getOrder/101' \ --header 'Accept: */*' \我们还可以给参数设定个名字
service OrderManagement { rpc getOrder(getOrderReq) returns (Order){ option(google.api.http) = { get: "/v1/getOrder" }; } } message getOrderReq { google.protobuf.StringValue id = 1; } curl -X GET \ '127.0.0.1:8010/v1/getOrder?id=101' \ --header 'Accept: */*' \依旧可以把参数放到path中
service OrderManagement { rpc getOrder(getOrderReq) returns (Order){ option(google.api.http) = { get: "/v1/getOrder/{id}" }; } } message getOrderReq { google.protobuf.StringValue id = 1; } curl -X GET \ '127.0.0.1:8010/v1/getOrder/101' \ --header 'Accept: */*' \
syntax = "proto3"; package ecommerce; import "google/protobuf/wrappers.proto"; import "google/api/annotations.proto"; option go_package = "/ecommerce"; service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order) { option (google.api.http) = { get : "/v1/getOrder" }; } rpc addOrder1(Order) returns (google.protobuf.StringValue) { option (google.api.http) = { post : "/v1/addOrder1" body : "*" }; } rpc addOrder2(OrderRequest) returns (google.protobuf.StringValue) { option (google.api.http) = { post : "/v1/addOrder2" body : "order" }; } } message OrderRequest { Order order = 1; } message Order { string id = 1; repeated string items = 2; string description = 3; float price = 4; google.protobuf.StringValue destination = 5; }1.对于addOrder1接口
$ curl -s -X POST \ '127.0.0.1:8010/v1/addOrder1' \ --header 'Accept: */*' \ --data '{"id": "102","items": ["Google","Baidu"],"description": "example","price": 0,"destination": "example"}'2.对于addOrder2接口
$ curl -s -X POST \ '127.0.0.1:8010/v1/addOrder2' \ --header 'Accept: */*' \ --data '{"id": "102","items": ["Google","Baidu"],"description": "example","price": 0,"destination": "example"}'
func main() { gwmux := runtime.NewServeMux() err := pb.RegisterOrderManagementHandlerServer(context.Background(), gwmux, &OrderManagementImpl{}) if err != nil { panic(err) } err = gwmux.HandlePath("GET", "/hello/{name}", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) { w.Write([]byte("hello " + pathParams["name"])) }) http.ListenAndServe(":8010", gwmux) }
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest2. 生成swagger文件
protoc -I ./pb --openapiv2_out ./doc --openapiv2_opt logtostderr=true \ ./pb/product.proto当然更推荐使用buf工具
version: v1 plugins: - plugin: go out: ecommerce opt: - paths=source_relative - plugin: go-grpc out: ecommerce opt: - paths=source_relative - name: grpc-gateway out: ecommerce opt: - paths=source_relative - generate_unbound_methods=true - name: openapiv2 out: doc opt: - logtostderr=true于是便可以得到doc/product.swagger.json文件