Skip to main content
Version: 3.17

grpc-transcode

描述#

grpc-transcode 插件在 HTTP 请求与 gRPC 请求及其对应响应之间进行转换。

启用此插件后,APISIX 接收来自客户端的 HTTP 请求,转码后转发给上游 gRPC 服务。当 APISIX 收到 gRPC 响应时,会将其转换回 HTTP 响应并发送给客户端。

属性#

名称类型必选项默认值描述
proto_idstring/integerproto 资源的 ID,包含 Protocol Buffer 定义。
servicestringgRPC 服务名称。
methodstringgRPC 服务的方法名称。
deadlinenumber0gRPC 服务的超时时间,单位为毫秒。即 APISIX 等待 gRPC 调用完成的时间。
pb_optionarray[string(pb_option_def)]["enum_as_name","int64_as_number","auto_default_values","disable_hooks"]编码器和解码器选项
show_status_in_bodybooleanfalse若为 true,则在响应体中展示解析后的 grpc-status-details-bin
status_detail_typestringgrpc-status-details-bindetails 部分对应的消息类型。若未指定,错误消息将不会被解码。

pb_option 的选项#

类型有效值
enum as resultenum_as_name, enum_as_value
int64 as resultint64_as_number, int64_as_string, int64_as_hexstring
default valuesauto_default_values, no_default_values, use_default_values, use_default_metatable
hooksenable_hooks, disable_hooks

示例#

以下示例演示了如何针对不同场景配置 grpc-transcode 插件。

note

您可以这样从 config.yaml 中获取 admin_key 并存入环境变量:

admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"//g')

在跟随示例操作之前,请先启动一个示例 gRPC 服务器

docker run -d \
--name grpc-example-server \
-p 50051:50051 \
api7/grpc-server-example:1.0.2

在 HTTP 和 gRPC 请求之间转换#

以下示例演示了如何在 APISIX 中配置 protobuf,并使用 grpc-transcode 插件在 HTTP 和 gRPC 请求之间进行转换。

创建 proto 资源以存储 protobuf:

curl "http://127.0.0.1:9180/apisix/admin/protos/echo-proto" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"content": "syntax = \"proto3\";
package echo;
service EchoService {
rpc Echo (EchoMsg) returns (EchoMsg);
}
message EchoMsg {
string msg = 1;
}"
}'

创建启用 grpc-transcode 插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"methods": ["GET"],
"uri": "/echo",
"plugins": {
"grpc-transcode": {
"proto_id": "echo-proto",
"service": "echo.EchoService",
"method": "Echo"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
}
}
}'

验证时,使用 EchoMsg 中定义的参数向路由发送 HTTP 请求:

curl "http://127.0.0.1:9080/echo?msg=Hello"

您应该收到以下响应:

{"msg":"Hello"}

使用 .pb 文件配置 Protobuf#

以下示例演示了如何使用 .pb 文件在 APISIX 中配置 protobuf,并使用 grpc-transcode 插件在 HTTP 和 gRPC 请求之间进行转换。

如果您的 proto 文件包含 import,或者想合并多个 proto 文件,可以使用 protoc 工具生成 .pb 文件并在 APISIX 中使用。

将 Protocol Buffer 定义保存到名为 echo.proto 的文件中:

echo.proto
syntax = "proto3";

package echo;

service EchoService {
rpc Echo (EchoMsg) returns (EchoMsg);
}

message EchoMsg {
string msg = 1;
}

使用 protoc 工具生成 .pb 文件:

protoc --include_imports --descriptor_set_out=echo_proto.pb echo.proto

.pb 文件从二进制转换为 base64 并在 APISIX 中配置:

curl "http://127.0.0.1:9180/apisix/admin/protos/echo-proto" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"content" : "'"$(base64 -w0 /path/to/echo_proto.pb)"'"
}'

创建启用 grpc-transcode 插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"methods": ["GET"],
"uri": "/echo",
"plugins": {
"grpc-transcode": {
"proto_id": "echo-proto",
"service": "echo.EchoService",
"method": "Echo"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
}
}
}'

验证时,向路由发送 HTTP 请求:

curl "http://127.0.0.1:9080/echo?msg=Hello"

您应该收到以下响应:

{"msg":"Hello"}

在响应体中显示错误详情#

以下示例演示了如何配置 grpc-transcode 插件,使其在 gRPC 服务器提供 grpc-status-details-bin 字段时,将其包含在响应头中用于错误报告,并将消息解码后展示在响应体中。

创建 proto 资源以存储 protobuf:

