如何将数据从grpc rpc调用传递到Java中的服务器拦截器

时间:2019-08-06 07:15:16

标签: grpc interceptor grpc-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调用中进行设置。所以我尝试使用服务器拦截器。

有人可以帮忙吗?

1 个答案:

答案 0 :(得分:3)

您可以为此使用grpc-java的Context。 在拦截器中,将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), previousContext);
        } 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(...)