绕过特定端点的 gRPC 拦截器

Bypass gRPC interceptor for specific endpoint

最近我一直在为一个问题而苦苦挣扎:我不能忽略某些端点的 io.grpc.ServerInterceptor,在我的例子中是健康检查端点,由 io.grpc.protobuf.services.HealthServiceImpl[=14 提供=]

我能找到的唯一选择是定义一个新的 ServerInterceptor,它通过 io.grpc.Listener#onMessage 检索端点名称,如果端点匹配,则在执行任何其他 ServerInteceptor 之前关闭 gRPC 调用。

不过,这个方案看起来不是很优雅。 有没有办法(也许是通过配置)忽略我现有的拦截器?

这个问题是新问题,当我开始使用 gRPC 版本 1.42.2 时。在 gRPC 1.35.0.

下一切正常

感谢您的帮助。

编辑:一些代码

在我的配置中 class :


@Bean
HealthStatusManager healthStatusManager() {
        return new HealthStatusManager();
}

@Bean
Server server(
        Service1 service1,
        Service2 service2,
        HealthStatusManager healthStatusManager
) {
    return ServerBuilder.forPort(9090)
            .addService(service1)
            .addService(service2)
            .addService(healthStatusManager.getHealthService())
            // I do not want these to interceptors to be executed for healthService
            .intercept(new Interceptor1()) 
            .intercept(new Interceptor2())
            .build();
}

Interceptor2 做了一些我的健康检查服务不需要的检查(甚至阻止它正常工作)

Protobuf/gRPC 反射在这种情况下可能很有用。我将通过执行以下操作来实现这样的解决方案:

假设我有以下服务:

service HealthService {
    //...
}

会生成一个 HealthServiceGrpc class 并且在这个 class 中有一个 public static final String SERVICE_NAME请注意,如果您使用 java gRPC 的精简版可能不存在).

在那之后,您在 ServerInterceptor.interceptCall 中的 ServerCall class 具有 getMethodDescriptor() 功能。这 returns 您正在调用的端点的描述符。从中您可以获得服务名称。尽管如此,一个基本的实现是:

public class Interceptor1 implements ServerInterceptor {
    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
        if (Objects.equals(call.getMethodDescriptor().getServiceName(), HealthServiceGrpc.SERVICE_NAME))
            return next.startCall(call, headers);

        return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) {
            //...
        };
    }
}

在这里稍微解释一下,如果服务的名称是HealthService,我们就直接调用,否则我们通过拦截器实现。

显然,在那之后,如果你想要更少的手动操作,你可能想要使用一个 Singleton 实例,它注册哪个拦截器来跳过哪个服务。

我们遇到了同样的问题,但做了一些我觉得更简单的不同事情。

在您的 Server Builder 中,您可以将拦截器附加到特定端点,如下所示:

@Bean
HealthStatusManager healthStatusManager() {
        return new HealthStatusManager();
}

@Bean
Server server(
        Service1 service1,
        Service2 service2,
        HealthStatusManager healthStatusManager
) {
    return ServerBuilder.forPort(9090)
            .addService(ServerInterceptor.intercept(service1, new Interceptor1(), new Interceptor2()))
            .addService(ServerInterceptor.intercept(service2, new Interceptor1(), new Interceptor2()))
            .addService(healthStatusManager.getHealthService())
            .build();
}

希望这对您有用!