Java 我可以隐式地将reactor上下文传递到grpc clientInterceptor吗?

Java 我可以隐式地将reactor上下文传递到grpc clientInterceptor吗?,java,grpc,project-reactor,grpc-java,Java,Grpc,Project Reactor,Grpc Java,以下是我想在高层做的事情 在WebFilter中捕获一些http头 在控制器方法中,我进行grpc调用 我想将http头传播为grpc元数据头 目前,我的工作实现是 WebFilter捕获http头并写入反应器上下文 控制器方法提取反应器上下文并将其传递到Grpc ClientInterceptor Grpc ClientInterceptor从上下文中提取http头并注入Grpc元数据头 但是我希望避免让控制器方法做任何工作(上面的步骤2) 下面是一个实现,但正在寻找一种方法,可以将http头

以下是我想在高层做的事情

  • 在WebFilter中捕获一些http头
  • 在控制器方法中,我进行grpc调用
  • 我想将http头传播为grpc元数据头
  • 目前,我的工作实现是

  • WebFilter捕获http头并写入反应器上下文
  • 控制器方法提取反应器上下文并将其传递到Grpc ClientInterceptor
  • Grpc ClientInterceptor从上下文中提取http头并注入Grpc元数据头
  • 但是我希望避免让控制器方法做任何工作(上面的步骤2)

    下面是一个实现,但正在寻找一种方法,可以将http头获取到Grpc元数据,而无需从控制器方法显式地传递它们

    WebFilter

    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
      return chain.filter(exchange)
        .contextWrite(Context.of("my-header", "header-value"));
    }
    
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
      final ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);
      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
        @Override
        public void start(Listener<RespT> responseListener, Metadata headers) {
          Metadata.Key < String > key =
              Metadata.Key.of("filter-context", Metadata.ASCII_STRING_MARSHALLER);
          headers.put(key, context.get("filter-context"));
    
          delegate().start(responseListener, headers);
        }
      };
    }
    
    我相信SpringSleuth有办法做到这一点,但不确定如何调整其方法。 我错过了什么聪明的东西

    编辑

    我之所以推出包含最少控制器方法代码的版本,是因为其他开发人员将编写控制器和方法。如果可能的话,我想建立一个不需要额外布线的模式,否则就有可能有人忘记或做错了

    编辑

    跟进问题。我没有让控制器方法在上下文中传递给clientInterceptor,而是尝试在Grpc clientInterceptor中获取上下文,但这似乎不起作用

    这就是我试图做的

    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
      final ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);
    
      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
        @Override
        public void start(Listener<RespT> responseListener, Metadata headers) {
    
          Mono.deferContextual(context -> {
            Metadata.Key < String > key =
                Metadata.Key.of("CONTEXT-HEADER", Metadata.ASCII_STRING_MARSHALLER);
            headers.put(key, context.get("CONTEXT-HEADER"));
    
            delegate().start(responseListener, headers);
    
            return Mono.empty();
          }).subscribe();
    
    
        }
      };
    }
    
    publicclientcallinterceptcall(MethodDescriptor方法、CallOptions、CallOptions、channelnext){
    final ClientCall call=next.newCall(方法,callOptions);
    返回新的ForwardingClientCall.SimpleForwardingClientCall(调用){
    @凌驾
    公共void开始(侦听器响应侦听器、元数据头){
    Mono.defercontext(上下文->{
    Metadata.KeyKey=
    Metadata.Key.of(“CONTEXT-HEADER”,Metadata.ASCII\u STRING\u MARSHALLER);
    headers.put(key,context.get(“context-HEADER”);
    委托().start(responseListener,headers);
    返回Mono.empty();
    }).subscribe();
    }
    };
    }
    
    但我犯了个错误

    reactor.core.Exceptions$ErrorCallbackNotImplemented:java.util.NoTouchElementException:Context不包含键Context-HEADER


    试图理解为什么反应堆管道在这里断裂

    grpc和反应堆彼此不了解,因此必须有人翻译两者之间的上下文信息。您当前的方法是推荐的方法,即使考虑到Sleuth(因为
    上下文的全面自动传播是一个混合包,对性能有很大影响)。这是针对您的确切需要,万无一失,所以我会保留它。

    我想出了另一个解决方案,不需要WebFilter或reactor上下文。但我还是更喜欢用某种方式来实现“全面的自动传播”

    在这个解决方案中,我只需将注入控制器方法的ServerWebExchange传递到一个客户端拦截器中,该拦截器将读取头

    @GetMapping
    public Mono<String> testHeaderPropagation(ServerWebExchange exchange) throws Exception {
      MyGrpcStub grpcStubWithInterceptor = attachMetadata(grpcStub, exchange);
      Response response = grpcStub
        .call(request);
      return Mono.just(response.getMessage());
    }
    
    public static <S extends AbstractStub<S>> S attachMetadata(S stub, ServerWebExchange exchange) {
      return stub.withInterceptors(new GrpcClientInterceptor(exchange));
    }
    
    @GetMapping
    公共Mono testHeaderPropagation(ServerWebExchange)引发异常{
    MyGrpcStub grpcStubWithInterceptor=附件元数据(grpcStub,exchange);
    响应=grpcStub
    .电话(请求);
    返回Mono.just(response.getMessage());
    }
    公共静态S attachMetadata(S存根,服务器WebExchange){
    带有拦截器的返回存根(新GRPCclientReceptor(exchange));
    }
    
    然后我的新GRPCclientReceptor

    grpcStub.call(request)
    
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
      HttpHeaders httpHeaders = exchange.getRequest().getHeaders();
    
      final ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);
      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
        @Override
        public void start(Listener<RespT> responseListener, Metadata headers) {
          Metadata.Key < String > key =
              Metadata.Key.of("my-header", Metadata.ASCII_STRING_MARSHALLER);
          headers.put(key, httpHeaders.getFirst("my-header"));
    
          delegate().start(responseListener, headers);
        }
      };
    }
    
    publicclientcallinterceptcall(MethodDescriptor方法、CallOptions、CallOptions、channelnext){
    HttpHeaders HttpHeaders=exchange.getRequest().getHeaders();
    final ClientCall call=next.newCall(方法,callOptions);
    返回新的ForwardingClientCall.SimpleForwardingClientCall(调用){
    @凌驾
    公共void开始(侦听器响应侦听器、元数据头){
    Metadata.KeyKey=
    Metadata.Key.of(“我的头”,Metadata.ASCII\u字符串\u封送器);
    headers.put(key,httpHeaders.getFirst(“我的头”);
    委托().start(responseListener,headers);
    }
    };
    }
    
    为了论证,您将如何创建“全面自动传播上下文”?你能提供一些代码让我评估一下吗?我找到了你的博客。很不错的!也许你可以写一篇关于这件事的博客(以及为什么它不好),我甚至不确定这是否可能。从grpc到reactor的转换是在控制器中进行的,因此除了为开发人员提供帮助之外,您没有什么可以做的。此外,我怀疑您的代码可能存在严重错误:is
    grpcStub.call
    阻塞?这似乎是解决此类问题的关键是,您的第二次尝试无法工作,因为
    上下文在订阅时初始化。在这里调用
    subscribe
    时,您没有提供上下文,因此它是空的。
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
      HttpHeaders httpHeaders = exchange.getRequest().getHeaders();
    
      final ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);
      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
        @Override
        public void start(Listener<RespT> responseListener, Metadata headers) {
          Metadata.Key < String > key =
              Metadata.Key.of("my-header", Metadata.ASCII_STRING_MARSHALLER);
          headers.put(key, httpHeaders.getFirst("my-header"));
    
          delegate().start(responseListener, headers);
        }
      };
    }