title: grpc-transcode keywords:
grpc-transcode 插件在 HTTP 请求与 gRPC 请求及其对应响应之间进行转换。
启用此插件后,APISIX 接收来自客户端的 HTTP 请求,转码后转发给上游 gRPC 服务。当 APISIX 收到 gRPC 响应时,会将其转换回 HTTP 响应并发送给客户端。
| 名称 | 类型 | 必选项 | 默认值 | 描述 |
|---|---|---|---|---|
| proto_id | string/integer | 是 | proto 资源的 ID,包含 Protocol Buffer 定义。 | |
| service | string | 是 | gRPC 服务名称。 | |
| method | string | 是 | gRPC 服务的方法名称。 | |
| deadline | number | 否 | 0 | gRPC 服务的超时时间,单位为毫秒。即 APISIX 等待 gRPC 调用完成的时间。 |
| pb_option | array[string(pb_option_def)] | 否 | ["enum_as_name","int64_as_number","auto_default_values","disable_hooks"] | 编码器和解码器选项。 |
| show_status_in_body | boolean | 否 | false | 若为 true,则在响应体中展示解析后的 grpc-status-details-bin。 |
| status_detail_type | string | 否 | grpc-status-details-bin 中 details 部分对应的消息类型。若未指定,错误消息将不会被解码。 |
| 类型 | 有效值 |
|---|---|
| enum as result | enum_as_name, enum_as_value |
| int64 as result | int64_as_number, int64_as_string, int64_as_hexstring |
| default values | auto_default_values, no_default_values, use_default_values, use_default_metatable |
| hooks | enable_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
以下示例演示了如何在 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 文件在 APISIX 中配置 protobuf,并使用 grpc-transcode 插件在 HTTP 和 gRPC 请求之间进行转换。
如果您的 proto 文件包含 import,或者想合并多个 proto 文件,可以使用 protoc 工具生成 .pb 文件并在 APISIX 中使用。
将 Protocol Buffer 定义保存到名为 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"}