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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 12:48:16