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

处理异常时关闭gRPC连接引发重复调用异常的解决咨询

解决gRPC校验拦截器中call重复关闭的IllegalStateException问题

问题根源

你当前的实现中,在拦截器的onMessage方法里直接调用exceptionHandler.handleError()关闭了call并返回错误响应。但gRPC的调用流程中,onMessage执行完成后还会触发onHalfClose事件,此时GrpcExceptionListener会再次调用异常处理器尝试关闭call,而call已经被提前关闭,因此抛出IllegalStateException: call already closed。

解决方案

不要在拦截器中手动调用异常处理器关闭call,而是直接抛出ValidationRuntimeException,把异常处理的控制权交给gRPC原生的异常处理链(包括你的@GrpcAdvice处理器),这样能确保call只会被关闭一次。

修改后的拦截器代码

@Slf4j
@GrpcGlobalServerInterceptor
public class ValidationInterceptor implements ServerInterceptor {
  private static final String UNKNOWN_CALL_NAME = "Unknown call";
  private final Validator validator;

  public ValidationInterceptor(Validator validator) {
    this.validator = validator;
  }

  @Override
  public <ReqT, RespT> Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call,
                                                    Metadata headers,
                                                    ServerCallHandler<ReqT, RespT> next) {
    final Listener<ReqT> delegateListener = next.startCall(call, headers);
    return new ForwardingServerCallListener.SimpleForwardingServerCallListener<>(delegateListener) {
      @Override
      public void onMessage(ReqT message) {
        try {
          final ValidationResult validationResult = validator.validate((Message) message);

          if (!validationResult.isSuccess()) {
            // 直接抛出异常,交给gRPC异常处理链统一处理
            throw new ValidationRuntimeException(
                getMethodName(call), validationResult.getViolations()
            );
          }

          super.onMessage(message);
        } catch (ValidationRuntimeException e) {
          throw e;
        } catch (Exception e) {
          log.warn("Unexpected exception caught when validating method {}", getMethodName(call), e);
          throw new RuntimeException("Validation failed unexpectedly", e);
        }
      }
    };
  }

  private <ReqT, RespT> String getMethodName(ServerCall<ReqT, RespT> call) {
    return Optional.ofNullable(call.getMethodDescriptor())
        .map(MethodDescriptor::getFullMethodName)
        .orElse(UNKNOWN_CALL_NAME);
  }
}

关键说明

  1. 移除手动调用异常处理器:拦截器不再依赖GrpcExceptionResponseHandler,而是通过抛出异常让gRPC自动触发后续的异常处理流程,避免提前关闭call。
  2. 异常流转逻辑:抛出的ValidationRuntimeException会被GrpcErrorHandler中的@GrpcExceptionHandler方法捕获,此时call处于未关闭状态,处理器可以正常构建StatusRuntimeException并关闭call。
  3. 原有处理器无需修改:你的GrpcErrorHandler代码保持原样即可,它会正确拦截并处理抛出的ValidationRuntimeException。

注意事项

  • 确保自定义异常是RuntimeException子类,这样gRPC才能自动捕获并触发异常处理链。
  • 禁止在拦截器中手动关闭call或调用异常处理器的关闭逻辑,必须遵循gRPC的原生调用流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 11:07:17