curl "http://127.0.0.1:9180/apisix/admin/protos/hello-proto" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"content": "syntax = \"proto3\";
package helloworld;
service Greeter {
rpc GetErrResp (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
repeated string items = 2;
}
message HelloReply {
string message = 1;
repeated string items = 2;
}"
}'

创建启用 grpc-transcode 插件的路由并将 show_status_in_body 设为 true

curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"uri": "/hello",
"plugins": {
"grpc-transcode": {
"proto_id": "hello-proto",
"service": "helloworld.Greeter",
"method": "GetErrResp",
"show_status_in_body": true
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
}
}
}'

向路由发送请求:

curl -i "http://127.0.0.1:9080/hello?name=world"

您应看到类似以下的错误响应:

HTTP/1.1 503 Service Temporarily Unavailable
Date: Wed, 21 Feb 2024 03:08:30 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
grpc-status: 14
grpc-message: Out of service
grpc-status-details-bin: CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2U
Server: APISIX/3.8.0

{"error":{"message":"Out of service","code":14,"details":[{"value":"\b\u0001\u0012\u001cThe server is out of service\u001a\u0007service","type_url":"type.googleapis.com/helloworld.ErrorDetail"}]}}

注意响应中某些信息未被完全解码。

要解码消息,请更新 protobuf 定义以添加 ErrorDetail 消息类型:

curl "http://127.0.0.1:9180/apisix/admin/protos/hello-proto" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"content": "syntax = \"proto3\";
package helloworld;
service Greeter {
rpc GetErrResp (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
repeated string items = 2;
}
message HelloReply {
string message = 1;
repeated string items = 2;
}
message ErrorDetail {
int64 code = 1;
string message = 2;
string type = 3;
}"
}'

更新路由以配置 status_detail_type

curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"uri": "/hello",
"plugins": {
"grpc-transcode": {
"proto_id": "hello-proto",
"service": "helloworld.Greeter",
"method": "GetErrResp",
"show_status_in_body": true,
"status_detail_type": "helloworld.ErrorDetail"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
}
}
}'

再次向路由发送请求:

curl -i "http://127.0.0.1:9080/hello?name=world"

您应看到错误消息已完全解码的响应:

HTTP/1.1 503 Service Temporarily Unavailable
Date: Wed, 21 Feb 2024 03:11:43 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
grpc-status: 14
grpc-message: Out of service
grpc-status-details-bin: CA4SDk91dCBvZiBzZXJ2aWNlGlcKKnR5cGUuZ29vZ2xlYXBpcy5jb20vaGVsbG93b3JsZC5FcnJvckRldGFpbBIpCAESHFRoZSBzZXJ2ZXIgaXMgb3V0IG9mIHNlcnZpY2UaB3NlcnZpY2U
Server: APISIX/3.8.0

{"error":{"message":"Out of service","code":14,"details":[{"message":"The server is out of service","code":1,"type":"service"}]}}

配置编码器/解码器选项#

以下示例演示了如何为 grpc-transcode 插件配置编码器和解码器选项。具体来说,您将对执行加法运算的方法应用 int64_as_string 选项,以观察其效果。

创建 proto 资源以存储 protobuf:

curl "http://127.0.0.1:9180/apisix/admin/protos/plus-proto" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"content": "syntax = \"proto3\";
package helloworld;
service Greeter {
rpc Plus (PlusRequest) returns (PlusReply) {}
}
message PlusRequest {
int64 a = 1;
int64 b = 2;
}
message PlusReply {
int64 result = 1;
}"
}'

创建启用 grpc-transcode 插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"uri": "/plus",
"plugins": {
"grpc-transcode": {
"proto_id": "plus-proto",
"service": "helloworld.Greeter",
"method": "Plus"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
}
}
}'

向路由发送请求:

curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491"

您应看到显示两数之和的响应:

{"result":2.475056748395e+15}

注意当结果以数字形式返回时会损失精度。更新路由以使用 int64_as_string 选项:

curl "http://127.0.0.1:9180/apisix/admin/routes/grpc-transcode-route" -H "X-API-KEY: $admin_key" -X PUT -d '
{
"uri": "/plus",
"plugins": {
"grpc-transcode": {
"proto_id": "plus-proto",
"service": "helloworld.Greeter",
"method": "Plus",
"pb_option":["int64_as_string"]
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"127.0.0.1:50051": 1
}
}
}'

再次向路由发送请求:

curl "http://127.0.0.1:9080/plus?a=1237528374197491&b=1237528374197491"

您应看到精度完整的两数之和:

{"result":"#2475056748394982"}