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头
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的转换是在控制器中进行的,因此除了为开发人员提供帮助之外,您没有什么可以做的。此外,我怀疑您的代码可能存在严重错误:isgrpcStub.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);
}
};
}