Envoy gRPC HTTP/1 反向桥接功能实现分析

1. 概述

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

该功能的主要特点包括:

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

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

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

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

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

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

2. 解决的问题

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

2.1 API 兼容性

  • 允许使用 gRPC 客户端与不支持 gRPC 协议的上游服务通信

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

  • 提供了与现有 HTTP/1.1 服务器和工具的兼容性

2.2 开发效率

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

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

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

2.3 性能优化

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

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

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

2.4 功能完整性

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

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

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

3. 架构设计

3.1 核心组件架构

3.2 类图

3.3 基础配置

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 }
static_resources:  clusters:  - name: http_backend    connect_timeout: 0.25s    type: STRICT_DNS    lb_policy: ROUND_ROBIN    load_assignment:      cluster_name: http_backend      endpoints:      - lb_endpoints:        - endpoint:            address:              socket_address:                address: http-backend.example.com                port_value: 80

3.4 高级配置

bash 复制代码
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/x-protobuf"    withhold_grpc_frames: 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_reverse_bridge            typed_config:              "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.Config              upstream_content_type: "application/x-protobuf"              withhold_grpc_frames: true          - name: envoy.filters.http.router          route_config:            name: local_route            virtual_hosts:            - name: backend              domains: ["*"]              routes:              - match: { prefix: "/" }                route: { cluster: http_backend }  clusters:  - name: http_backend    connect_timeout: 0.25s    type: STRICT_DNS    lb_policy: ROUND_ROBIN    load_assignment:      cluster_name: http_backend      endpoints:      - lb_endpoints:        - endpoint:            address:              socket_address:                address: http-backend.example.com                port_value: 80

3.5 路由级配置

bash 复制代码
route_config:  name: local_route  virtual_hosts:  - name: backend    domains: ["*"]    routes:    - match:        prefix: "/api"      route:        cluster: http_backend        per_filter_config:          envoy.filters.http.grpc_http1_reverse_bridge:            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.FilterConfigPerRoute            disabled: false

3.6 复杂配置

bash 复制代码
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: "X-Content-Length"
route_config:  name: local_route  virtual_hosts:  - name: backend    domains: ["*"]    routes:    - match:        prefix: "/api/grpc"      route:        cluster: http_backend        per_filter_config:          envoy.filters.http.grpc_http1_reverse_bridge:            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.FilterConfigPerRoute            disabled: false    - match:        prefix: "/api/http"      route: { cluster: http_backend }

4. 工作流程分析

4.1 过滤器执行流程

4.2 请求转换流程

5. 代码实现ER图

6. 最佳实践

6.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_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- name: envoy.filters.http.router

6.2 性能优化

bash 复制代码
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/x-protobuf"    withhold_grpc_frames: true    response_size_header: "Content-Length"

6.3 错误处理

bash 复制代码
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- name: envoy.filters.http.fault  typed_config:    "@type": type.googleapis.com/envoy.extensions.filters.http.fault.v3.HTTPFault    # 错误注入配置- name: envoy.filters.http.router

6.4 复杂路由配置

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

7. 代码实现细节

7.1 过滤器初始化

css 复制代码
Filter::Filter(std::string upstream_content_type, bool withhold_grpc_frames,               std::string response_size_header)    : upstream_content_type_(std::move(upstream_content_type)),      withhold_grpc_frames_(withhold_grpc_frames),      response_size_header_(!response_size_header.empty()                                ? absl::make_optional(Http::LowerCaseString(response_size_header))                                : absl::nullopt) {}

7.2 请求处理

ruby 复制代码
Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) {  if (end_stream) {    return Http::FilterHeadersStatus::Continue;  }
  const auto* per_route_config =      Http::Utility::resolveMostSpecificPerFilterConfig<FilterConfigPerRoute>(decoder_callbacks_);  if (per_route_config != nullptr && per_route_config->disabled()) {    enabled_ = false;    return Http::FilterHeadersStatus::Continue;  }
  if (Envoy::Grpc::Common::isGrpcRequestHeaders(headers)) {    enabled_ = true;
    content_type_ = std::string(headers.getContentTypeValue());    headers.setContentType(upstream_content_type_);    headers.setInline(accept_handle.handle(), upstream_content_type_);
    if (withhold_grpc_frames_) {      adjustContentLength(headers, [](auto size) { return size - Grpc::GRPC_FRAME_HEADER_SIZE; });    }
    decoder_callbacks_->clearRouteCache();  }
  return Http::FilterHeadersStatus::Continue;}
Http::FilterDataStatus Filter::decodeData(Buffer::Instance& buffer, bool) {  if (enabled_ && withhold_grpc_frames_ && !prefix_stripped_) {    if (buffer.length() < Grpc::GRPC_FRAME_HEADER_SIZE) {      decoder_callbacks_->sendLocalReply(Http::Code::OK, "invalid request body", nullptr,                                         Grpc::Status::WellKnownGrpcStatus::Unknown,                                         RcDetails::get().GrpcBridgeFailedTooSmall);      return Http::FilterDataStatus::StopIterationNoBuffer;    }
    buffer.drain(Grpc::GRPC_FRAME_HEADER_SIZE);    prefix_stripped_ = true;  }
  return Http::FilterDataStatus::Continue;}

