Envoy gRPC HTTP/1.1 桥接功能实现分析

1. 概述

Envoy 的 gRPC HTTP/1.1 桥接功能为 gRPC 和 HTTP/1.1 协议之间的双向转换提供了强大而灵活的解决方案。它允许使用标准的 HTTP/1.1 协议访问 gRPC 服务,同时保持了 API 的完整性和性能。

该功能的主要特点包括:

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

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

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

  4. 支持灵活的配置选项,包括路由级配置

  5. 与其他 Envoy 功能配合良好,如认证、限流和路由

通过合理配置 gRPC HTTP/1.1 桥接过滤器,您可以快速将现有的 gRPC 服务暴露为 HTTP/1.1 API,或者将 HTTP/1.1 客户端与 gRPC 服务集成,提高了开发效率和系统的可维护性。

2. 解决的问题

gRPC HTTP/1.1 桥接功能主要解决以下问题:

2.1 API 兼容性

  • 允许使用 HTTP/1.1 协议访问 gRPC 服务,无需修改后端代码

  • 支持将 gRPC 请求转换为 HTTP/1.1 请求,反之亦然

  • 提供了与现有 HTTP 客户端和工具的兼容性

2.2 开发效率

  • 简化了系统集成,允许使用标准的 HTTP/1.1 客户端访问 gRPC 服务

  • 减少了编写和维护 gRPC 到 HTTP/1.1 适配器的需求

  • 提供了对 Protobuf 格式请求的自动处理

2.3 性能优化

  • 直接在 Envoy 层面进行协议转换,避免了额外的网络开销

  • 支持流式处理和双向通信

  • 提供了高效的序列化和反序列化机制

2.4 功能完整性

  • 支持所有 gRPC 方法类型,包括一元、服务器流式、客户端流式和双向流式

  • 自动处理 gRPC 状态码和消息的转换

  • 提供了与其他 Envoy 功能的集成支持

3. 架构设计

3.1 核心组件架构

3.2 类图

4. 配置用例

4.1 基础配置

css 复制代码
http_filters:- name: envoy.filters.http.grpc_http1_bridge  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config    upgrade_protobuf_to_grpc: true
route_config:  name: local_route  virtual_hosts:  - name: backend    domains: ["*"]    routes:    - match: { prefix: "/" }      route: { cluster: grpc_backend }
static_resources:  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.2 高级配置

bash 复制代码
http_filters:- name: envoy.filters.http.grpc_http1_bridge  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config    upgrade_protobuf_to_grpc: true
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_http1_bridge            typed_config:              "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config              upgrade_protobuf_to_grpc: 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.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_http1_bridge:            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config            upgrade_protobuf_to_grpc: true

4.4 反向桥接配置

css 复制代码
http_filters:- name: envoy.filters.http.grpc_http1_reverse_bridge  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.Config    upstream_content_type: "application/json"    withhold_grpc_frames: true    response_size_header: "Content-Length"
route_config:  name: local_route  virtual_hosts:  - name: backend    domains: ["*"]    routes:    - match: { prefix: "/" }      route: { cluster: http_backend }

5. 工作流程分析

5.1 过滤器执行流程

5.2 请求转换流程

6. 代码实现ER图

7. 最佳实践

7.1 与其他过滤器配合使用

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_http1_bridge  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config    upgrade_protobuf_to_grpc: true- name: envoy.filters.http.router

7.2 性能优化

bash 复制代码
http_filters:- name: envoy.filters.http.grpc_http1_bridge  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config    upgrade_protobuf_to_grpc: true    # 其他性能优化配置

7.3 错误处理

bash 复制代码
http_filters:- name: envoy.filters.http.grpc_http1_bridge  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config    upgrade_protobuf_to_grpc: true- name: envoy.filters.http.fault  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault    # 错误注入配置- name: envoy.filters.http.router

7.4 复杂路由配置

python 复制代码
route_config:  name: local_route  virtual_hosts:  - name: backend    domains: ["*"]    routes:    - match:        prefix: "/api/v1/grpc"      route:        cluster: grpc_backend        per_filter_config:          envoy.filters.http.grpc_http1_bridge:            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config            upgrade_protobuf_to_grpc: true    - match:        prefix: "/api/v1/http"      route: { cluster: http_backend }

8. 代码实现细节

8.1 过滤器初始化

php 复制代码
Http1BridgeFilter::Http1BridgeFilter(    Grpc::Context& context,    const envoy::extensions::filters::http::grpc_http1_bridge::v3::Config& proto_config)    : context_(context), upgrade_protobuf_(proto_config.upgrade_protobuf_to_grpc()) {}

8.2 请求处理

