在网格中使用gRPC
可以方便的定义服务,而当需要对外提供服务时往往HTTP
接口比较普遍,grpc-gateway
提供了RESTful JSON API
转gRPC
的代理服务。
在mesh
中grpc-gateway
的角色更像一个服务聚合层,将内部gRPC
服务聚合并提供HTTP
接口服务,在使用的过程中也遇到些问题,如:进程内路由、Header映射等,这里结合使用经验做个总结。
接口定义
gRPC
的服务定义这里不做过多关联介绍,grpc-gateway
协议支持两种方式添加,一是在.proto
的服务定义中以option
的方式直接绑定;
二是使用单独的.yaml
文件定义。接口定义遵循google.api大家可以参考,
主要内容在HttpRule
,具体字段参考协议http.proto更清晰,
比如:response_body
在文档中并没有提到,用于选择Response
结构的某一字段作为响应内容。
grpc-gateway
文档参考
syntax = "proto3";
package example;
import "google/api/annotations.proto";
message StringMessage {
string value = 1;
}
service YourService {
rpc Echo (StringMessage) returns (StringMessage) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}
type: google.api.Service
config_version: 3
http:
rules:
- selector: example.YourService.Echo
post: /v1/example/echo
body: "*"
进程内路由
grpc-gateway
的代理方式是gRPC
远程调用,在mesh
中有已经有ingress
网关,如果继续用rpc
调用,
将在链路上又增加一层网络开销ingress -> grpc-gatewap -> grpc server
,而我们的主要目的是将gRPC
服务映射成HTTP
并不需要其它能力,
所以我们期望grpc-gateway
能够进程将HTTP
请求转发给gRPC
服务。
针对这一需求需要gRPC
服务支持进程内调用,社区现有的方案是test/bufconn,
示例参考grpc-gateway/pull/947中的讨论。
至于直接进程内调Contributor
的最新回复是8月份启动grpc-go/issues/906。
另一个临时的方案是我提交到grpc-gateway
的PR
pull/947,已经merge
到master
,
但此方案仅支持Unary
,不支持Streaming
,不过对于网关应该能够满足大部分场景的需求。
func RegisterXXXHandlerServer(ctx context.Context, mux *runtime.ServeMux, server XXXServer) error
Header传递
在tracing
等场景我们往往需要将Header
信息在服务之间传递,包括RESTful API
请求。首先grpc-gateway
的NewServeMux()
需要runtime.WithMetadata()
Option,
筛选HTTP Header
映射为metadata
;其次如果是使用test/bufconn方案,
在RegisterXXXHandlerFromEndpoint(...,opts []grpc.DialOption)
opts
的grpc.WithChainUnaryInterceptor()
中需要增加metadata
的映射。
grpc-gateway
的另一个方式是runtime.WithIncomingHeaderMatcher()
为了方便使用可以参考我的metadata
插件github.com/hb-go/grpc-contrib/metadata,
支持指定Header和前缀匹配。
hb-go/grpc-contrib/metadata
示例
mux := runtime.NewServeMux(
runtime.WithMetadata(metadata.GatewayMetadataAnnotator(
metadata.WithHeader("x-request-id"),
metadata.WithPrefix("x-prefix"),
)),
)
err := pb.RegisterXXXHandlerFromEndpoint(ctx, mux, "",
[]grpc.DialOption{
grpc.WithChainUnaryInterceptor(
metadata.UnaryClientInterceptor(
metadata.WithHeader("x-request-id"),
metadata.WithPrefix("x-prefix"),
),
),
},
)
Istio
环境要完整tracing
链路,ClientConn
需要与grpc-gateway
一样,在DialOption
的grpc.WithChainUnaryInterceptor()
中加入metadata.UnaryClientInterceptor(...)
,
Header
说明参考Istio
文档WHAT IS REQUIRED FOR DISTRIBUTED TRACING WITH ISTIO?
metadata.WithHeader("x-request-id"),
metadata.WithHeader("x-b3-traceid"),
metadata.WithHeader("x-b3-spanid"),
metadata.WithHeader("x-b3-parentspanid"),
metadata.WithHeader("x-b3-sampled"),
metadata.WithHeader("x-b3-flags"),
metadata.WithHeader("b3"),
metadata.WithHeader("x-ot-span-context"),
TODO
参数映射
- body
- path
- queue