跳到主要内容

grpc-transcode

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

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

示例

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

要跟随示例操作,请在 Docker 中启动一个示例 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" -X PUT -d '
{
"id": "echo-proto",
"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" -X PUT -d '
{
"id": "grpc-transcode-route",
"methods": ["GET"],
"uri": "/echo",
"plugins": {
"grpc-transcode": {
// Annotate 1
"proto_id": "echo-proto",
// Annotate 2
"service": "echo.EchoService",
// Annotate 3
"method": "Echo"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"grpc-example-server:50051": 1
}
}
}'

proto_id:定义 gRPC 服务的 proto 对象的 ID

service:要交互的 gRPC 服务

method:要使用的 gRPC 方法

为了验证,向路由发送一个带有 EchoMsg 中定义的参数的 HTTP 请求:

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

你应该收到以下响应:

{"msg":"Hello"}

使用 .pb 文件配置 Protobuf

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

如果你的 proto 文件包含导入,或者你想要组合多个 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 文件,并将其输出到名为 echo_proto.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" -X PUT -d '
{
"id": "echo-proto",
"content" : "'"$(base64 -w0 /path/to/echo_proto.pb)"'"
}'

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

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

为了验证,向路由发送一个带有 EchoMsg 中定义的参数的 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" -X PUT -d '
{
"id": "hello-proto",
"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" -X PUT -d '
{
"id": "grpc-transcode-route",
"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": {
"grpc-example-server: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 定义:

curl "http://127.0.0.1:9180/apisix/admin/protos" -X PUT -d '
{
"id": "hello-proto",
"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;
}"
}'

如下配置启用 grpc-transcode 插件的路由:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "grpc-transcode-route",
"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": {
"grpc-example-server: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" -X PUT -d '
{
"id": "plus-proto",
"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" -X PUT -d '
{
"id": "grpc-transcode-route",
"uri": "/plus",
"plugins": {
"grpc-transcode": {
"proto_id": "plus-proto",
"service": "helloworld.Greeter",
"method": "Plus"
}
},
"upstream": {
"scheme": "grpc",
"type": "roundrobin",
"nodes": {
"grpc-example-server: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" -X PUT -d '
{
"id": "grpc-transcode-route",
"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": {
"grpc-example-server:50051": 1
}
}
}'

发送另一个请求到路由:

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

你应该看到一个响应,显示两个数字的总和,且精度更高:

{"result":"#2475056748394982"}