今天继续gRPC系列。之前宋哥跟大家讲了gRPC的简单案例,也讲了四种不同的通信方式。感兴趣的朋友可以点击这里:一个简单的案例入门gRPC,说说gRPC的四种通信模式。今天继续。下面说说gRPC中的拦截器。对于请求的发送和处理,当然会有拦截器的要求。比如在服务端,使用拦截器进行统一的请求鉴权等操作。这些都需要拦截器来完成。今天宋哥先和朋友聊聊。gRPC中拦截器的基本使用,后面会和小伙伴们一起整篇文章做一个基于拦截器的JWT认证的gRPC。gRPC中的拦截器一般可以分为两类:服务端拦截器和客户端拦截器。让我们分别看看它们。1、服务端拦截器服务端拦截器的作用有点像我们Java中的Filter,服务端拦截器又可以进一步细分为一元拦截器和流拦截器。一元拦截器对应的就是我们上篇文章讲的一元RPC,即一次请求,一次响应。流量拦截器对应上一篇我们讲的服务端流RPC、客户端流RPC、双向流RPC。但是在Java代码中,不管是一元拦截器还是流拦截器,代码其实都是一样的。但是如果你用Go实现gRPC,那么这就不一样了。所以在后面的内容中,我不会区分一元拦截器和流拦截器。让我们直接看一个服务器端拦截器的例子。这里就不从头写了,在上一篇的基础上继续添加拦截器即可。服务端拦截器的工作位置大致如下:从这张图可以看出,我们可以在服务端处理请求之前拦截请求,统一进行权限校验等操作,或者处理完请求在server-side之后,当response准备好之后,response被拦截,response可以被处理两次。首先,让我们看一下请求拦截器,它实际上是一个监听器:publicBookServiceCallListener(ServerCall.Listenerdelegate){this.delegate=delegate;}@OverrideprotectedServerCall.Listenerdelegate(){返回委托;}@OverridepublicvoidonMessage(Rmessage){System.out.println("这是客户端的消息,你可以在这里进行预处理:"+message);super.onMessage(消息);}}这里我们自定义一个类,继承自ForwardingServerCallListener类,在这里重写onMessage方法,当有请求到来时,会通过这里的onMessage方法。如果我们需要验证传入的参数,我们可以在这里进行。让我们看一下响应拦截器:}@OverrideprotectedServerCalldelegate(){returnsuper.delegate();}@OverridepublicMethodDescriptorgetMethodDescriptor(){返回super.getMethodDescriptor();}@OverridepublicvoidsendMessage(RespTmessage){System.out.println("这是服务器返回给客户端的消息:"+message);super.sendMessage(消息);}}朋友们可能已经发现,我这里使用了很多泛型,不建议指定请求类型和响应类型具体类型,因为拦截器可能会拦截多种类型的请求,请求参数和响应的数据类型是不一定相同。这里就是重写sendMessage方法,在这个方法中我们可以对服务端要返回给客户端的消息进行预处理。所以这个位置相当于响应拦截器。最后,我们需要在启动服务时配置这两个拦截器,代码如下:publicvoidstart()throwsIOException{intport=50051;server=ServerBuilder.forPort(port).addService(ServerInterceptors.intercept(newBookServiceImpl(),newServerInterceptor(){@OverridepublicServerCall.ListenerinterceptCall(ServerCallcall,Metadataheaders,ServerCallHandlernext){StringfullMethodName=call.getMethodDescriptor().getFullMethodName();System.out.println(fullMethodName+":pre");Setkeys=headers.keys();}for(Stringkey:keys){System.out.println(key+">>>"+headers.get(Metadata.Key.of(key,ASCII_STRING_MARSHALLER)));}返回新的BookServiceCallListener<>(next.startCall(新书ServiceCall(调用),标题));}})).build().start();Runtime.getRuntime().addShutdownHook(newThread(()->{BookServiceServer.this.stop();}));这是我之前启动服务的方法。我们之前调用addService方法的时候,只是直接添加了对应的服务。现在,除了添加之前的BookServiceImpl服务外,我们还额外提供了一个请求到达时的拦截器,它会通过拦截器的interceptCall方法,它有三个参数:第一个参数call是一个回调来消费传入的RPC消息。第二个参数headers是请求的消息头。如果我们通过JWT验证请求,则从标头中提取请求的JWT令牌并进行验证。第三个参数next类似于我们javafilter过滤器中的filterChain,让请求继续往下走。在这个方法中,我们的请求头的信息被打印出来,供大家参考。然后在返回值中,构建并返回我们刚才写的请求拦截器和响应拦截器。好了,那么我们的服务器端拦截器就准备好了~不管是一元RPC消息还是流式RPC消息,都会通过这个拦截器,得到的响应都是一样的。2.客户端拦截器客户端拦截器比较简单。客户端拦截器可以拦截我们的请求。比如我们想为所有的请求添加一个统一的令牌Token,我们可以在这里进行。如下:ManagedChannelchannel=ManagedChannelBuilder.forAddress("localhost",50051).usePlaintext().intercept(newClientInterceptor(){@OverridepublicClientCallinterceptCall(MethodDescriptor方法,CallOptionscallOptions,Channelnext){System.out.println("!!!!!!!!!!!!!!!!");callOptions=callOptions.withAuthority("javaboy");returnnext.newCall(方法,callOptions);}}).build();BookServiceGrpc.BookServiceStubstub=BookServiceGrpc.newStub(channel);当我们的请求被执行时,这个客户端拦截器就会被触发。3.总结好了,今天就和小伙伴们简单介绍下服务端拦截器和客户端拦截器。下一篇宋大哥会通过JWT认证的方式和小伙伴们一起演示一下这个拦截器的具体用法。