乐趣区

关于grpc:聊一聊-gRPC-中的拦截器

明天咱们持续 gRPC 系列。

后面松哥跟大家聊了 gRPC 的简略案例,也说了四种不同的通信模式,感兴趣的小伙伴能够戳这里:

  1. 一个简略的案例入门 gRPC
  2. 聊一聊 gRPC 的四种通信模式

明天咱们来持续聊一聊 gRPC 中的拦截器。

有申请的发送、解决,当然就会有拦截器的需要,例如在服务端通过拦截器对立进行申请认证等操作,这些就须要拦截器来实现,明天松哥先和小伙伴们来聊一聊 gRPC 中拦截器的根本用法,前面我再整一篇文章和小伙伴们做一个基于拦截器实现的 JWT 认证的 gRPC。

gRPC 中的拦截器整体上来说能够分为两大类:

  1. 服务端拦截器
  2. 客户端拦截器

咱们别离来看。

1. 服务端拦截器

服务端拦截器的作用有点像咱们 Java 中的 Filter,服务端拦截器又能够持续细分为 一元拦截器 流拦截器

一元拦截器对应咱们上篇文章中所讲的一元 RPC,也就是一次申请,一次响应这种状况。

流拦截器则对应咱们上篇文章中所讲的服务端流 RPC、客户端流 RPC 以及双向流 RPC。

不过,在 Java 代码中,无论是一元拦截器还是流拦截器,代码其实都是一样的。不过如果你是用 Go 实现的 gRPC,那么这块是不一样的。

所以接下来的内容我就不去辨别一元拦截器和流拦截器了,咱们间接来看一个服务端拦截器的例子。

这里我就不从头开始写了,咱们间接在上篇文章的根底之上持续增加拦截器即可。

服务端拦截器工作地位大抵如下:

从这张图中小伙伴们能够看到,咱们能够在服务端解决申请之前将申请拦挡下来,对立进行权限校验等操作,也能够在服务端将申请处理完毕之后,筹备响应的时候将响应拦挡下来,能够对响应进行二次解决。

首先咱们来看申请拦截器,实际上是一个监听器:

public class BookServiceCallListener<R> extends ForwardingServerCallListener<R> {
    private final ServerCall.Listener<R> delegate;

    public BookServiceCallListener(ServerCall.Listener<R> delegate) {this.delegate = delegate;}

    @Override
    protected ServerCall.Listener<R> delegate() {return delegate;}

    @Override
    public void onMessage(R message) {System.out.println("这是客户端发来的音讯,能够在这里进行预处理:"+message);
        super.onMessage(message);
    }
}

这里咱们自定义一个类,继承自 ForwardingServerCallListener 类,在这里重写 onMessage 办法,当有申请达到的时候,就会通过这里的 onMessage 办法。如果咱们须要对传入的参数进行验证等操作,就能够在这里实现。

再来看看响应拦截器:

public class BookServiceCall<ReqT,RespT> extends ForwardingServerCall.SimpleForwardingServerCall<ReqT,RespT> {protected BookServiceCall(ServerCall<ReqT, RespT> delegate) {super(delegate);
    }

    @Override
    protected ServerCall<ReqT, RespT> delegate() {return super.delegate();
    }

    @Override
    public MethodDescriptor<ReqT, RespT> getMethodDescriptor() {return super.getMethodDescriptor();
    }

    @Override
    public void sendMessage(RespT message) {System.out.println("这是服务端返回给客户端的音讯:"+message);
        super.sendMessage(message);
    }
}

小伙伴们可能发现了,我这里用到了很多泛型,申请类型和响应类型都不倡议指定具体类型,因为拦截器可能会拦挡多种类型的申请,申请参数和响应的数据类型都不肯定一样。

这里是重写 sendMessage 办法,在这个办法中咱们能够对服务端筹备返回给客户端的音讯进行预处理。

所以这个地位就相当于 响应拦截器

最初,咱们须要在启动服务的时候,将这两个拦截器配置进去,代码如下:

public void start() throws IOException {
    int port = 50051;
    server = ServerBuilder.forPort(port)
            .addService(ServerInterceptors.intercept(new BookServiceImpl(), new ServerInterceptor() {
                @Override
                public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {String fullMethodName = call.getMethodDescriptor().getFullMethodName();
                    System.out.println(fullMethodName + ":pre");
                    Set<String> keys = headers.keys();
                    for (String key : keys) {System.out.println(key + ">>>" + headers.get(Metadata.Key.of(key, ASCII_STRING_MARSHALLER)));
                    }
                    return new BookServiceCallListener<>(next.startCall(new BookServiceCall(call), headers));
                }
            }))
            .build()
            .start();
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {BookServiceServer.this.stop();
    }));
}

这是我之前服务启动的办法,以前咱们调用 addService 办法的时候,间接增加对应的服务就能够了,当初,咱们除了增加之前的 BookServiceImpl 服务之外,还额定给了一个拦截器。

每当申请达到的时候,就会通过拦截器的 interceptCall 办法,这个办法有三个参数:

  • 第一个参数 call 是生产传入的 RPC 音讯的一个回调。
  • 第二个参数 headers 则是申请的音讯头,如果咱们通过 JWT 进行申请校验,那么就从这个 headers 中提取出申请的 JWT 令牌而后进行校验。
  • 第三个参数 next 就相似于咱们在 Java 过滤器 filter 中的 filterChain 一样,让这个申请持续向下走。

在这个办法中,咱们申请头的信息都打印进去给小伙伴们参考了。而后在返回值中,将咱们刚刚写的申请拦截器和响应拦截器构建并返回。

好啦,这样咱们的服务端拦截器就搞好啦~无论是一元的 RPC 音讯还是流式的 RPC 音讯,都会通过这个拦截器,响应也是一样。

2. 客户端拦截器

客户端拦截器就比较简单了,客户端拦截器能够将咱们的申请拦挡下来,例如咱们如果想为所有申请增加对立的令牌 Token,那么就能够在这里来做,形式如下:

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051)
        .usePlaintext()
        .intercept(new ClientInterceptor() {
            @Override
            public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {System.out.println("!!!!!!!!!!!!!!!!");
                callOptions = callOptions.withAuthority("javaboy");
                return next.newCall(method,callOptions);
            }
        })
        .build();
BookServiceGrpc.BookServiceStub stub = BookServiceGrpc.newStub(channel);

当咱们的申请执行的时候,这个客户端拦截器就会被触发。

3. 小结

好啦,明天就和小伙伴们简略介绍一下服务端拦截器和客户端拦截器。下篇文章,松哥会通过一个 JWT 认证来和小伙伴们演示这个拦截器的具体用法。

退出移动版