7.3 响应处理

ruby 复制代码
Http::FilterHeadersStatus Filter::encodeHeaders(Http::ResponseHeaderMap& headers, bool) {  if (enabled_) {    absl::string_view content_type = headers.getContentTypeValue();
    if (content_type != upstream_content_type_) {      decoder_callbacks_->sendLocalReply(Http::Code::OK, badContentTypeMessage(headers), nullptr,                                         Grpc::Status::WellKnownGrpcStatus::Unknown,                                         RcDetails::get().GrpcBridgeFailedContentType);
      return Http::FilterHeadersStatus::StopIteration;    }
    headers.setContentType(content_type_);
    if (withhold_grpc_frames_) {      if (response_size_header_) {        auto length_headers = headers.get(*response_size_header_);        if (!length_headers.empty() &&            absl::SimpleAtoi(length_headers[0]->value().getStringView(), &response_message_length_)) {          headers.setContentLength(response_message_length_ + Grpc::GRPC_FRAME_HEADER_SIZE);        } else {          decoder_callbacks_->sendLocalReply(              Http::Code::OK, "envoy reverse bridge: upstream did not set content length", nullptr,              Grpc::Status::WellKnownGrpcStatus::Internal,              RcDetails::get().GrpcBridgeFailedMissingContentLength);          return Http::FilterHeadersStatus::StopIteration;        }      } else {        adjustContentLength(headers,                            [](auto length) { return length + Grpc::GRPC_FRAME_HEADER_SIZE; });      }    }
    grpc_status_ = grpcStatusFromHeaders(headers);  }
  return Http::FilterHeadersStatus::Continue;}
Http::FilterDataStatus Filter::encodeData(Buffer::Instance& buffer, bool end_stream) {  upstream_response_bytes_ += buffer.length();  if (!enabled_) {    return Http::FilterDataStatus::Continue;  }
  if (withhold_grpc_frames_ && response_size_header_ && !frame_header_added_) {    buildGrpcFrameHeader(buffer, response_message_length_);    frame_header_added_ = true;  }
  if (end_stream) {    auto& trailers = encoder_callbacks_->addEncodedTrailers();    trailers.setGrpcStatus(grpc_status_);
    if (withhold_grpc_frames_) {      if (response_size_header_) {        if (upstream_response_bytes_ != response_message_length_) {          encoder_callbacks_->sendLocalReply(              Http::Code::OK, "envoy reverse bridge: upstream set incorrect content length",              nullptr, Grpc::Status::WellKnownGrpcStatus::Internal,              RcDetails::get().GrpcBridgeFailedWrongContentLength);          return Http::FilterDataStatus::StopIterationNoBuffer;        }      } else {        buffer.prepend(buffer_);        buildGrpcFrameHeader(buffer, buffer.length());      }    }
    return Http::FilterDataStatus::Continue;  }
  if (withhold_grpc_frames_) {    if (response_size_header_) {      return Http::FilterDataStatus::Continue;    }
    buffer_.move(buffer);    return Http::FilterDataStatus::StopIterationAndBuffer;  } else {    return Http::FilterDataStatus::Continue;  }}

7.4 帧头构建

css 复制代码
void Filter::buildGrpcFrameHeader(Buffer::Instance& buffer, uint32_t message_length) {  Grpc::Encoder().prependFrameHeader(Grpc::GRPC_FH_DEFAULT, buffer, message_length);}
相关推荐
草莓熊Lotso7 分钟前
【Linux网络】深入理解Linux IO多路复用:select服务器完善、内核原理与poll实战
linux·运维·服务器·c语言·网络·c++
●VON13 分钟前
鸿蒙 PC Markdown 编辑器 Alpha 评审:如何用退出条件判断阶段完成
网络·安全·华为·编辑器·harmonyos·鸿蒙
网安老伯35 分钟前
网络安全基础要点知识介绍(非常详细),零基础入门到精通,看这一篇就够了
运维·前端·网络协议·web安全·网络安全·职场和发展
nVisual1 小时前
01-环境监控集成方案
运维·服务器·开发语言·网络·数据库·数据中心布线·综合布线管理软件
沐苏瑶1 小时前
计算机网络核心笔记:打通 TCP/UDP 与 HTTP/HTTPS 底层逻辑(重点上)
笔记·计算机网络·http·https
会编程的土豆1 小时前
HTTP 是什么:你打开 localhost:8080 时发生了什么
网络·网络协议·http
M1582276905510 小时前
跨协议互通利器|SG-PN-EIP (S)-220 Profinet 转 EtherNet/IP 网关,打通欧美 PLC 通讯壁垒
网络·网络协议·tcp/ip
北龙云海10 小时前
案例分享 | 面向公网业务系统境内访问白名单配置实操方案
运维·网络·安全·网络安全·告警·数据泄露·入侵攻击
闲猫11 小时前
Spring AI对接Deepseek 使用Charles代理api.deepseek.com 拦截报文,查看Tool和Skill的本质
网络·网络协议·http
niaiheni13 小时前
Fastjson 1.2.83 “Gadget-Free“ RCE
网络·安全·web安全