1. 概述
Envoy 的 gRPC HTTP/1 反向桥接功能为 gRPC 和 HTTP/1.1 协议之间的双向转换提供了强大而灵活的解决方案。它允许使用标准的 gRPC 客户端访问传统的 HTTP/1.1 服务器,同时保持了 API 的完整性和性能。
该功能的主要特点包括:
-
支持所有 gRPC 方法类型(一元、流式等)
-
提供了完整的错误处理和状态码转换
-
与 Envoy 的架构深度集成,性能优异
-
支持灵活的配置选项,包括路由级配置
-
与其他 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);}