TPM达6000时GRPC请求丢失问题求助(AWS ALB环境)
环境信息
- Java客户端
- 中间层为AWS gRPC ALB
- 后端服务部署2个实例,CPU、内存、网络带宽使用率均低于5%
- 当前配置:10-20规模的Channel Pooling,唯一通道名称,启用gRPC重试(最大6次)
背景
遵循gRPC最佳实践后,TPM达6000时仍存在1-2%的请求丢失,表现为UNAVAILABLE错误。此前通过Channel Pooling解决了Deadline Exceeded问题(原超时1秒,服务实际响应仅1毫秒),但UNAVAILABLE错误导致的服务不可用问题已持续3-4个月,未找到有效文档解决。
通道创建代码
Map<String, Object> customArgs = new HashMap<>(); customArgs.put("channelNumber", String.valueOf(channelNumber)); ManagedChannel channel = ManagedChannelBuilder.forTarget(grpcUrl) .useTransportSecurity() .defaultLoadBalancingPolicy("round_robin") .enableRetry() .maxRetryAttempts(6) .defaultServiceConfig(customArgs) .build();
报错信息
RankingServiceSort EXCEPTION : UNAVAILABLE: unavailableio.grpc.stub.ClientCalls|toStatusRuntimeException|262 io.grpc.stub.ClientCalls|getUnchecked|243 io.grpc.stub.ClientCalls|blockingUnaryCall|156 proto.RankingServiceGrpc$RankingServiceBlockingStub|sortInventoryData|169 RankingService.RankingGrpcConnector|GetInventoryRanking|80
已尝试措施
- 采用Channel Pooling结合AWS gRPC ALB,缓解了Deadline Exceeded问题
- 启用gRPC重试机制(最大6次),但请求丢失率无变化
排查与解决步骤
1. 修复gRPC重试配置的有效性
你的代码仅启用了重试并设置最大次数,但未指定重试触发的错误码和方法匹配规则。gRPC默认不会对所有错误重试,必须显式配置才能让UNAVAILABLE错误触发重试。
修改通道创建代码,添加精确的重试策略:
// 定义重试策略 Map<String, Object> retryPolicy = new HashMap<>(); retryPolicy.put("maxAttempts", 6); retryPolicy.put("initialBackoff", "0.1s"); retryPolicy.put("maxBackoff", "1s"); retryPolicy.put("backoffMultiplier", 2.0); retryPolicy.put("retryableStatusCodes", Arrays.asList("UNAVAILABLE", "DEADLINE_EXCEEDED")); // 绑定到目标服务 Map<String, Object> methodConfig = new HashMap<>(); methodConfig.put("name", Collections.singletonList(Collections.singletonMap("service", "proto.RankingService"))); methodConfig.put("retryPolicy", retryPolicy); // 组装服务配置 Map<String, Object> customArgs = new HashMap<>(); customArgs.put("methodConfig", Collections.singletonList(methodConfig)); customArgs.put("channelNumber", String.valueOf(channelNumber)); // 构建通道 ManagedChannel channel = ManagedChannelBuilder.forTarget(grpcUrl) .useTransportSecurity() .defaultLoadBalancingPolicy("round_robin") .enableRetry() .defaultServiceConfig(customArgs) .build();
注意:maxRetryAttempts()是全局默认值,但通过serviceConfig配置的重试策略优先级更高,必须显式包含UNAVAILABLE到retryableStatusCodes中,重试机制才会对该错误生效。
2. 排查AWS gRPC ALB配置问题
AWS gRPC ALB的几个常见配置坑会导致UNAVAILABLE错误:
- 调整ALB空闲超时:默认空闲超时为60秒,若客户端与ALB的连接空闲超时,ALB会主动断开连接,此时发起请求会触发UNAVAILABLE。建议将ALB空闲超时调整为30秒,与客户端keepalive配置匹配。
- 添加客户端keepalive配置:防止连接被ALB主动断开:
ManagedChannel channel = ManagedChannelBuilder.forTarget(grpcUrl) .useTransportSecurity() .defaultLoadBalancingPolicy("round_robin") .enableRetry() .keepAliveTime(30, TimeUnit.SECONDS) // 每30秒发送keepalive ping .keepAliveTimeout(5, TimeUnit.SECONDS) // ping超时时间 .keepAliveWithoutCalls(true) // 无请求时也发送ping .defaultServiceConfig(customArgs) .build();
- 检查目标组健康检查:确保ALB目标组的gRPC健康检查路径正确(对应服务的
grpc.health.v1.Health/Check接口),若健康检查失败,ALB会移除实例,导致请求路由到不可用节点。
3. 验证Channel Pooling实现逻辑
如果Channel Pooling是自定义实现,需确认:
- 通道被复用,而非每个请求创建新通道(gRPC通道本身线程安全,可处理多并发请求)
- 及时移除已关闭或失效的通道
- 避免频繁创建/销毁通道导致连接不稳定
4. 启用gRPC详细日志排查
添加日志配置查看请求生命周期,确认重试是否实际触发:
# Logback配置示例 <logger name="io.grpc.netty" level="DEBUG"/> <logger name="io.grpc.internal" level="DEBUG"/> <logger name="io.grpc.stub" level="DEBUG"/>
通过日志可确认:请求是否被重试、触发重试的错误码、连接是否被断开/重置。
5. 检查后端服务配置
后端服务需确保:
- 启用keepalive配置,避免被ALB判定为不健康
- 请求处理线程池足够(即使CPU使用率低,线程池耗尽也会触发UNAVAILABLE)
- 服务端无
Connection reset by peer等连接重置日志
内容的提问来源于stack exchange,提问作者Shubham Hudda
相关产品推荐
相关产品推荐

