gRPC升级至1.55.1后出现“call already closed”日志报错求助
gRPC升级后"call already closed"日志报错的原因与解决方法
问题概述
将gRPC从1.44.0升级到1.55.1后,服务端日志持续输出严重级别的堆栈信息,但实际业务逻辑完全正常:健康检查请求完成、客户端收到成功响应并正常关闭连接,仅日志被大量无效报错填充。目前希望避免通过完全禁用io.grpc.internal.SerializingExecutor日志的极端方式解决问题。
完整报错堆栈:
SEVERE: Exception while executing runnable io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed@56a7a5ef java.lang.IllegalStateException: call already closed at com.google.common.base.Preconditions.checkState(Preconditions.java:502) at io.grpc.internal.ServerCallImpl.closeInternal(ServerCallImpl.java:212) at io.grpc.internal.ServerCallImpl.close(ServerCallImpl.java:207) at brave.grpc.TracingServerInterceptor$TracingServerCall.close(TracingServerInterceptor.java:120) at io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl.onCompleted(ServerCalls.java:395) at MYOWNSERVICE.SimpleGrpcHealthServiceImpl.check(SimpleGrpcHealthServiceImpl.java:22) at io.grpc.health.v1.HealthGrpc$MethodHandlers.invoke(HealthGrpc.java:326) at io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener.onHalfClose(ServerCalls.java:182) at brave.grpc.TracingServerInterceptor$TracingServerCallListener.onHalfClose(TracingServerInterceptor.java:153) at io.grpc.PartialForwardingServerCallListener.onHalfClose(PartialForwardingServerCallListener.java:35) at io.grpc.ForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:23) at io.grpc.ForwardingServerCallListener$SimpleForwardingServerCallListener.onHalfClose(ForwardingServerCallListener.java:40) at io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.halfClosed(ServerCallImpl.java:346) at io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1HalfClosed.runInContext(ServerImpl.java:860) 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:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833)
原因分析
从堆栈追踪可明确问题根源:brave的gRPC链路追踪拦截器(TracingServerInterceptor)与gRPC新版本的兼容性问题:
- gRPC 1.50+版本对
ServerCall的关闭逻辑做了更严格的状态校验,当通过responseObserver.onCompleted()完成请求后,gRPC内部已标记call为关闭状态。 - 旧版本的
brave-grpc拦截器会在请求完成后额外调用ServerCall.close(),触发重复关闭操作,进而抛出IllegalStateException。
解决方法
1. 升级brave-grpc依赖版本(最优解)
将brave及brave-grpc依赖升级到适配gRPC 1.50+的版本(推荐brave 5.15.0及以上)。新版本的brave-grpc拦截器已修复重复关闭ServerCall的问题,适配了gRPC的严格校验逻辑。
2. 排查自定义拦截器
检查服务端是否存在其他自定义gRPC拦截器,确认是否有拦截器在responseObserver.onCompleted()之后重复调用ServerCall.close()或触发类似的完成逻辑,如有则调整拦截器逻辑避免重复操作。
3. 温和的日志调整(临时方案)
若暂时无法升级依赖,可调整日志级别替代完全禁用日志:仅降低io.grpc.internal.SerializingExecutor的日志级别至WARNING,既避免错误日志刷屏,又保留其他重要日志信息。代码示例:
Logger grpcExecutorLogger = Logger.getLogger("io.grpc.internal.SerializingExecutor"); grpcExecutorLogger.setLevel(Level.WARNING);
验证要点
无论采用哪种方案,验证时需确认:
- 健康检查请求仍正常完成,客户端能收到SERVING响应并正常关闭连接
- 日志中不再出现"call already closed"的严重报错
内容的提问来源于stack exchange,提问作者Archimedes Trajano
相关产品推荐
相关产品推荐

