Golang gRPC practice serial seven HTTP protocol conversion

gRPC HTTP protocol conversion

Just as there is the demand to see the realization of this posture. Coreos from a blog, reproduced to grpc official blog GRPC with REST APIs and Open .

After etcd3 use grpc for compatibility with the original api, while providing API http / json way, in order to meet this demand, either to develop two sets of API, or to implement a conversion mechanism, they chose the latter, but we choose to follow them footsteps.

They achieved a gateway protocol conversion, the corresponding item on GitHub grpc-Gateway , the gateway is responsible for receiving client requests, and then decided to forward directly to the grpc service or transferred to http service, of course, also need to request the service http grpc service acquisition response and then converted to json response to the client. FIG Structure:

image description

Here we directly combat it. Based hello-tls extension project, the client modifications are minor, the server and the larger proto changes.

Installation 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编译后文件

This uses two proto official google profile of Api, do not do a direct copy of the modification, which defines the protocol buffer extension of HTTP option, to provide support for http conversion of grpc.

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; }

Here in the original SayHelloadds http option defined method, POST way, the route is "/ example / echo".

Compile proto

cd $GOPATH/src/grpc-go-practice/example/hello-http-2/proto

# 编译google.api
protoc -I . --go_out=plugins=grpc,Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:. google/api/*.proto # 编译hello_http.proto protoc -I . --go_out=plugins=grpc,Mgoogle/api/annotations.proto=git.vodjk.com/go-grpc/example/proto/google/api:. ./*.proto # 编译hello_http.proto gateway protoc --grpc-gateway_out=logtostderr=true:. ./hello_http.proto

Note that you need to compile two proto file google / api in, specifying the package name is introduced when compiling hello_http.proto, grpc-gateway using the last compiled hello_http_pb.gw.gofile that is used for protocol conversion, you can see the view file generated inside http handler, routing process defined above "example / echo" POST parameters received calls and services HelloHTTP grpc service client requests and respond to the results.

server/main.go

package main

import (
    "crypto/tls"
    "fmt"
    "io/ioutil" "log" "net" "net/http" "strings" "github.com/grpc-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" ) // 定义helloHttpService并实现约定的接口 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 检查请求协议并返回http handler func 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" // 实例化标准grpc server creds, 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) } // 开启HTTP服务 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 } 

Well, so much cook. The core is to open an http server, after receiving a request to check request is grpc or http, then the decision is directly handled by the service or to the gateway do grpc forwarding process. Which grpcHandlerFuncfunction is responsible for the decision which handler to process the request with this approach is straightforward Copy over with, the original notes say they are elsewhere Copy of. Thank contributors.

The basic process:

  • Examples of standards grpc server

  • The grpc server registered to the gateway

  • Open http service, handler assigned to grpcHandlerFunc method

Note: You must turn on HTTPS

operation result

Start the service:

# hello-http-2/server
go run main.go

> grpc and https on port: 50052 

Call grpc client:

# hello-http-2/client
go 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 hello-http-2, because 1 is not a complete implementation posture, you can not https, but need to turn grpc services and are http services, not explained here.

reference

This series of sample code

Guess you like

Origin www.cnblogs.com/ExMan/p/12163109.html