【问题标题】:How to pass data from grpc rpc call to server interceptor in java如何将数据从grpc rpc调用传递到java中的服务器拦截器
【发布时间】:2019-12-13 16:41:39
【问题描述】:

在处理 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 interceptor grpc-java


    【解决方案1】:

    您可以为此使用 grpc-java 的 Contexts。 在拦截器中,您附加一个 Context 和一个包含可变引用的自定义键。然后在调用中再次访问该标头并从中提取值。

    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 调用或拦截器中创建新的上下文?我想将 rpc 调用中的数据传递给拦截器。
    • 如何在我的 rpc 方法中为上下文添加一些值?
    • 添加了一个完整的例子
    • 非常感谢。这行得通。还有一个问题,Context 是线程安全的,对吧?
    • AFAICT: Contexts 是不可变的,我假设用于设置/获取当前上下文的方法是线程安全的。
    猜你喜欢
    • 2016-04-26
    • 2017-12-09
    • 2022-12-03
    • 2016-11-13
    • 2019-11-24
    • 2019-07-18
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    相关资源
    最近更新 更多