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);}
相关推荐
yqcoder13 小时前
Web 世界的基石:深入解析 HTTP/1.1 的六大核心特点
前端·网络协议·http
灰子学技术13 小时前
Envoy gRPC HTTP/1.1 桥接功能实现分析
网络·网络协议·http
yqcoder13 小时前
速度的革命:深入解析 HTTP/2.0 的四大核心特性
网络·网络协议·http
哇嘎呀13 小时前
OSPF综合实验
网络
Shota Kishi13 小时前
ERPC 为 Solana 专项基础设施推出小时计费:从 1 小时起实测 RPC 与流式传输性能
网络·网络协议·rpc
杰克逊的日记14 小时前
k8s的两种网络转发规则及原理
网络·容器·kubernetes
TechWayfarer1 天前
查询IP所在地的3种方案:从API到离线库,风控场景怎么选?
开发语言·网络·python·网络协议·tcp/ip
ylscode1 天前
微软Exchange Server曝高危零日漏洞:朝鲜黑客利用“Toast攻击“入侵企业邮件系统
网络·安全·web安全
heimeiyingwang1 天前
【架构实战】可观测性体系:从监控到全链路追踪
网络·数据库·架构