java 如何将数据从 grpc rpc 调用传递到服务器拦截器
How to pass data from grpc rpc call to server interceptor in java
我正在尝试使用处理完 rpc 服务器调用后的响应值来设置一些元数据。计划是使用服务器拦截器并覆盖 close
方法。
像这样:https://github.com/dconnelly/grpc-error-example/blob/master/src/main/java/example/Errors.java#L38
由于元数据值取决于响应,我需要一些方法将数据从 rpc 服务器调用传递到服务器拦截器或访问拦截器的响应
在 Golang 中,元数据可以在处理后的 rpc 调用 grpc.SetTrailer
中轻松设置,但在 java 中无法在 rpc 调用中设置。所以我正在尝试使用服务器拦截器。
有人可以帮忙吗?
您可以为此使用 grpc-java 的 Context
。
在拦截器中,您附加一个 Context
和一个包含可变引用的自定义键。然后在调用中再次访问 header 并从中提取值。
public static final Context.Key<TrailerHolder> TRAILER_HOLDER_KEY = Context.key("trailerHolder");
Context context = Context.current().withValue(TRAILER_HOLDER_KEY, new TrailerHolder());
Context previousContext = context.attach();
[...]
context.detach(previousContext);
您可以像这样访问上下文值:
TrailerHolder trailerHolder = TRAILER_HOLDER_KEY.get();
您可能希望实现类似于此方法的代码:
Contexts#interceptCall(Context, ServerCall, Metadata, ServerCallHandler)
编辑:
import io.grpc.Context;
import io.grpc.ForwardingServerCall.SimpleForwardingServerCall;
import io.grpc.ForwardingServerCallListener;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
public class TrailerServerInterceptor implements ServerInterceptor {
public static final Context.Key<Metadata> TRAILER_HOLDER_KEY = Context.key("trailerHolder");
@Override
public <ReqT, RespT> Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call, final Metadata headers,
final ServerCallHandler<ReqT, RespT> next) {
final TrailerCall<ReqT, RespT> call2 = new TrailerCall<>(call);
final Context context = Context.current().withValue(TRAILER_HOLDER_KEY, new Metadata());
final Context previousContext = context.attach();
try {
return new TrailerListener<>(next.startCall(call2, headers), context);
} finally {
context.detach(previousContext);
}
}
private class TrailerCall<ReqT, RespT> extends SimpleForwardingServerCall<ReqT, RespT> {
public TrailerCall(final ServerCall<ReqT, RespT> delegate) {
super(delegate);
}
@Override
public void close(final Status status, final Metadata trailers) {
trailers.merge(TRAILER_HOLDER_KEY.get());
super.close(status, trailers);
}
}
private class TrailerListener<ReqT> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
private final Context context;
public TrailerListener(final ServerCall.Listener<ReqT> delegate, final Context context) {
super(delegate);
this.context = context;
}
@Override
public void onMessage(final ReqT message) {
final Context previous = this.context.attach();
try {
super.onMessage(message);
} finally {
this.context.detach(previous);
}
}
@Override
public void onHalfClose() {
final Context previous = this.context.attach();
try {
super.onHalfClose();
} finally {
this.context.detach(previous);
}
}
@Override
public void onCancel() {
final Context previous = this.context.attach();
try {
super.onCancel();
} finally {
this.context.detach(previous);
}
}
@Override
public void onComplete() {
final Context previous = this.context.attach();
try {
super.onComplete();
} finally {
this.context.detach(previous);
}
}
@Override
public void onReady() {
final Context previous = this.context.attach();
try {
super.onReady();
} finally {
this.context.detach(previous);
}
}
}
}
在您的 grpc 服务方法中,您可以简单地使用 TRAILER_HOLDER_KEY.get().put(...)
我正在尝试使用处理完 rpc 服务器调用后的响应值来设置一些元数据。计划是使用服务器拦截器并覆盖 close
方法。
像这样:https://github.com/dconnelly/grpc-error-example/blob/master/src/main/java/example/Errors.java#L38
由于元数据值取决于响应,我需要一些方法将数据从 rpc 服务器调用传递到服务器拦截器或访问拦截器的响应
在 Golang 中,元数据可以在处理后的 rpc 调用 grpc.SetTrailer
中轻松设置,但在 java 中无法在 rpc 调用中设置。所以我正在尝试使用服务器拦截器。
有人可以帮忙吗?
您可以为此使用 grpc-java 的 Context
。
在拦截器中,您附加一个 Context
和一个包含可变引用的自定义键。然后在调用中再次访问 header 并从中提取值。
public static final Context.Key<TrailerHolder> TRAILER_HOLDER_KEY = Context.key("trailerHolder");
Context context = Context.current().withValue(TRAILER_HOLDER_KEY, new TrailerHolder());
Context previousContext = context.attach();
[...]
context.detach(previousContext);
您可以像这样访问上下文值:
TrailerHolder trailerHolder = TRAILER_HOLDER_KEY.get();
您可能希望实现类似于此方法的代码:
Contexts#interceptCall(Context, ServerCall, Metadata, ServerCallHandler)
编辑:
import io.grpc.Context;
import io.grpc.ForwardingServerCall.SimpleForwardingServerCall;
import io.grpc.ForwardingServerCallListener;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
public class TrailerServerInterceptor implements ServerInterceptor {
public static final Context.Key<Metadata> TRAILER_HOLDER_KEY = Context.key("trailerHolder");
@Override
public <ReqT, RespT> Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call, final Metadata headers,
final ServerCallHandler<ReqT, RespT> next) {
final TrailerCall<ReqT, RespT> call2 = new TrailerCall<>(call);
final Context context = Context.current().withValue(TRAILER_HOLDER_KEY, new Metadata());
final Context previousContext = context.attach();
try {
return new TrailerListener<>(next.startCall(call2, headers), context);
} finally {
context.detach(previousContext);
}
}
private class TrailerCall<ReqT, RespT> extends SimpleForwardingServerCall<ReqT, RespT> {
public TrailerCall(final ServerCall<ReqT, RespT> delegate) {
super(delegate);
}
@Override
public void close(final Status status, final Metadata trailers) {
trailers.merge(TRAILER_HOLDER_KEY.get());
super.close(status, trailers);
}
}
private class TrailerListener<ReqT> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
private final Context context;
public TrailerListener(final ServerCall.Listener<ReqT> delegate, final Context context) {
super(delegate);
this.context = context;
}
@Override
public void onMessage(final ReqT message) {
final Context previous = this.context.attach();
try {
super.onMessage(message);
} finally {
this.context.detach(previous);
}
}
@Override
public void onHalfClose() {
final Context previous = this.context.attach();
try {
super.onHalfClose();
} finally {
this.context.detach(previous);
}
}
@Override
public void onCancel() {
final Context previous = this.context.attach();
try {
super.onCancel();
} finally {
this.context.detach(previous);
}
}
@Override
public void onComplete() {
final Context previous = this.context.attach();
try {
super.onComplete();
} finally {
this.context.detach(previous);
}
}
@Override
public void onReady() {
final Context previous = this.context.attach();
try {
super.onReady();
} finally {
this.context.detach(previous);
}
}
}
}
在您的 grpc 服务方法中,您可以简单地使用 TRAILER_HOLDER_KEY.get().put(...)