You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 11 gRPC客户端经Envoy代理连Go服务时ALPN协商失败

gRPC Java客户端通过TLS Envoy代理连接Go服务时ALPN协商失败

使用Java 11开发的gRPC流式客户端,通过启用TLS的Envoy代理连接Go语言编写的gRPC服务时,收到ServerResponseObserver返回的错误:

UNAVAILABLE: Failed ALPN negotiation: Unable to find compatible protocol

完整堆栈信息:

12/05/22 18:29:29 (798) grpc-default-executor-1 SEVERE *** ERROR *** server respond with onError: 
io.grpc.StatusRuntimeException: UNAVAILABLE: Failed ALPN negotiation: Unable to find compatible protocol
Channel Pipeline: [SslHandler#0, ProtocolNegotiators$ClientTlsHandler#0, WriteBufferingAndExceptionHandler#0, DefaultChannelPipeline$TailContext#0]
    at io.grpc.Status.asRuntimeException(Status.java:535)
    at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:487)
    at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:39)
    at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
    at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
    at io.grpc.internal.DelayedClientCall$DelayedListener$3.run(DelayedClientCall.java:468)
    at io.grpc.internal.DelayedClientCall$DelayedListener.delayOrExecute(DelayedClientCall.java:432)
    at io.grpc.internal.DelayedClientCall$DelayedListener.onClose(DelayedClientCall.java:465)
    at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:562)
    at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:70)
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInternal(ClientCallImpl.java:743)
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamClosed.runInContext(ClientCallImpl.java:722)
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)

请求无法通过Envoy代理到达gRPC服务,Envoy跟踪日志存在ssl error occurred while read: WANT_READ等SSL错误。

Java gRPC客户端配置如下:

ChannelCredentials credentials;
credentials = TlsChannelCredentials.newBuilder()
        .trustManager(new File("<path-to-file>/cacert.pem"))
        .build();
originChannel = Grpc.newChannelBuilderForAddress(host, port, credentials).build();
ClientInterceptor interceptor = new HeaderClientInterceptor();
Channel channel = ClientInterceptors.intercept(originChannel, interceptor);

stub = LogStreamerGrpc.newStub(channel);

解决方案

1. 确保Java 11的ALPN支持正常

Java 11默认包含ALPN支持,但部分环境可能存在异常:

  • 启动JVM时添加-Djavax.net.debug=ssl:handshake:verbose参数,查看握手日志中是否有ALPN协议列表的相关输出,确认ALPN功能启用。
  • 若使用OpenJDK,升级至11.0.10及以上版本,早期版本存在ALPN兼容性问题。

2. 显式配置gRPC客户端的ALPN协议

在构建TlsChannelCredentials时,指定仅支持HTTP/2协议,确保与Envoy、Go服务的ALPN配置匹配:

import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.ApplicationProtocolNames;
import java.util.Arrays;

// ...

credentials = TlsChannelCredentials.newBuilder()
        .trustManager(new File("<path-to-file>/cacert.pem"))
        .tlsContextCustomizer(tlsContextBuilder -> {
            tlsContextBuilder.applicationProtocolConfig(
                    ApplicationProtocolConfig.newBuilder()
                            .protocols(Arrays.asList(ApplicationProtocolNames.HTTP2))
                            .protocol(ApplicationProtocolConfig.Protocol.ALPN)
                            .selectorFailureBehavior(ApplicationProtocolConfig.SelectorFailureBehavior.FATAL_ALERT)
                            .build()
            );
        })
        .build();

3. 检查Envoy代理的TLS配置

确保Envoy监听配置中启用ALPN并指定h2协议:

listeners:
- name: listener_0
  address:
    socket_address: { address: 0.0.0.0, port_value: 443 }
  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
        codec_type: AUTO
        stat_prefix: ingress_http
        route_config:
          name: local_route
          virtual_hosts:
          - name: local_service
            domains: ["*"]
            routes:
            - match: { prefix: "/" }
              route: { cluster: grpc_service }
        http_filters:
        - name: envoy.filters.http.router
          typed_config: {}
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
        common_tls_context:
          tls_params:
            alpn_protocols: ["h2"]
          validation_context:
            trusted_ca: { filename: "/path/to/ca.crt" }

同时确认Envoy与Go服务之间的上游连接也配置了正确的TLS和ALPN参数。

4. 验证Go gRPC服务的TLS配置

Go gRPC服务默认支持ALPN和HTTP/2,但需确保证书配置正确:

import (
    "log"
    "google.golang.org/grpc/credentials"
)

// ...

creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil {
    log.Fatalf("Failed to generate credentials: %v", err)
}
server := grpc.NewServer(grpc.Creds(creds))
// 注册服务并启动监听

确保服务使用的证书在客户端信任列表中,且服务端未禁用ALPN功能。

内容的提问来源于stack exchange,提问作者dorony

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 00:15:35