2021年3月31日星期三

Can I pass reactor Context into grpc clientInterceptor implicitly?

Here's what I want to do at a high-level

  1. Capture some http headers in a WebFilter
  2. In the Controller method, I make a grpc call
  3. I'd like to propagate the http headers as grpc Metadata headers

Currently, my working implementation is

  1. WebFilter captures http headers and writes to reactor Context
  2. Controller method extracts reactor Context and passes it into a Grpc ClientInterceptor
  3. Grpc ClientInterceptor extracts http headers from Context and injects into Grpc Metadata headers

But I'd like to avoid having the Controller method doing any work (step 2 above).

Here's an implementation but looking for a way to get the http headers to Grpc Metadata without explicitly passing them in from Controller method.

WebFilter

public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {    return chain.filter(exchange)      .contextWrite(Context.of("my-header", "header-value"));  }  

Controller method

@GetMapping  public Mono<String> testHeaderPropagation() throws Exception {    return Mono.deferContextual(reactorContext -> {      Response response = grpcStub        .withInterceptors(new GrpcClientInterceptor(reactorContext))        .call(request);      return Mono.just(response.getMessage());    });  }  

GrpcClientInterceptor

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);      }    };  }  

I'd like simplify my Controller method (remove the explicit passing in of the reactor Context into a clientInterceptor)

grpcStub.call(request)  

I believe Spring Sleuth has a way of doing this but not sure how to adapt its approach. What clever thing am I missing?

Edit

The reason I'm pushing for a version that involves minimal controller method code is because other developers will be writing the controller and methods. I want to establish a pattern that doesn't require extra wiring if possible, otherwise there's a chance someone forgets to do it or does it wrong.

Edit

Follow-up question. Instead of having the controller method pass in the Context to the clientInterceptor, I tried to get the Context inside the Grpc ClientInterceptor but that doesn't seem to work.

Here's is what I tried to do

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();          }    };  }  

But I get an error

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.util.NoSuchElementException: Context does not contain key CONTEXT-HEADER

Trying to understand why the reactor pipeline breaks here

https://stackoverflow.com/questions/66880712/can-i-pass-reactor-context-into-grpc-clientinterceptor-implicitly March 31, 2021 at 10:09AM

没有评论:

发表评论