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

时间:2019-07-01 13:26:28

标签: java rpc grpc

我们有一个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
                }
            }
        )
    }
}

问题。

  • 服务方法本身的执行可能不在同一线程上执行拦截器,对吗?
  • 如果为true,则上述方法不起作用,那么问题是,在gRPC世界中设置ThreadLocal变量的规范方法是什么?我知道从0.12开始,gRPC就提供了Context支持,但就我而言,我必须使用AuthContext的ThreadLocal机制。

非常感谢。

1 个答案:

答案 0 :(得分:1)

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

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

您需要遵循Contexts.interceptCall()之类的模式。您必须在每次通话后设置/取消设置:

public class ImpersonationInterceptor {
  public <ReqT, RespT> interceptCall(
      ServerCall<ReqT, RespT> serverCall, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
    Principal principal = ...;
    AuthContext.setPrincipal(principal);
    try {
      return new WrappingListener<>(next.startCall(call, headers), principal);
    } finally {
      AuthContext.clearPrincipal();
    }
  }

  private static class WrappingListener<ReqT> extends
      ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
    private final Principal principal;

    public WrappingListener(ServerCall.Listener<ReqT> delegate, Principal principal) {
      super(delegate);
      this.principal = principal;
    }

    @Override
    public void onMessage(ReqT message) {
      AuthContext.setPrincipal(principal);
      try {
        super.onMessage(message);
      } finally {
        AuthContext.clearPrincipal();
      }
    }
    ... repeat for each method
  }
}