ruby 复制代码
Http::FilterHeadersStatus Http1BridgeFilter::decodeHeaders(Http::RequestHeaderMap& headers, bool) {  const bool protobuf_request = Grpc::Common::isProtobufRequestHeaders(headers);  if (upgrade_protobuf_ && protobuf_request) {    do_framing_ = true;    headers.setContentType(Http::Headers::get().ContentTypeValues.Grpc);    headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Grpc);    headers.removeContentLength(); // message length part of the gRPC frame    decoder_callbacks_->clearRouteCache();  }
  const bool grpc_request = Grpc::Common::isGrpcRequestHeaders(headers);
  const absl::optional<Http::Protocol>& protocol = decoder_callbacks_->streamInfo().protocol();  ASSERT(protocol);  if (protocol.value() < Http::Protocol::Http2 && grpc_request) {    do_bridging_ = true;  }
  return Http::FilterHeadersStatus::Continue;}
Http::FilterDataStatus Http1BridgeFilter::decodeData(Buffer::Instance& data, bool end_stream) {  if (!do_bridging_ || !do_framing_) {    return Http::FilterDataStatus::Continue;  }
  decoder_callbacks_->addDecodedData(data, true);  if (end_stream && do_framing_) {    decoder_callbacks_->modifyDecodingBuffer(        [](Buffer::Instance& buf) { Grpc::Common::prependGrpcFrameHeader(buf); });    return Http::FilterDataStatus::Continue;  }
  return Http::FilterDataStatus::StopIterationAndBuffer;}

8.3 响应处理

php 复制代码
Http::FilterHeadersStatus Http1BridgeFilter::encodeHeaders(Http::ResponseHeaderMap& headers,                                                           bool end_stream) {
  if (!do_bridging_ || end_stream) {    return Http::FilterHeadersStatus::Continue;  } else {    response_headers_ = &headers;    return Http::FilterHeadersStatus::StopIteration;  }}
Http::FilterDataStatus Http1BridgeFilter::encodeData(Buffer::Instance& data, bool end_stream) {  if (!do_bridging_ || end_stream) {    return Http::FilterDataStatus::Continue;  } else {    // Buffer until the complete request has been processed.    if (do_framing_) {      data.drain(Grpc::GRPC_FRAME_HEADER_SIZE);    }    return Http::FilterDataStatus::StopIterationAndBuffer;  }}
Http::FilterTrailersStatus Http1BridgeFilter::encodeTrailers(Http::ResponseTrailerMap& trailers) {
  if (do_bridging_) {    // Here we check for grpc-status. If it's not zero, we change the response code. We assume    // that if a reset comes in and we disconnect the HTTP/1.1 client it will raise some type    // of exception/error that the response was not complete.    const Http::HeaderEntry* grpc_status_header = trailers.GrpcStatus();    if (grpc_status_header) {      uint64_t grpc_status_code;      if (!absl::SimpleAtoi(grpc_status_header->value().getStringView(), &grpc_status_code) ||          grpc_status_code != 0) {        response_headers_->setStatus(enumToInt(Http::Code::ServiceUnavailable));      }      response_headers_->setGrpcStatus(grpc_status_header->value().getStringView());    }
    const Http::HeaderEntry* grpc_message_header = trailers.GrpcMessage();    if (grpc_message_header) {      response_headers_->setGrpcMessage(grpc_message_header->value().getStringView());    }
    // Since we are buffering, set content-length so that HTTP/1.1 callers can better determine    // if this is a complete response.    response_headers_->setContentLength(        encoder_callbacks_->encodingBuffer() ? encoder_callbacks_->encodingBuffer()->length() : 0);  }
  // NOTE: We will still write the trailers, but the HTTP/1.1 codec will just eat them and end  //       the chunk encoded response which is what we want.  return Http::FilterTrailersStatus::Continue;}
相关推荐
yqcoder2 小时前
速度的革命:深入解析 HTTP/2.0 的四大核心特性
网络·网络协议·http
哇嘎呀2 小时前
OSPF综合实验
网络
Shota Kishi2 小时前
ERPC 为 Solana 专项基础设施推出小时计费:从 1 小时起实测 RPC 与流式传输性能
网络·网络协议·rpc
杰克逊的日记2 小时前
k8s的两种网络转发规则及原理
网络·容器·kubernetes
TechWayfarer10 小时前
查询IP所在地的3种方案:从API到离线库,风控场景怎么选?
开发语言·网络·python·网络协议·tcp/ip
ylscode13 小时前
微软Exchange Server曝高危零日漏洞:朝鲜黑客利用“Toast攻击“入侵企业邮件系统
网络·安全·web安全
heimeiyingwang13 小时前
【架构实战】可观测性体系:从监控到全链路追踪
网络·数据库·架构
小茴香35314 小时前
HTTP缓存
网络协议·http·缓存·面试
weixin_5118404714 小时前
2026年5月4日 OCS技术方案路线选择与优劣深度调研报告
网络·人工智能