由grpc的interceptor的trace log理解client和server端处理流程

616 阅读4分钟

代码:

github.com/singgel/RPC…

interceptor原理:

一个小例子:github.com/singgel/jav…

ClientInterceptor:

github.com/singgel/Spr…

根据文档描述,作用在传输channel和本地存根stub之间,用于:

      • 向标头元数据添加凭据
      • 记录和监控呼叫行为
      • 请求和响应重写

1.针对客户端调用前的拦截

(ClientCall是针对客户端要调用的方法的)

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

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

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

 

call = channel.newCall(unaryMethod, callOptions);``call.start(listener, headers);``call.sendMessage(message);``//消息发送``call.halfClose();``//请求端关闭发送通道``call.request(``1``);``// wait for listener.onMessage()


在ClientCall的子类中有ForwardingClientCall<ReqT, RespT>,该类用于包装ClientCall,然后实现委托嵌套调用,里面方法都如下代码所示:

@Override``public void start(Listener<RespT> responseListener, Metadata headers) {``    ``delegate().start(responseListener, headers);``}``@Override``public void request(``int numMessages) {``    ``delegate().request(numMessages);``}

之所以要实现ClientInterceptor接口,因为Channel本身也是可以嵌套的类,所以创建ClientCall也是被一层一层的调用.

2.对于客户端收到的回复拦截

通过ClientCall的静态内部类Listener来实现,该Listener也是可以嵌套的,其内有如下方法:

 

public void onHeaders(Metadata headers) {}``public void onMessage(T message) {}``//消息正常到达``public void onClose(Status status, Metadata trailers) {}``//消息通道关闭``public void onReady() {}

 

 

ServerInterceptor:

github.com/singgel/Spr…

ServerCall是针对ClientCall的

根据文档描述,作用在client过来的请求被ServerCallHandler分发之前,用于:

      • 执行有效的身份验证凭据
      • 记录和监控呼叫行为
      • 将呼叫委托给其他服务器

1.针对客户端调用前的拦截

(ServerCall是针对ClientCall的,与client方向刚好相反)

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

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

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

 

// wait for listener.onMessage()``call = channel.newCall(call, headers, next);``call.sendMessage(message);``call.sendHeaders(headers);``call.close(status, trailers);}``//消息通道关闭``next.start(call, headers)

 

 

通过ClientCall的静态内部类Listener来实现,该Listener也是可以嵌套的,其内有如下方法:

 

public void onMessage(T message) {}``//请求消息到达``public void onHalfClose() {}``//发送端关闭连接的发送通道``public void onCancel() {}``public void onComplete() {}``public void onReady() {}

2.对于服务端的回复拦截

在ServerCall的子类中有ForwardingServerCall<ReqT, RespT>,该类用于包装ServerCall,然后实现委托嵌套调用,里面方法都如下代码所示:

 

public void sendMessage(RespT message) {}``//消息发送``public void request(``int numMessages) {}

处理流程:

(本图的流程是将server的异常捕获后,封装为StatusRuntimeException传递)

 

issues参考:

client:

github.com/grpc/grpc-j…

grpc.github.io/grpc-java/j…

server:

github.com/grpc/grpc-j…

grpc.github.io/grpc-java/j…

exception部分:

github.com/grpc/grpc-j…

github.com/grpc/grpc-j…

解决:

目前日志记录下:

接口方法、请求源、响应时间、traceId(traceId由xueqiu-toolbox的IDBox获取)

异常是方法的在onHalfClose()方法中捕获,因为异常的发生将会导致channel直接调用cancel,导致channel关闭:

server端异常是由grpc的java框架封装好的Status类,将异常向client端传递(由Status的枚举Code类中INTERNAL(13)标记)

client端将server的异常信息接收到,也和server一样全部记录,前后由traceId形成一条完整链路

client:

 

server:

 

备注:

之所以将server端的异常封装,是为了保证整个traceing链路完整,本次的修改目前主要是为了支持当前版本没有traceing工具时的临时策略(为此采用了Status传递方式)

方法:

代码修改依据于interceptor原理和Issues参考:

主要实现:

ClientInterceptor:

重写sendMessage和onMessage方法,在发送和接收处做记录(可用作网络问题诊断)

重写onClose方法,解析Status类,记录trace的log日志

ServerInterceptor:

重写onMessage和sendMessage方法,在发送和接收端做记录(可用作网络问题的诊断)

重写onHalfClose方法,拦截channel关闭,捕获server异常信息

压测记录:

本机压测环境:

-Xmx4g
-Xms4g
-Xmn1g
-Xss256k
-XX:+CMSClassUnloadingEnabled
-XX:PermSize=256M
-XX:MaxPermSize=512M
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+UseG1GC

 

参考资料:

skyao.io/learning-gr…

stackoverflow.com/questions/3…

moeyui.cn/%E5%BC%80%E…

doc.oschina.net/grpc?t=6013…

juejin.im/entry/59bb3…

yq.aliyun.com/articles/67…