This is a creation in Article, where the information may have evolved or changed.
GRPC HTTP protocol Conversion
Just when there is a need, we see this implementation posture. A blog from CoreOS, reprinted to the GRPC official blog Grpc with REST and Open APIs.
ETCD3 switch to GRPC to be compatible with the original API, but also to provide Http/json way of the API, in order to meet this need, either develop two sets of APIs, or implement a conversion mechanism, they chose the latter, and we choose to follow their footsteps.
They implemented a gateway to the protocol transformation that corresponds to the project Grpc-gateway on GitHub, which is responsible for receiving client requests and then deciding to go directly to the GRPC service or to the HTTP service, and of course the HTTP service needs to request the GRPC service to get a response. It then transitions to the JSON response to the client. Structure
Let's go straight to the actual combat. Based on the HELLO-TLS project extension, client churn is small and the server and proto changes are large.
Installing Grpc-gateway
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
Project structure:
$GOPATH/src/grpc-go-practice/example/|—— hello-http-2/ |—— client/ |—— main.go // 客户端 |—— server/ |—— main.go // 服务端|—— keys/ // 证书目录 |—— server.key |—— server.pem|—— proto/ |—— google // googleApi http-proto定义 |—— api |—— annotations.proto |—— annotations.pb.go |—— http.proto |—— http.pb.go |—— hello_http.proto // proto描述文件 |—— hello_http.pb.go // proto编译后文件 |—— hello_http_pb.gw.go // gateway编译后文件
The two proto profiles in Google's official API are used here, and the direct copy is not modified, which defines the HTTP option for the protocol buffer extension, providing support for GRPC HTTP conversion.
Sample code
Proto/hello_http.proto
syntax = "proto3"; // 指定proto版本package proto; // 指定包名import "google/api/annotations.proto";// 定义Hello服务service HelloHttp { // 定义SayHello方法 rpc SayHello(HelloHttpRequest) returns (HelloHttpReply) { // http option option (google.api.http) = { post: "/example/echo" body: "*" }; }}// HelloRequest 请求结构message HelloHttpRequest { string name = 1;}// HelloReply 响应结构message HelloHttpReply { string message = 1;}
This SayHello
adds the HTTP option to the original method definition, the Post method, and the route to "/example/echo".
Compiling proto
cd $GOPATH/src/grpc-go-practice/example/hello-http-2/proto# 编译google.apiprotoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto# 编译hello_http.protoprotoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=git.vodjk.com/go-grpc/example/proto/google/api:. ./*.proto# 编译hello_http.proto gatewayprotoc --grpc-gateway_out=logtostderr=true:. ./hello_http.proto
Note that it is necessary to compile the two proto files in the Google/api, and specify the introduction package name when compiling Hello_http.proto, and finally use Grpc-gateway to compile the generated hello_http_pb.gw.go
file, which is used for the protocol conversion, View the file to see the generated HTTP handler, handle the route defined above "Example/echo" to receive the post parameters, call the Hellohttp Service client Request GRPC service and respond to the results.
Server/main.go
Package Mainimport ("Crypto/tls" "FMT" "Io/ioutil" "Log" "Net" "Net/http" "Strings" "github.com/g Rpc-ecosystem/grpc-gateway/runtime "Golang.org/x/net/context" "Google.golang.org/grpc" PB "Git.vodjk.com/go-grpc /example/proto "" Google.golang.org/grpc/credentials "" Google.golang.org/grpc/grpclog ")// Define Hellohttpservice and implement the agreed interface type Hellohttpservice struct{}//hellohttpservice ... var hellohttpservice = Hellohttpservice{}func (H hellohttpservice) SayHello (CTX context. Context, in *PB. Hellohttprequest) (*PB. Hellohttpreply, error) {resp: = new (Pb). hellohttpreply) resp. Message = "Hello" + in. Name + "." Return resp, nil}//Grpchandlerfunc examines the request protocol and returns the HTTP Handlerfunc grpchandlerfunc (grpcserver *grpc. Server, Otherhandler http. Handler) http. Handler {return HTTP. Handlerfunc (Func (w http. Responsewriter, R *http. Request) {//TODO (Tamird): Point to merged grpc code rather than a PR. This is a partial recreation of GRPC ' s internal checks https://github.com/grpc/grpc-go/pull/514/files#diff-95e9a25b738459a2d3030e1e6fa2a718R61 if r.protomajor = = 2 & ;& strings. Contains (R.header.get ("Content-type"), "Application/grpc") {grpcserver.servehttp (W, R)} else { Otherhandler.servehttp (W, R)})}func main () {endpoint: = "127.0.0.1:50052"//instantiation standard GRPC Server C Reds, err: = credentials. Newservertlsfromfile (".. /.. /keys/server.pem ",". /.. /keys/server.key ") if err! = Nil {grpclog. Fatalf ("Failed to generate credentials%v", err)} conn, _: = Net. Listen ("TCP", endpoint) Grpcserver: = Grpc. NewServer (GRPC. Creds (creds)) PB. Registerhellohttpserver (Grpcserver, Hellohttpservice)//HTTP-GRPC Gateway CTX: = context. Background () ctx, Cancel: = context. Withcancel (CTX) defer cancel () dcreds, err: = credentials. Newclienttlsfromfile (".. /.. /keys/server.pem "," server name ") If err! = Nil {grpclog. Fatalf ("Failed to create TLS credentials%V ", Err)} dopts: = []grpc. Dialoption{grpc. Withtransportcredentials (Dcreds)} Gwmux: = Runtime. Newservemux () Err = PB. Registerhellohttphandlerfromendpoint (CTX, Gwmux, endpoint, dopts) if err! = Nil {fmt. Printf ("Serve:%v\n", err) return} MUX: = http. Newservemux () Mux. Handle ("/", Gwmux) if err! = Nil {panic (ERR)}//Turn on HTTP Service cert, _: = Ioutil. ReadFile (".. /.. /keys/server.pem ") Key, _: = Ioutil. ReadFile (".. /.. /keys/server.key ") var demokeypair *tls. Certificate pair, Err: = TLS. X509keypair (cert, key) if err! = Nil {panic (err)} Demokeypair = &pair srv: = &http. server{Addr:endpoint, Handler:grpchandlerfunc (Grpcserver, MUX), Tlsconfig: &tls. config{certificates: []tls. Certificate{*demokeypair},},} FMT. Printf ("Grpc and HTTPS on port:%d\n", 50052) Err = srv. Serve (TLS. Newlistener (conn, srv. Tlsconfig)) If err! = Nil {log. Fatal ("LIstenandserve: ", Err)} return}
All right, that's a big lump. The core is to open an HTTP server, check whether the request is GRPC or HTTP after receiving the request, and then decide whether it is handled directly by the GRPC service or forwarded to the gateway. The grpcHandlerFunc
function is responsible for processing the decision with which handler to process the request, this method is directly copied to use, the original note that they also copy from elsewhere. Thanks to the contributors.
Basic Flow:
Instantiating standard GRPC Server
Registering GRPC server with the gateway
Turn on HTTP service, handler assign to Grpchandlerfunc method
Note: HTTPS must be turned on
Run results
Open service:
# hello-http-2/servergo run main.go> grpc and https on port: 50052
Call the GRPC client:
# hello-http-2/clientgo run main.go> Hello gRPC.
Request https:
curl -X POST -k https://localhost:50052/example/echo -d '{"name": "gRPC-HTTP is working!"}'> {"message":"Hello gRPC-HTTP is working!."}
Why is hello-http-2, because 1 is an incomplete implementation posture, can not use HTTPS, but need to open GRPC Service and HTTP service separately, here do not explain.
Reference
Sample code for this series