Multithreading springbootgrpc:ServerIntereceptor读取请求中的数据,并在响应中设置它

Multithreading springbootgrpc:ServerIntereceptor读取请求中的数据,并在响应中设置它,multithreading,grpc,interceptor,thread-local,grpc-java,Multithreading,Grpc,Interceptor,Thread Local,Grpc Java,在GRPC服务的每个请求协议中都有一个名为“元数据”的字段(不要与GRPC元数据混淆): message MyRequest { RequestResponseMetadata metadata = 1; ... } 所有响应中也存在相同的字段: message MyResponse { RequestResponseMetadata metadata = 1; ... } 我正在尝试编写一个ServerInterceptor(或者其他东西,如果可以的话),从请求中读取“me

在GRPC服务的每个请求协议中都有一个名为“元数据”的字段(不要与GRPC元数据混淆):

message MyRequest {
  RequestResponseMetadata metadata = 1;
  ...
}
所有响应中也存在相同的字段:

message MyResponse {
  RequestResponseMetadata metadata = 1;
  ...
}
我正在尝试编写一个
ServerInterceptor
(或者其他东西,如果可以的话),从请求中读取“metadata”字段,将其保存在某个地方,然后在处理完请求后在响应中设置它

尝试1:ThreadLocal

public class ServerInterceptor implements io.grpc.ServerInterceptor {

  private ThreadLocal<RequestResponseMetadata> metadataThreadLocal = new ThreadLocal<>();

  @Override
  public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
      ServerCall<ReqT, RespT> call,
      final Metadata requestHeaders,
      ServerCallHandler<ReqT, RespT> next) {
    return new SimpleForwardingServerCallListener<ReqT>(
        next.startCall(
            new SimpleForwardingServerCall<ReqT, RespT>(call) {
              @Override
              public void sendMessage(RespT message) {
                super.sendMessage(
                    (RespT)
                        MetadataUtils.setMetadata(
                            (GeneratedMessageV3) message, metadataThreadLocal.get()));
                metadataThreadLocal.remove();
              }
            },
            requestHeaders)) {
      @Override
      public void onMessage(ReqT request) {
        // todo nava see if ReqT can extend GenericV3Message
        var metadata = MetadataUtils.getMetadata((GeneratedMessageV3) request);
        metadataThreadLocal.set(metadata);
        super.onMessage(request);
      }
    };
  }
}

我计划在一个
onComplete()
中分离上下文,但在它自身到达之前,
sendMessage中的
METADATA\u KEY.get()
返回
null
,而我希望它返回数据

甚至在点击
sendMessage()
函数之前,我就在控制台中得到了这个消息,表明我做错了什么:

3289640 [grpc-default-executor-0] ERROR i.g.ThreadLocalContextStorage - Context was not attached when detaching
java.lang.Throwable: null
    at io.grpc.ThreadLocalContextStorage.detach(ThreadLocalContextStorage.java:48)
    at io.grpc.Context.detach(Context.java:421)
    at io.grpc.Context$CancellableContext.detach(Context.java:761)
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:39)
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
如何在接收到请求时读取数据、将其存储在某处并在发送回响应时使用它?

您可以使用将值从请求传递到响应:

公共类MetadataServerInterceptor实现ServerInterceptor{
public static final Metadata.Key Metadata\u Key=Metadata.Key.of(“Metadata bin”,Metadata.BINARY\u BYTE\u MARSHALLER);
@凌驾
public ServerCall.Listener interceptCall(ServerCall调用、元数据头、ServerCallHandler-next){
var serverCall=new ForwardingServerCall.SimpleForwardingServerCall(调用){
@凌驾
公共无效发送消息(响应消息){
字节[]metadata=headers.get(metadata\u键);
message=(RespT)MetadataUtils.setMetadata((GeneratedMessageV3)消息,元数据);
super.sendMessage(message);
}
};
ServerCall.Listener listenerWithContext=Contexts.interceptCall(Context.current(),ServerCall,headers,next);
返回新的ForwardingServerCallListener.SimpleForwardingServerCallListener(listenerWithContext){
@凌驾
公共消息无效(请求消息){
字节[]元数据=MetadataUtils.getMetadata((GeneratedMessageV3)消息);
headers.put(元数据\关键字,元数据);
super.onMessage(message);
}
};
}
}

注意:由于无法将
RequestResponseMetadata
的实例放入元数据中(至少在不实现自定义封送拆收器的情况下),因此可以将其保存为字节数组。您可以在
RequestResponseMetadata
对象上使用
toByteArray()
来获取
byte[]
RequestResponseMetadata.#parseFrom(byte[])
byte[]
获取对象这是否回答了您的问题@Eric Anderson很抱歉造成混淆,我对问题进行了编辑以进一步解释。请按照其他答案进行操作。使用Contexts.interceptCall()。上下文附加/分离遵循调用堆栈,因此如果没有在同一方法中进行分离,则无法附加它。在您的情况下,您可以在初始interceptCall()期间在上下文中放置一些可变的对象(例如,AtomicReference、SettableFuture、custom对象),然后在onMessage()中对该对象进行突变。
3289640 [grpc-default-executor-0] ERROR i.g.ThreadLocalContextStorage - Context was not attached when detaching
java.lang.Throwable: null
    at io.grpc.ThreadLocalContextStorage.detach(ThreadLocalContextStorage.java:48)
    at io.grpc.Context.detach(Context.java:421)
    at io.grpc.Context$CancellableContext.detach(Context.java:761)
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:39)
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)