尝试使用 gRPC Web 将 Envoy 连接到 gRPC 服务时出现“协议错误”

`Protocol Error` when trying to connect Envoy to gRPC service using gRPC Web

我有一个网络应用程序使用 gRPC Web 通过 dockerized Envoy 代理与我的 gRPC 服务交互。当我尝试调用 Envoy 中公开的端点时,我收到以下错误:

gRPC Error (code: 14, codeName: UNAVAILABLE, message: upstream connect error or disconnect/reset before headers. reset reason: protocol error, details: [], rawResponse: null, trailers: {content-length: 0})

这是我的客户端代码:

class FiltersService {
  static ResponseFuture<Filters> getFilters() {    
    GrpcWebClientChannel channel =
        GrpcWebClientChannel.xhr(Uri.parse('http://localhost:9000'));

    FiltersServiceClient clientStub = FiltersServiceClient(
      channel,
    );

    return clientStub.getFilters(Void());
  }
}

这是我的 Envoy.yaml:

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 9000 }
      filter_chains:
        - filters:
            - name: envoy.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                codec_type: auto
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/" }
                          route:
                            cluster: greeter_service
                            max_grpc_timeout: 0s
                      cors:
                        allow_origin_string_match:
                          - prefix: "*"
                        allow_methods: GET, PUT, DELETE, POST, OPTIONS
                        allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
                        max_age: "1728000"
                        expose_headers: custom-header-1,grpc-status,grpc-message
                http_filters:
                  - name: envoy.filters.http.grpc_http1_bridge
                  - name: envoy.filters.http.grpc_web
                  - name: envoy.filters.http.cors
                  - name: envoy.filters.http.router
  clusters:
    - name: greeter_service
      connect_timeout: 0.25s
      type: logical_dns
      upstream_connection_options:
        tcp_keepalive:
          keepalive_time: 300
      lb_policy: round_robin
      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: host.docker.internal
                      port_value: 9001

其中 9001 端口是我的 gRPC 服务 运行ning 所在的位置。我能够通过直接从 Kreya 调用它来验证该服务是否正常工作,并且我收到了正确的响应。

我也能够从 localhost:9901 成功访问管理服务器。

但是,当我尝试通过 Kreya 或客户端调用 Envoy (9000) 公开的端点时,我得到

gRPC Error (code: 14, codeName: UNAVAILABLE, message: upstream connect error or disconnect/reset before headers. reset reason: protocol error, details: [], rawResponse: null, trailers: {content-length: 0})

我正在 运行 将以下命令发送给 运行 Dockerized Envoy:

docker build -t my-envoy:1.0 .

docker run -p 9000:9000 -p 9901:9901 my-envoy:1.0

经过一番折腾和折腾,我终于弄明白了。查看文档,似乎 envoy.filters.httl.grpc_web 过滤器可以将请求转换为 HTTP/2 和 HTTP/3。我假设(如果在该领域有更多知识的人可以纠正我,请纠正)如果没有指定,Envoy 不知道要转换成什么协议。因此,只需将 http2_protocol_options: {} 添加到我的 cluster 即可解决问题。我在下面包含了完整的 cluster 块,以供将来可能遇到相同问题的任何人使用。

clusters:
    - name: greeter_service
      connect_timeout: 0.25s
      type: logical_dns
      lb_policy: round_robin
      http2_protocol_options: {}
      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: host.docker.internal
                      port_value: 9001

出于某种原因,每当我尝试在 docker run 命令中使用 --networks=host 标志时,我都无法访问管理门户,所以我遇到的解决方案和教程使用 Envoy 代理 gRPC Web 对解决这个问题不是很有帮助。希望这对面临同样问题的人有所帮助。