探秘高效通信:grpc-http-proxy
:警告:这不是生产就绪的版本
grpc-http-proxy
是一个反向代理,它能够将JSON HTTP请求无缝转换为gRPC调用,几乎无需配置。特别设计用于Kubernetes集群中运行,它利用Kubernetes API通过自定义注解找到提供所需gRPC服务的内部服务器。
背景
尽管已存在如gRPC-gateway这样的解决方案,可以生成将JSON HTTP请求转化为gRPC调用的反向代理,但它们通常需要以下操作:
- 手动在gRPC服务定义中添加自定义注解以定义HTTP端点到gRPC方法的映射。
- 针对每个gRPC服务生成单独的反向代理。
随着gRPC服务规模的增长和更多服务的创建,这变得难以管理。
grpc-http-proxy
的目标是成为一个单一的反向代理,适用于所有gRPC服务,并避免繁琐的手动映射。这使得可以通过HTTP请求轻松访问gRPC服务。
工作原理
向 grpc-http-proxy
端点 /v1/<service>/<method>
发送请求,代理将会调用 <service>
中的 <method>
方法。
只要知道服务名称和服务方法,gRPC调用就可以按以下步骤进行:
- 使用gRPC服务器反射协议获取gRPC服务支持的方法以及消息格式的信息。
- 根据获取的信息,按照Proto3到JSON映射规范,将JSON请求体转换为Protobuf消息。
- 向上游服务发起gRPC调用。
- 将响应转换为JSON并返回给调用者。
如果HTTP请求头中的键以 Grpc-Metadata-
开头,则会将元数据传递给上游。
此外,grpc-http-proxy
可以通过设置访问令牌进行配置。如果有指定的访问令牌,只有包含在 X-Access-Token
头中的请求才会被处理。
通过自定义注解,在Kubernetes服务资源中定义gRPC服务名与上游Kubernetes服务之间的映射。监听Kubernetes API并保持这些映射的更新。
安装
使用Helm部署到Kubernetes集群:
$ git clone https://github.com/mercari/grpc-http-proxy && cd ./grpc-http-proxy
$ helm install --name grpc-http-proxy helm/grpc-http-proxy --namespace kube-system
默认在 kube-system
命名空间中无访问令牌部署。若需设置,可执行以下命令:
$ helm install --set accessToken SUPER_SECRET --name grpc-http-proxy helm/grpc-http-proxy --namespace kube-system
也有 values.yaml
文件供更详细的配置。
注意:目前Helm图表不支持RBAC,所以默认的pod ServiceAccount应该有权访问集群内的所有服务。
配置
安装后,需要一些配置让grpc-http-proxy
找到你的服务。
启用gRPC反射
在你的gRPC服务器上启用服务器反射,请遵循这里的说明。
集群侧Kubernetes API服务发现设置
服务发现依赖于具有特定注解的Kubernetes服务。为了使grpc-http-proxy
找到你的gRPC服务器,做以下操作:
1. 添加 grpc-service
注解
在Service上放置 grpc-http-proxy.alpha.mercari.com/grpc-service
注解。如果你的gRPC服务的全限定名为 my.package.MyService
,则添加注解 grpc-http-proxy.alpha.mercari.com/grpc-service: my.package.MyService
。
kind: Service
apiVersion: v1
metadata:
name: my-service
annotations:
+ grpc-http-proxy.alpha.mercari.com/grpc-service: my.package.MyService
如果你的gRPC服务器导出多个服务,可以用逗号(,)分隔列出它们。
kind: Service
apiVersion: v1
metadata:
name: my-service
annotation:
+ grpc-http-proxy.alpha.mercari.com/grpc-service: my.package.MyService,my.anotherpackage.OtherService
2. 以 'grpc' 开头命名端口
grpc-http-proxy
将向名字以 'grpc' 开头的端口发送请求。如果有多个匹配的端口,会选择第一个。
如果打算使用的端口是Kubernetes Service暴露的唯一端口,这一步可以跳过。
kind: Service
apiVersion: v1
metadata:
name: my-service
annotations:
grpc-http-proxy.alpha.mercari.com/grpc-service: my.package.MyService
spec:
ports:
- - name: foo
+ - name: grpc-foo
port: 5000
protocol: TCP
targetPort: 5000
3. [可选] 添加 grpc-service-version
注解
如果你打算通过grpc-http-proxy
调用多个版本的gRPC服务器,那么在Service上添加 grpc-http-proxy.alpha.mercari.com/grpc-service-version
注解。版本可以是你喜欢的任何字符串。
annotations:
+ grpc-http-proxy.alpha.mercari.com/grpc-service-version: pr-42
示例
以下示例中,grpc-http-proxy
运行在 grpc-http-proxy.example.com
,并且访问令牌设置为 foo
。gRPC服务 Echo
如下.proto文件定义:
syntax = "proto3";
package com.example;
service Echo {
rpc Say(EchoMessage) returns (EchoMessage) {};
}
message EchoMessage {
string message_body = 1;
}
单一版本服务
Kubernetes Service清单如下:
kind: Service
apiVersion: v1
metadata:
name: echo-service
annotations:
grpc-http-proxy.alpha.mercari.com/grpc-service: com.example.Echo
spec:
ports:
- name: grpc-echo
port: 5000
protocol: TCP
targetPort: 5000
通过grpc-http-proxy
调用Say
及其响应如下:
$ curl -H'X-Access-Token: foo' -XPOST -d'{"message_body":"Hello, World!"}' grpc-http-proxy.example.com/v1/com.example.Echo/Say
{"message_body":"Hello, World!"}
多个版本的服务
假设你有同一个集群中的新版本 Echo
服务器,你想调用这个新版本。新服务器Service的清单应指定版本注解,如下所示:
kind: Service
apiVersion: v1
metadata:
name: newer-echo-service
annotations:
grpc-http-proxy.alpha.mercari.com/grpc-service: com.example.Echo
grpc-http-proxy.alpha.mercari.com/grpc-service-version: newer-version
spec:
ports:
- name: grpc-echo
port: 5000
protocol: TCP
targetPort: 5000
选择新版本,只需在查询参数中添加注解中指定的版本名。调用新版本的 Say
和其响应如下:
$ curl -H'X-Access-Token: foo' -XPOST -d'{"message_body":"Hello, World!"}' grpc-http-proxy.example.com/v1/com.example.Echo/Say?version=newer-version
{"message_body":"Hello, World!"}
传递元数据到gRPC服务
要在调用 Echo
服务时传递键为 somekey
的元数据,像下面这样添加到HTTP请求中:
$ curl -H'X-Access-Token: foo' -H'Grpc-Metadata-somekey: value' -XPOST -d'{"message_body":"Hello, World!"}' grpc-http-proxy.example.com/v1/com.example.Echo/Say
{"message_body":"Hello, World!"}
待办事项
尚未实现的一些可能期望的功能包括:
- 通过静态配置文件查找服务的能力。
欢迎贡献!
致谢
Tomoya TABUCHI (@tomoyat1) 是主要的开发者。
贡献指南
在提交您的贡献之前,请仔细阅读CLA。
https://www.mercari.com/cla/
许可证
版权2018年 Mercari, Inc.
授权MIT许可证。