1. 概述
Envoy 的 gRPC-JSON 转码器功能为 HTTP/REST/JSON API 和 gRPC API 之间的双向转换提供了强大而灵活的解决方案。它允许开发人员利用 gRPC 的高性能和类型安全特性,同时保持与现有的 HTTP/JSON 客户端和工具的兼容性。它允许客户端使用熟悉的 JSON API 访问后端的 gRPC 服务,而不需要修改任何后端代码。
该功能的主要特点包括:
-
支持所有 gRPC 方法类型(一元、流式等)
-
提供了完整的错误处理和状态码转换
-
与 Envoy 的架构深度集成,性能优异
-
支持灵活的配置选项,包括打印格式、验证选项和路由级配置
-
与其他 Envoy 功能配合良好,如认证、路由和限流
通过合理配置 gRPC-JSON 转码器,您可以快速将现有的 gRPC 服务暴露为 RESTful API,或者将 HTTP/JSON 客户端与 gRPC 服务集成,提高了开发效率和系统的可维护性。
2. 解决的问题
gRPC-JSON 转码器功能主要解决以下问题:
2.1 API 兼容性
-
允许客户端使用 HTTP/JSON API 访问 gRPC 服务,无需修改后端代码
-
支持将 gRPC 服务自动转换为 RESTful API
-
提供了与现有 HTTP 客户端和工具的兼容性
2.2 开发效率
-
简化了前端开发,允许使用标准的 JSON API
-
减少了编写和维护 HTTP 代理层的需求
-
提供了自动的 API 文档生成支持
2.3 性能优化
-
直接在 Envoy 层面进行转换,避免了额外的网络开销
-
支持流式处理和双向通信
-
提供了高效的序列化和反序列化机制
2.4 功能完整性
-
支持所有 gRPC 方法类型,包括一元、服务器流式、客户端流式和双向流式
-
自动处理 HTTP 头部和 gRPC 元数据之间的转换
-
提供了错误处理和状态码转换
3. 架构设计
3.1 核心组件架构

3.2 类图

3. 配置用例
3.1 基础配置
bash
http_filters:- name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"] print_options: add_whitespace: true always_print_enums_as_ints: false always_print_primitive_fields: true connect_timeout: 0.25s lb_policy: ROUND_ROBIN
3.2 高级配置
bash
http_filters:- name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"] print_options: add_whitespace: true always_print_enums_as_ints: false always_print_primitive_fields: true match_incoming_request_route: true convert_grpc_status: true ignore_unknown_query_parameters: true request_validation_options: reject_unknown_fields: true reject_unknown_query_parameters: true
3.3 路由级配置
bash
route_config: name: local_route virtual_hosts: - name: backend domains: ["*"] routes: - match: prefix: "/api" route: cluster: grpc_backend per_filter_config: envoy.filters.http.grpc_json_transcoder: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"] print_options: add_whitespace: true
3.4 复杂配置
bash
static_resources: listeners: - name: listener_0 address: socket_address: address: 0.0.0.0 port_value: 8080 filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http http_filters: - name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"] print_options: add_whitespace: true always_print_enums_as_ints: false always_print_primitive_fields: true - name: envoy.filters.http.router route_config: name: local_route virtual_hosts: - name: backend domains: ["*"] routes: - match: { prefix: "/" } route: { cluster: grpc_backend } clusters: - name: grpc_backend connect_timeout: 0.25s type: STRICT_DNS lb_policy: ROUND_ROBIN load_assignment: cluster_name: grpc_backend endpoints: - lb_endpoints: - endpoint: address: socket_address: address: grpc-backend.example.com port_value: 50051
4. 工作流程分析
4.1 过滤器执行流程

4.2 请求转换流程

5. 代码实现ER图

6. 最佳实践
6.1 性能优化
bash
http_filters:- name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"] print_options: add_whitespace: false always_print_enums_as_ints: true always_print_primitive_fields: true
6.2 安全性配置
bash
http_filters:- name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"] request_validation_options: reject_unknown_fields: true reject_unknown_query_parameters: true
6.3 与其他过滤器配合使用
bash
http_filters:- name: envoy.filters.http.jwt_authn typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication providers: # 认证配置- name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service.Name"]- name: envoy.filters.http.router
6.4 多服务配置
bash
http_filters:- name: envoy.filters.http.grpc_json_transcoder typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder proto_descriptor: "/path/to/your/proto_descriptor_set.pb" services: ["your.service1.Name", "your.service2.Name"]
7. 代码实现细节
7.1 过滤器初始化
css
JsonTranscoderFilter::JsonTranscoderFilter(JsonTranscoderConfig& config) : config_(config), transcoder_(nullptr), method_(nullptr) {}
7.2 转码器创建
php
void JsonTranscoderFilter::initPerRouteConfig() { const auto* route_config = Http::Utility::resolveMostSpecificPerFilterConfig<JsonTranscoderConfig>( *decoder_callbacks_);
if (route_config != nullptr) { per_route_config_ = route_config; } else { per_route_config_ = &config_; }}
Http::FilterHeadersStatus JsonTranscoderFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool) { initPerRouteConfig(); if (per_route_config_->disabled()) { return Http::FilterHeadersStatus::Continue; }
auto status = per_route_config_->createTranscoder( headers, request_in_, response_in_, transcoder_, method_);
if (!status.ok()) { if (status.error_code() == ProtobufUtil::StatusCode::NOT_FOUND) { return Http::FilterHeadersStatus::Continue; }
return checkAndRejectIfRequestTranscoderFailed(status.ToString()); }
// 配置转码器 const auto& request_info = headers; if (transcoder_ != nullptr) { // 初始化请求转换 }
return Http::FilterHeadersStatus::Continue;}
7.3 请求处理
php
Http::FilterDataStatus JsonTranscoderFilter::decodeData(Buffer::Instance& data, bool end_stream) { if (per_route_config_->disabled() || transcoder_ == nullptr) { return Http::FilterDataStatus::Continue; }
if (!first_request_sent_) { // 处理初始请求数据 first_request_sent_ = true; maybeSendHttpBodyRequestMessage(); }
// 处理请求数据 request_data_.add(data); if (decoderBufferLimitReached(request_data_.length())) { return Http::FilterDataStatus::StopIterationNoBuffer; }
if (end_stream) { // 处理请求结束 }
return Http::FilterDataStatus::Continue;}
7.4 响应处理
php
Http::FilterDataStatus JsonTranscoderFilter::encodeData(Buffer::Instance& data, bool end_stream) { if (per_route_config_->disabled() || transcoder_ == nullptr) { return Http::FilterDataStatus::Continue; }
response_in_.add(data);
if (hasHttpBodyAsOutputType()) { if (!http_body_response_headers_set_) { response_headers_->setReferenceContentType(content_type_); http_body_response_headers_set_ = true; encoder_callbacks_->encodeHeaders(*response_headers_, false); }
if (buildResponseFromHttpBodyOutput(*response_headers_, data)) { if (end_stream) { // 发送响应结束 } return Http::FilterDataStatus::Continue; } }
return Http::FilterDataStatus::StopIterationAndBuffer;}