Java 在grpc拦截器中使用ThreadLocal的正确方法是什么?

Java 在grpc拦截器中使用ThreadLocal的正确方法是什么?,java,rpc,grpc,Java,Rpc,Grpc,我们有一个gPRC服务,它需要在类中的ThreadLocal变量中设置身份验证/标识信息,以便正确调用另一个服务。gPRC服务从请求中获取身份验证/标识信息,因此我考虑使用拦截器 首先,我有一些代码如下所示 public class ImpersonationInterceptor { public <ReqT, RespT> interceptCall( ServerCall<ReqT, RespT> serverCall, Metadata h

我们有一个gPRC服务,它需要在类中的ThreadLocal变量中设置身份验证/标识信息,以便正确调用另一个服务。gPRC服务从请求中获取身份验证/标识信息,因此我考虑使用拦截器

首先,我有一些代码如下所示

public class ImpersonationInterceptor {
    public <ReqT, RespT> interceptCall(
        ServerCall<ReqT, RespT> serverCall, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
        Principal principal = ... // get the identify from the request

        AuthContext.setPrincipal(principal); // underneath it uses a ThreadLocal.

        return next.startCall(
            new SimpleForwardingServerCall<>(call) {
                public void close(Status status, Metadata trailers) {
                    AuthContext.setPrincipal(null); // clear the identity
                }
            }
        )
    }
}
公共类模拟interceptor{
公用电话(
ServerCall ServerCall、元数据头、ServerCallHandler(下一步){
Principal=…//从请求中获取标识
AuthContext.setPrincipal(principal);//下面使用ThreadLocal。
返回next.startCall(
新建SimpleForwardingServerCall(调用){
公共作废关闭(状态、元数据){
AuthContext.setPrincipal(null);//清除标识
}
}
)
}
}
问题

  • 服务方法本身的执行可能不在执行拦截器的同一线程上,对吗
  • 如果是真的,上述方法行不通,那么问题是,在gRPC世界中设置ThreadLocal变量的标准方法是什么?我知道gRPC从0.12开始就支持上下文,但就我而言,我必须使用AuthContext的ThreadLocal机制

非常感谢。

对于此类上下文信息,您必须非常小心地使用ThreadLocals,因为您不希望意外地为客户端使用错误的标识

来自gRPC的每个回调都可以发生在不同的线程上,多个RPC的回调可以发生在同一个线程上

你需要遵循这样的模式。您必须在每次通话后设置/取消设置:

公共类模拟interceptor{
公用电话(
ServerCall ServerCall、元数据头、ServerCallHandler(下一步){
本金=。。。;
AuthContext.setPrincipal(principal);
试一试{
返回新的WrappingListener(next.startCall(call,headers),主体);
}最后{
AuthContext.clearPrincipal();
}
}
私有静态类包装器扩展
ForwardingServerCallListener.SimpleForwardingServerCallListener{
私人最终委托人;
public WrappingListener(ServerCall.Listener委托,主体){
超级(代表);
this.principal=principal;
}
@凌驾
公共消息无效(请求消息){
AuthContext.setPrincipal(principal);
试一试{
super.onMessage(message);
}最后{
AuthContext.clearPrincipal();
}
}
…对每个方法重复
}
}

感谢您的回复和澄清@Eric Anderson。你所解释的对我有意义。另一个愚蠢的问题是,这些回调的执行与服务方法的实际执行有什么关系,因为后者是我关心的ThreadLocal是如何使用的。此外,我是否可以通过以下方式对正确的行为编写单元测试?服务方法的执行由io.grpc.stub.ServerCalls决定。根据方法的类型和ServerCalls中的特定实现,可以在startCall()、onMessage()和onHalfClose()期间调用应用程序。根据服务代码的不同,也可以在onCancel()和onReady()期间调用它。将来onComplete()可以调用该服务。因此,您需要为每个方法设置主体,本质上是这样的。@Eric Anderson-关于“来自gRPC的每个回调都可能发生在不同的线程上”,假设您有3个链式拦截器。是否保证在同一线程上调用给定回调的完整拦截器链?否则我不明白这是怎么回事。。。您必须以某种方式在每个拦截器中传播主体…?拦截器通常直接在它们被调用的线程上相互调用。因此,这对大多数拦截器来说都不是问题。如果拦截器使用另一个线程发出回调,则需要确保上下文已传播。