怎么看得出网站是哪个公司做的,字幕如何做模板下载网站,短视频seo排名系统,华为荣耀商城官网旗舰店1、gRPC Gateway
etcd3 API全面升级为gRPC后#xff0c;同时要提供REST API服务#xff0c;维护两个版本的服务显然不太合理#xff0c;所以
grpc-gateway 诞生了。通过protobuf的自定义option实现了一个网关#xff0c;服务端同时开启gRPC和HTTP服务#xff0c;
HTTP服…1、gRPC Gateway
etcd3 API全面升级为gRPC后同时要提供REST API服务维护两个版本的服务显然不太合理所以
grpc-gateway 诞生了。通过protobuf的自定义option实现了一个网关服务端同时开启gRPC和HTTP服务
HTTP服务接收客户端请求后转换为grpc请求数据获取响应后转为json数据返回给客户端。结构如图 grpc-gateway地址https://github.com/grpc-ecosystem/grpc-gateway
1.1 安装grpc-gateway
$ go get github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gatewayv1.16.0
$ go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gatewayv1.16.01.2 proto编写
这里用到了google官方Api中的两个proto描述文件直接拷贝不要做修改里面定义了protocol buffer扩展的
HTTP option为grpc的http转换提供支持。
annotations.proto文件的内容
// ./proto/google/api/annotations.proto
syntax proto3;package google.api;option go_package google/api;google_api;import google/api/http.proto;
import google/protobuf/descriptor.proto;option java_multiple_files true;
option java_outer_classname AnnotationsProto;
option java_package com.google.api;extend google.protobuf.MethodOptions {HttpRule http 72295728;}http.proto文件的内容
// ./proto/google/api/http.proto
syntax proto3;package google.api;option go_package google/api;google_api;option cc_enable_arenas true;
option java_multiple_files true;
option java_outer_classname HttpProto;
option java_package com.google.api;message Http {repeated HttpRule rules 1;
}message HttpRule {string selector 1;oneof pattern {string get 2;string put 3;string post 4;string delete 5;string patch 6;CustomHttpPattern custom 8;}string body 7;repeated HttpRule additional_bindings 11;
}message CustomHttpPattern {string kind 1;string path 2;
}编写自定义的proto描述文件hello_http.proto
// ./proto/hello_http/hello_http.proto
syntax proto3;
package hello_http;
option go_package ./hello_http;hello_http;import google/api/annotations.proto;// 定义Hello服务
service HelloHTTP {// 定义SayHello方法rpc SayHello(HelloHTTPRequest) returns (HelloHTTPResponse) {// http optionoption (google.api.http) {post: /example/echobody: *};}
}// HelloRequest 请求结构
message HelloHTTPRequest {string name 1;
}// HelloResponse 响应结构
message HelloHTTPResponse {string message 1;
}这里在原来的SayHello方法定义中增加了http optionPOST方式路由为/example/echo。
1.3 编译proto
$ cd proto# 编译google.api
$ protoc -I . --go_outpluginsgrpc:. google/api/*.proto# 编译hello_http.proto
$ protoc -I . --go_outpluginsgrpc:. hello_http/*.proto# 编译hello_http.proto gateway
$ protoc --grpc-gateway_outlogtostderrtrue:. hello_http/hello_http.proto注意这里需要编译google/api中的两个proto文件最后使用grpc-gateway编译生成hello_http_pb.gw.go文
件这个文件就是用来做协议转换的查看文件可以看到里面生成的http handler处理proto文件中定义的路由
example/echo接收POST参数调用HelloHTTP服务的客户端请求grpc服务并响应结果。
1.4 实现服务端
server.go的内容
package mainimport (contextfmtnet// 引入编译生成的包pb demo/proto/hello_httpgoogle.golang.org/grpclog
)const (// Address gRPC服务地址Address 127.0.0.1:50053
)// 定义helloService并实现约定的接口
type helloService struct{}// HelloService Hello服务
var HelloService helloService{}// SayHello 实现Hello服务接口
func (h helloService) SayHello(ctx context.Context, in *pb.HelloHTTPRequest) (*pb.HelloHTTPResponse, error) {resp : new(pb.HelloHTTPResponse)resp.Message fmt.Sprintf(Hello %s., in.Name)return resp, nil
}func main() {listen, err : net.Listen(tcp, Address)if err ! nil {log.Fatalf(Failed to listen: %v, err)}// 实例化grpc Servers : grpc.NewServer()// 注册HelloServicepb.RegisterHelloHTTPServer(s, HelloService)log.Println(Listen on Address)s.Serve(listen)
}1.5 客户端实现
client.go的内容
package mainimport (contextpb demo/proto/hello_httpgoogle.golang.org/grpclog
)const (// Address gRPC服务地址Address 127.0.0.1:50053
)func main() {// 连接conn, err : grpc.Dial(Address, grpc.WithInsecure())if err ! nil {log.Fatalln(err)}defer conn.Close()// 初始化客户端c : pb.NewHelloHTTPClient(conn)// 调用方法req : pb.HelloHTTPRequest{Name: gRPC}res, err : c.SayHello(context.Background(), req)if err ! nil {log.Fatalln(err)}log.Println(res.Message)
}1.6 http server
server_http.go的内容
package mainimport (fmtgithub.com/grpc-ecosystem/grpc-gateway/runtimegw demo/proto/hello_httpgolang.org/x/net/contextgoogle.golang.org/grpclognet/http
)func main() {ctx : context.Background()ctx, cancel : context.WithCancel(ctx)defer cancel()// grpc服务地址endpoint : 127.0.0.1:50053mux : runtime.NewServeMux()opts : []grpc.DialOption{grpc.WithInsecure()}// HTTP转grpcerr : gw.RegisterHelloHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts)if err ! nil {log.Fatalf(Register handler err:%v\n, err)}log.Println(HTTP Listen on 8080)http.ListenAndServe(:8080, mux)
}就是这么简单。开启了一个http server收到请求后根据路由转发请求到对应的RPC接口获得结果。grpc-
gateway做的事情就是帮我们自动生成了转换过程的实现。
1.7 测试
依次开启gRPC服务和HTTP服务端
[rootzsx demo]# go run server.go
2023/02/12 09:38:52 Listen on 127.0.0.1:50053
[rootzsx demo]# go run server_http.go
2023/02/12 09:39:07 HTTP Listen on 8080调用grpc客户端
[rootzsx demo]# go run client.go
2023/02/12 09:39:37 Hello gRPC.# 发送 HTTP 请求
[rootzsx demo]# curl -X POST -k http://localhost:8080/example/echo -d {\name\:\gRPC-HTTP\}
{message:Hello gRPC-HTTP.}# 项目结构
$ tree demo
demo
├── client.go
├── go.mod
├── go.sum
├── proto
│ ├── google # googleApi http-proto定义
│ │ └── api
│ │ ├── annotations.pb.go
│ │ ├── annotations.proto
│ │ ├── http.pb.go
│ │ └── http.proto
│ └── hello_http
│ ├── hello_http.pb.go
│ ├── hello_http.pb.gw.go # gateway编译后文件
│ └── hello_http.proto
├── server.go # gRPC服务端
└── server_http.go # HTTP服务端4 directories, 12 files1.8 升级版服务端(gRPC转换HTTP)
上面的使用方式已经实现了我们最初的需求grpc-gateway 项目中提供的示例也是这种使用方式这样后台需
要开启两个服务两个端口。其实我们也可以只开启一个服务同时提供http和gRPC调用方式。
新建一个项目基于上面的项目改造客户端只要修改调用的proto包地址就可以了。
1.8.1 服务端实现
server.go的内容
package mainimport (crypto/tlsgithub.com/grpc-ecosystem/grpc-gateway/runtimepb demo/proto/hello_httpgolang.org/x/net/contextgolang.org/x/net/http2google.golang.org/grpcgoogle.golang.org/grpc/credentialslogio/ioutilnetnet/httpstrings
)// 定义helloHTTPService并实现约定的接口
type helloHTTPService struct{}// HelloHTTPService Hello HTTP服务
var HelloHTTPService helloHTTPService{}// SayHello 实现Hello服务接口
func (h helloHTTPService) SayHello(ctx context.Context, in *pb.HelloHTTPRequest) (*pb.HelloHTTPResponse, error) {resp : new(pb.HelloHTTPResponse)resp.Message Hello in.Name .return resp, nil
}func main() {endpoint : 127.0.0.1:50052conn, err : net.Listen(tcp, endpoint)if err ! nil {log.Fatalf(TCP Listen err:%v\n, err)}// grpc tls servercreds, err : credentials.NewServerTLSFromFile(./cert/server/server.pem, ./cert/server/server.key)if err ! nil {log.Fatalf(Failed to create server TLS credentials %v, err)}grpcServer : grpc.NewServer(grpc.Creds(creds))pb.RegisterHelloHTTPServer(grpcServer, HelloHTTPService)// gw serverctx : context.Background()dcreds, err : credentials.NewClientTLSFromFile(./cert/server/server.pem, test.example.com)if err ! nil {log.Fatalf(Failed to create client TLS credentials %v, err)}dopts : []grpc.DialOption{grpc.WithTransportCredentials(dcreds)}gwmux : runtime.NewServeMux()if err pb.RegisterHelloHTTPHandlerFromEndpoint(ctx, gwmux, endpoint, dopts); err ! nil {log.Fatalf(Failed to register gw server: %v\n, err)}// http服务mux : http.NewServeMux()mux.Handle(/, gwmux)srv : http.Server{Addr: endpoint,Handler: grpcHandlerFunc(grpcServer, mux),TLSConfig: getTLSConfig(),}log.Printf(gRPC and https listen on: %s\n, endpoint)if err srv.Serve(tls.NewListener(conn, srv.TLSConfig)); err ! nil {log.Fatal(ListenAndServe: , err)}return
}func getTLSConfig() *tls.Config {cert, _ : ioutil.ReadFile(./cert/server/server.pem)key, _ : ioutil.ReadFile(./cert/server/server.key)var demoKeyPair *tls.Certificatepair, err : tls.X509KeyPair(cert, key)if err ! nil {log.Fatalf(TLS KeyPair err: %v\n, err)}demoKeyPair pairreturn tls.Config{Certificates: []tls.Certificate{*demoKeyPair},NextProtos: []string{http2.NextProtoTLS}, // HTTP2 TLS支持}
}// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise. Copied from cockroachdb.
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {if otherHandler nil {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {grpcServer.ServeHTTP(w, r)})}return 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)}})
}gRPC服务端接口的实现没有区别重点在于HTTP服务的实现。gRPC是基于http2实现的net/http包也实现了
http2所以我们可以开启一个HTTP服务同时服务两个版本的协议在注册http handler的时候在方法
grpcHandlerFunc中检测请求头信息决定是直接调用gRPC服务还是使用gateway的HTTP服务。
net/http中对http2的支持要求开启https所以这里要求使用https服务。
步骤
注册开启TLS的grpc服务注册开启TLS的gateway服务地址指向grpc服务开启HTTP server
1.8.2 客户端实现
client.go的内容
package mainimport (context// 引入proto包pb demo/proto/hello_httpgoogle.golang.org/grpc// 引入grpc认证包google.golang.org/grpc/credentialslog
)const (// Address gRPC服务地址Address 127.0.0.1:50052
)func main() {log.Println(客户端连接!)// TLS连接creds, err : credentials.NewClientTLSFromFile(./cert/server/server.pem, test.example.com)if err ! nil {log.Fatalf(Failed to create TLS credentials %v, err)}conn, err : grpc.Dial(Address, grpc.WithTransportCredentials(creds))if err ! nil {log.Fatalln(err:, err)}defer conn.Close()// 初始化客户端c : pb.NewHelloHTTPClient(conn)// 调用方法req : pb.HelloHTTPRequest{Name: gRPC}res, err : c.SayHello(context.Background(), req)if err ! nil {log.Fatalln(err)}log.Println(res.Message)
}1.8.3 测试
[rootzsx demo]# go run server.go
2023/02/12 09:57:44 gRPC and https listen on: 127.0.0.1:50052[rootzsx demo]# go run client.go
2023/02/12 09:59:46 客户端连接!
2023/02/12 09:59:46 Hello gRPC.# 发送 HTTP 请求
[rootzsx demo]# curl -X POST -k https://localhost:50052/example/echo -d {\name\:\gRPC-HTTP\}
{message:Hello gRPC-HTTP.}# 项目结构
$ tree demo/
demo/
├── cert
│ ├── ca.crt
│ ├── ca.csr
│ ├── ca.key
│ ├── ca.srl
│ ├── client
│ │ ├── client.csr
│ │ ├── client.key
│ │ └── client.pem
│ ├── openssl.cnf
│ └── server
│ ├── server.csr
│ ├── server.key
│ └── server.pem
├── client.go
├── go.mod
├── go.sum
├── proto
│ ├── google
│ │ └── api
│ │ ├── annotations.pb.go
│ │ ├── annotations.proto
│ │ ├── http.pb.go
│ │ └── http.proto
│ └── hello_http
│ ├── hello_http.pb.go
│ ├── hello_http.pb.gw.go
│ └── hello_http.proto
└── server.go