grpc java 泛型_gRPC学习记录(五)-拦截器分析 - Java 技术驿站-Java 技术驿站

本文介绍了gRPC Java中的拦截器机制,通过一个简单的例子展示了如何使用拦截器来添加额外的功能,例如在客户端添加token到header。文章详细分析了`ClientCall`的调用流程,并给出了客户端和服务器端拦截器的实现,强调了拦截器在处理请求前后的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于此类调用拦截器是必不可少的,本篇就分析下拦截器的实现.(博主本来想分析源码的,但是水平不足,并发知识欠缺,看的不是很懂,哎,仍需努力),另外貌似不同版本有些差异,这里使用的是1.0.3版本.

1.一个拦截器的小例子

在分析之前先看一种设计.

有一个接口如下:

/**

* 主调用接口

*/

public abstract class Client {

public abstract void start(String say);

}

/**

* 上述接口实现类

*/

public class ClientImp extends Client {

@Override

public void start(String say) {

System.out.println(say);

}

}

对此接口相关的转换器:

/**

* 用于包装Client到另一个Client

*/

public abstract class ForwardingClient extends Client{

//要包装的对象

protected abstract Client delegate();

@Override

public void start(String say) {

delegate().start(say);

}

}

/**

* 一个简单的包装实现类,必须要传入要包装的对象

*/

public class ForwardingClientImpl extends ForwardingClient{

//被委托对象

private final Client client;

public ForwardingClientImpl(Client client) {

this.client = client;

}

@Override

protected Client delegate() {

return client;

}

}

然后在下列方法中调用:

public class InterceptTest {

public static void main(String[] args) {

Client client = new ClientImp();//主要想执行的方法

//构造第一个拦截器

Client intercept1 = new ForwardingClientImpl(client){

@Override

public void start(String say) {

System.out.println("拦截器1");

super.start(say);

}

};

//构造第二个拦截器

Client intercept2 = new ForwardingClientImpl(intercept1){

@Override

public void start(String say) {

System.out.println("拦截器2");

super.start(say);

}

};

//执行主方法

intercept2.start("这是要执行的方法");

}

}

毫无疑问会输出

拦截器2

拦截器1

这是要执行的方法

分析一下针对Client接口,通过ForwardingClient可以实现自身的嵌套调用,从而达到了类似拦截器的效果.在gRPC中有很多类似的嵌套类,其本质和上面差不多,上面例子有助于对gRPC拦截器的掌握.

2.gRPC的ClientCall

该抽象类就是用来调用远程方法的,实现了发送消息和接收消息的功能,该接口由两个泛型ReqT和ReqT,分别对应着请求发送的信息,和请求收到的回复.

ClientCall抽象类主要有两个部分组成,一是public abstract static class Listener用于监听服务端回复的消息,另一部分是针对客户端请求调用的一系列过程,如下代码流程所示:

该类中方法都是抽象方法,规定了整个调用顺序,如下:

call = channel.newCall(unaryMethod, callOptions);

call.start(listener, headers);

call.sendMessage(message);

call.halfClose();

call.request(1);

// wait for listener.onMessage()

在ClientCall的子类中有ForwardingClientCall,该类的作用和之前的Demo一样,用于包装ClientCall,然后实现委托嵌套调用,里面方法都如下代码所示:

@Override

public void start(Listener responseListener, Metadata headers) {

delegate().start(responseListener, headers);

}

@Override

public void request(int numMessages) {

delegate().request(numMessages);

}

```

那和之前的Demo一对比,拦截器怎么使用就变得很容易了.

创建一个客户端拦截器,其中为header添加了token参数.之所以要实现`ClientInterceptor`接口,因为Channel本身也是可以嵌套的类,所以创建ClientCall也是被一层一层的调用.

/**

* 客户端拦截器

* @author Niu Li

* @date 2017/2/4

*/

//ClientInterceptor接口是针对ClientCall的创建进行拦截

public class ClientInterruptImpl implements ClientInterceptor {

@Override

public ClientCall interceptCall(MethodDescriptor method,

CallOptions callOptions, Channel next) {

//创建client

System.out.println("创建client1");

ClientCall clientCall = next.newCall(method,callOptions);

return new ForwardingClientCall() {

@Override

protected ClientCall delegate() {

return clientCall;

}

@Override

public void start(ListenerresponseListener, Metadata headers) {

System.out.println("拦截器1,在此可以对header参数进行修改");

Metadata.Keytoken = Metadata.Key.of("token",Metadata.ASCII_STRING_MARSHALLER);

headers.put(token,"123456");

super.start(responseListener, headers);

}

};

}

}

调用输出如下:

创建client1

拦截器1,在此可以对header参数进行修改

这是针对客户端调用前的拦截,对于客户端收到的回复拦截则通过ClientCall的静态内部类Listener来实现,该Listener也是可以嵌套的,其内有如下方法:

public void onHeaders(Metadata headers) {}

public void onMessage(T message) {}

public void onClose(Status status, Metadata trailers) {}

public void onReady() {}

对之前start方法改造下,让其判断返回的header中有没有传送过去的token,没有则该请求视为失败.

@Override

public void start(ListenerresponseListener, Metadata headers) {

System.out.println("拦截器1,在此可以对header参数进行修改");

Metadata.Keytoken = Metadata.Key.of("token",Metadata.ASCII_STRING_MARSHALLER);

headers.put(token,"123456");

ListenerforwardListener = new ForwardingClientCallListener.

SimpleForwardingClientCallListener(responseListener) {

@Override

public void onHeaders(Metadata headers) {

Metadata.Keytoken = Metadata.Key.of("token",Metadata.ASCII_STRING_MARSHALLER);

if (!"123456".equals(headers.get(token))){

System.out.println("返回参数无token,关闭该链接");

super.onClose(Status.DATA_LOSS,headers);

}

super.onHeaders(headers);

}

};

super.start(forwardListener, headers);

}

最后再Channel创建的时候使用`intercept(new ClientInterruptImpl())`加入拦截器这样就简单实现了客户端的拦截了.

* * *

### 3.gRPC的ServerCall

有一点要搞明白,ClientCall是针对客户端要调用的方法的,而ServerCall是针对ClientCall的.看如下例子:

public class ServerInterruptImpl implements ServerInterceptor{

@Override

public ServerCall.ListenerinterceptCall(ServerCall call,

Metadata headers, ServerCallHandler next) {

System.out.println("执行server拦截器1,获取token");

//获取客户端参数

Metadata.Keytoken = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER);

String tokenStr = headers.get(token);

if (StringUtil.isNullOrEmpty(tokenStr)){

System.out.println("未收到客户端token,关闭此连接");

call.close(Status.DATA_LOSS,headers);

}

//服务端写回参数

ServerCall serverCall = new ForwardingServerCall.SimpleForwardingServerCall(call) {

@Override

public void sendHeaders(Metadata headers) {

System.out.println("执行server拦截器2,写入token");

headers.put(token,tokenStr);

super.sendHeaders(headers);

}

};

return next.startCall(serverCall,headers);

}

}

当服务端接收到请求的时候就会打印出来如下的日志.这样就实现了服务端接手前的拦截和写回时的拦截.

执行server拦截器1,获取token

收到的信息:world:0

执行server拦截器2,写入token

```

关于更多使用还在琢磨中,目前欠缺并发知识,所以下一步打算看看并发相关的资料.

附录:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值