Envoy gRPC-JSON 转码器功能实现分析

1. 概述

Envoy 的 gRPC-JSON 转码器功能为 HTTP/REST/JSON API 和 gRPC API 之间的双向转换提供了强大而灵活的解决方案。它允许开发人员利用 gRPC 的高性能和类型安全特性,同时保持与现有的 HTTP/JSON 客户端和工具的兼容性。它允许客户端使用熟悉的 JSON API 访问后端的 gRPC 服务,而不需要修改任何后端代码。

该功能的主要特点包括:

  1. 支持所有 gRPC 方法类型(一元、流式等)

  2. 提供了完整的错误处理和状态码转换

  3. 与 Envoy 的架构深度集成,性能优异

  4. 支持灵活的配置选项,包括打印格式、验证选项和路由级配置

  5. 与其他 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;}
相关推荐
夜雪闻竹12 小时前
Cursor 的 state.vscdb 解析踩坑记
json·aigc·ai编程
水煮白菜王12 小时前
JSONEditor 使用指南
前端·javascript·chrome·json
会编程的土豆2 天前
Gin 中 `c.BindJSON` 与 `c.JSON` 详细讲解
c语言·json·gin
ID_180079054732 天前
企业级实战:淘宝商品详情 API简要说明,json数据返回参考
json
学习3人组2 天前
业务主表+JSON自定义字段
java·spring boot·json
Car123 天前
在vscode中添加一个tasks.json实现 rt thread的scons编译功能
vscode·json·build·scons
学习3人组3 天前
基于 主表 + JSON 自定义字段的条件查询
json
XMYX-03 天前
28 - Go JSON 数据操作
开发语言·golang·json
Promise微笑3 天前
Geo专家于磊:Json-LD优化实战SOP与双核四驱体系
大数据·人工智能·重构·json