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);}
相关推荐
小新讲网安11 小时前
HTTP请求走私攻击实战:CL.TE与TE.CL绕过前端服务器全解析
服务器·前端·网络·web安全·http·架构·漏洞
zhengqiqiqinqin11 小时前
Ubuntu16.04 升级ssl到3.5.7、升级ssh到9.8p1
网络协议·ssh·ssl
上海安当技术1 天前
固件签名与Secure Boot:安当CAS如何构建汽车固件信任链
网络·汽车·信任链·boot·secure·固件签名·安当cas
上海安当技术1 天前
诊断接入认证:安当CAS如何堵住汽车网络安全的物理后门
网络·web安全·汽车·access·secure·安当cas·诊断接入认证
ze^01 天前
【软考信息安全】第2章 网络攻击原理与常用方法
网络·web安全·网络安全
这个DBA有点耶1 天前
数据库一体机架构演进:从硬件堆叠到软硬深度耦合
服务器·网络·数据库·硬件架构·运维开发·database·数据库架构
arbboter1 天前
【网络工具】NetProxy网络代理用户手册
开发语言·网络·c#·网络代理·网络异常·代理工具
消失的旧时光-19431 天前
第一篇:Ktor Client 到底是什么?从 Retrofit 迁移理解 Ktor 网络请求架构
网络·架构·retrofit·ktor·dsl
数据知道1 天前
反序列化漏洞:Java、PHP、Python 三条线各讲透
java·网络·python·安全·网络安全·php
江畔柳前堤1 天前
Function Calling 与 Tool Calling:从认知到工程的全景深度解析
开发语言·网络·人工智能·深度学习·算法·机器学习·php