Dubbo的整套handler。。。反正我刚看的时候挺头疼。从Protocol层到Transporter层。纵深3层。从DubboProtocol构建,被逐层传递到NettyServer,然后在逐层返回。整个过程中,还被不断包裹,从同步到异步,不停变换用法和说法。总之,看得挺累,还是坚持逐个分析一遍。下面来说说。
先看一张图,是使用Dubbo Protocol并且使用Netty作为服务器的情况下Handler的整个包装过程。
解释下上面这张图
1.在DubboProtocol中构建ExchangeHandler命名requestHandler
2.在Exchange层做两次包装new DecodeHandler(new HeaderExchangeHandler(requestHandler)),具体参考类:HeaderExchanger
① 使用HeaderExchangeHandler做一次包装,HeaderExchangeHandler的作用是实现了Request和Response的概念,当接到received请求后,将请求转为reply。请参考类HeaderExchangeHandler,对这部分不熟悉的可以参考文章dubbo的exchange层
② 使用DecodeHandler做一次包装,DecodeHandler的作用是用来对Request Message和Response Message做解码操作,解码完成后才能给HeaderExchangeHandler使用。
3.在Exchange层包装后的Handler会被传递到Transporter层(NettyTransporter)并且把类型转换成ChannelHandler,因为ChannelHandler更为抽象。
4.Handler在Transporter层流转,会被传递到NettyServer中
5.在NettyServer中被AllChannelHandler包装,其作用是把NettyServer接收到的请求转移给Transporter层的线程池来处理。同步转异步。
6.Handler被再次NettyServerHandler包装,NettyServerHandler的父类是ChannelDuplexHandler。它属于Netty的Handler。由Netty来管理和调用其中的回调方法。Netty在接受到channelActive,channelRead等方法后,会把请求转移给Dubbo的Handler,这样每当请求过来,Netty的Handler接到请求就立马把数据和相关信息转交给Dubbo的Handler,由Dubbo的Handler来管理了。
上述的整个包装过程基本提现了Dubbo Protocol的Handler的转移过程。从DubboProtocol构建逐层向下传递。当Netty接到TCP请求后,调用过程又逐层向上传递。下面看下几个关键转折点的代码。
1.请参考DubboProtocol类,Dubbo Handler初始化创建的地方。
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
// ----------------------此处省略一堆代码------------------------
};
2.下面看下HeaderExchangeHandler,Request和Response概念重点提现的地方
public class HeaderExchangeHandler implements ChannelHandlerDelegate {
// ----------------------此处生路一堆代码------------------------
@Override // 接受信息
public void received(Channel channel, Object message) throws RemotingException {
channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
try {
if (message instanceof Request) {// 看看信息是不是Request
// handle request.
Request request = (Request) message;
if (request.isEvent()) {
handlerEvent(channel, request);
} else {
if (request.isTwoWay()) {// twoWay代表这个消息要回复
// 服务器端接到请求,调用handleRequest得到Response。
// 这边就提现了Request和Response的概念
// handleRequest,实际上是去做实际的业务动作了
Response response = handleRequest(exchangeChannel, request);
channel.send(response);
} else {
// 不需要返回
handler.received(exchangeChannel, request.getData());
}
}
} else if (message instanceof Response) {// 看看信息是不是Response
// 如果是Response,那么消息肯定是从Privoder方发来的。
handleResponse(channel, (Response) message);
} else if (message instanceof String) {
if (isClientSide(channel)) {
Exception e = new Exception("Dubbo client can not supported string message: " + message + " in channel: " + channel + ", url: " + channel.getUrl());
logger.error(e.getMessage(), e);
} else {
String echo = handler.telnet(channel, (String) message);
if (echo != null && echo.length() > 0) {
channel.send(echo);
}
}
} else {
handler.received(exchangeChannel, message);
}
} finally {
HeaderExchangeChannel.removeChannelIfDisconnected(channel);
}
}
// 收到Privoder方返回的Response信息,并且做出处理
static void handleResponse(Channel channel, Response response) throws RemotingException {
if (response != null && !response.isHeartbeat()) {
// 调用DefaultFuture.received来通知Response消息到了。
DefaultFuture.received(channel, response);
}
}
// 服务器端接到请求,调用handleRequest得到Response。
// 这边就提现了Request和Response的概念
// handleRequest,实际上是去做实际的业务动作了
Response handleRequest(ExchangeChannel channel, Request req) throws RemotingException {
Response res = new Response(req.getId(), req.getVersion());
if (req.isBroken()) {
Object data = req.getData();
String msg;
if (data == null) msg = null;
else if (data instanceof Throwable) msg = StringUtils.toString((Throwable) data);
else msg = data.toString();
res.setErrorMessage("Fail to decode request due to: " + msg);
res.setStatus(Response.BAD_REQUEST);
return res;
}
// find handler by message class.
Object msg = req.getData();
try {
// handle data.
// 完成实际的业务动作,也就是调用DubboProtocol.requestHandler.reply类中的实现。
// 并且返回Response信息
Object result = handler.reply(channel, msg);
res.setStatus(Response.OK);
res.setResult(result);
} catch (Throwable e) {
res.setStatus(Response.SERVICE_ERROR);
res.setErrorMessage(StringUtils.toString(e));
}
return res;
}
// ----------------------此处生路一堆代码------------------------
}
上面的代码比较多,这边总结下。received方法用于接受信息,这个方法是Provider和Consumer共用的。Provider接收的信息必然是Request,它所处的角色就类似与服务器。Consumer接收的信息必然是Response,它所处的角色就类似于客户端。当然,对于事件Event类信息另说,这边为了思路清晰,不展开细说。
角色Provider接收Request信息。然后就是做业务动作,接着就是判断是否回复Response,要看twoWay这个标识。具体再看下代码中的一些注释。追踪下需要回复Response的情况(因为大部分情况下,我们都是用同步请求,需要回复Response)
找到代码Object result = handler.reply(channel, msg);,reply动作会调用到DubboProtocol.requestHandler.reply。这个地方如果自己跟代码会比较乱。对Dubbo Handler不太熟悉可以看文章dubbo的handler机制。提醒下,看HeaderExchanger.bind动作的实现,会发现DubboProtocol.requestHandler被包起来了。所以这个地方reply一定是调用DubboProtocol.requestHandler的。看下那块代码。
@Override// 执行invoke并且返回Response信息
public Object reply(ExchangeChannel channel, Object message) throws RemotingException {
if (message instanceof Invocation) {
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
// ----------------------此处生路一堆代码------------------------
RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
// 执行invoke并且返回Response信息
return invoker.invoke(inv);
}
}
invoker.invoke(inv);执行的是服务在初始化的时候Protocol.export出来的。实际export出来的Exporter会被过滤器链给包住。这部分知识可以查看文档dubbo的filter
3.看下NettyServerHandler的源码,Netty handler服务与Dubbo handler职责交接的地方。
public class NettyServerHandler extends ChannelDuplexHandler {
// dubbo的handler
private final ChannelHandler handler;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
try {
if (channel != null) {
channels.put(NetUtils.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
}
// netty的客户端激活消息被传递给dubbo的handler,并转换概念变为connected
handler.connected(channel);
} finally {
NettyChannel.removeChannelIfDisconnected(ctx.channel());
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
try {
// netty的数据到达消息被传递给dubbo的handler,并转换概念变为received
handler.received(channel, msg);
} finally {
NettyChannel.removeChannelIfDisconnected(ctx.channel());
}
}
}
上面的类,省略打大部分代码。只是以客户端建立连接和TCP数据到达作为案例说下。其他场景也是类似。
总结
Dubbo的Handler纵深3层,包装过程较多,代码追踪比较麻烦。本文用一张示意图说了具体的包装过程。源码追踪过程要关注3个重要点位。
1.DubboProtocol类,Dubbo Handler初始化创建的地方
2.HeaderExchangeHandler类,Request和Response概念重点提现的地方
3.NettyServerHandler类,Netty Handler服务与Dubbo Handler职责交接的地方