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
相关产品推荐
相关产品推荐

