Go gRPC进阶-gRPC转换HTTP(十)

我们通常把RPC用作内部通信,而使用Restful Api进行外部通信。为了避免写两套应用,我们使用grpc-gateway把gRPC转成HTTP。服务接收到HTTP请求后,grpc-gateway把它转成gRPC进行处理,然后以JSON形式返回数据。本篇代码以上篇为基础,最终转成的Restful Api支持bearer token验证、数据验证,并添加swagger文档。

gRPC转成HTTP 编写和编译proto

1.编写simple.proto

syntax = "proto3"; package proto; import "github.com/mwitkow/go-proto-validators/validator.proto"; import "go-grpc-example/10-grpc-gateway/proto/google/api/annotations.proto"; message InnerMessage { // some_integer can only be in range (1, 100). int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}]; // some_float can only be in range (0;1). double some_float = 2 [(validator.field) = {float_gte: 0, float_lte: 1}]; } message OuterMessage { // important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax). string important_string = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}]; // proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage. InnerMessage inner = 2 [(validator.field) = {msg_exists : true}]; } service Simple{ rpc Route (InnerMessage) returns (OuterMessage){ option (google.api.http) ={ post:"/v1/example/route" body:"*" }; } }

可以看到,proto变化不大,只是添加了API的路由路径

option (google.api.http) ={ post:"/v1/example/route" body:"*" };

2.编译simple.proto

simple.proto文件引用了google/api/annotations.proto(来源),先要把它编译了。我这里是把google/文件夹直接复制到项目中的proto/目录中进行编译。发现annotations.proto引用了google/api/http.proto,那把它也编译了。

进入annotations.proto所在目录,编译:

protoc --go_out=plugins=grpc:./ ./http.proto protoc --go_out=plugins=grpc:./ ./annotations.proto

进入simple.proto所在目录,编译:

#生成simple.validator.pb.go和simple.pb.go protoc --govalidators_out=. --go_out=plugins=grpc:./ ./simple.proto #生成simple.pb.gw.go protoc --grpc-gateway_out=logtostderr=true:./ ./simple.proto

以上完成proto编译,接着修改服务端代码。

服务端代码修改

1.server/文件夹下新建gateway/目录,然后在里面新建gateway.go文件

package gateway import ( "context" "crypto/tls" "io/ioutil" "log" "net/http" "strings" pb "go-grpc-example/10-grpc-gateway/proto" "go-grpc-example/10-grpc-gateway/server/swagger" "github.com/grpc-ecosystem/grpc-gateway/runtime" "golang.org/x/net/http2" "golang.org/x/net/http2/h2c" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/grpclog" ) // ProvideHTTP 把gRPC服务转成HTTP服务,让gRPC同时支持HTTP func ProvideHTTP(endpoint string, grpcServer *grpc.Server) *http.Server { ctx := context.Background() //获取证书 creds, err := credentials.NewClientTLSFromFile("../tls/server.pem", "go-grpc-example") if err != nil { log.Fatalf("Failed to create TLS credentials %v", err) } //添加证书 dopts := []grpc.DialOption{grpc.WithTransportCredentials(creds)} //新建gwmux,它是grpc-gateway的请求复用器。它将http请求与模式匹配,并调用相应的处理程序。 gwmux := runtime.NewServeMux() //将服务的http处理程序注册到gwmux。处理程序通过endpoint转发请求到grpc端点 err = pb.RegisterSimpleHandlerFromEndpoint(ctx, gwmux, endpoint, dopts) if err != nil { log.Fatalf("Register Endpoint err: %v", err) } //新建mux,它是http的请求复用器 mux := http.NewServeMux() //注册gwmux mux.Handle("http://www.likecs.com/", gwmux) log.Println(endpoint + " HTTP.Listing whth TLS and token...") return &http.Server{ Addr: endpoint, Handler: grpcHandlerFunc(grpcServer, mux), TLSConfig: getTLSConfig(), } } // grpcHandlerFunc 根据不同的请求重定向到指定的Handler处理 func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler { return h2c.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { grpcServer.ServeHTTP(w, r) } else { otherHandler.ServeHTTP(w, r) } }), &http2.Server{}) } // getTLSConfig获取TLS配置 func getTLSConfig() *tls.Config { cert, _ := ioutil.ReadFile("../tls/server.pem") key, _ := ioutil.ReadFile("../tls/server.key") var demoKeyPair *tls.Certificate pair, err := tls.X509KeyPair(cert, key) if err != nil { grpclog.Fatalf("TLS KeyPair err: %v\n", err) } demoKeyPair = &pair return &tls.Config{ Certificates: []tls.Certificate{*demoKeyPair}, NextProtos: []string{http2.NextProtoTLS}, // HTTP2 TLS支持 } }

它主要作用是把不用的请求重定向到指定的服务处理,从而实现把HTTP请求转到gRPC服务。

2.gRPC支持HTTP

//使用gateway把grpcServer转成httpServer httpServer := gateway.ProvideHTTP(Address, grpcServer) if err = httpServer.Serve(tls.NewListener(listener, httpServer.TLSConfig)); err != nil { log.Fatal("ListenAndServe: ", err) } 使用postman测试

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzpjxj.html