上篇我们介绍了ExchangeClient初始化原理,地址如下
Dubbo源码解读-Exchangeclient初始化-CSDN博客
本文主要针Dubbo消费端调用流程中,DubboInvoker的调用流程,从dubbo源码角度进行解析。
大家可以好好仔细读一下本文。有疑问欢迎留言。
接着说明,读Dubbo源码最好是先对Spring源码有一定的了解。如果大家需要,我也可以针对Spring框架做一系列源码的解读专栏。
不过不用担心,如果需要Spring的源码知识,文章中也会进行Spring源码铺垫介绍的。
如果内容中有没描述清楚的,或者大家在阅读源代码有疑问的,欢迎留言,看到就会及时回复。
主要内容
- 关联知识介绍
- DubboInvoker的调用流程解析
- 图解调用流程
关联知识介绍
- 消费端调用流程
消费端依赖注入服务端后,当消费端请求服务端时,调用流程为:MockClusterInvoker->FailOverClusterInvoker->DubboInvokerr.doInvoke().最终回调DubboInvoker类。DubboInvoker调用前的详细流程可跳转至
https://mp.csdn.net/mp_blog/creation/editor/138048991
- ExchangeClient初始化过程。
- DubboInvoker调用流程总结和铺垫
DubboInvoker.doInvoker的调用流程解析
概括流程
MockClusterInvoker.invoke->FailOverClusterInvoker.invoke->DubboInvoker.doInvoke()
流程总结如下:
- 单工通信:消费端注解配置。method:isReturn=false;
- 不需要返回值,减少一次服务端给客户端响应的TCP通信
- 不需要创建DefaultFeature
- 异步处理:
- netty本来就是异步通信,不等待返回结果。返回ResponseFuture设置到Rpc上下文。
- 需要返回结果时,调用ResponseFuture.get()阻塞等待即可
- 双端通信(消费端分两个流程发送请求和接受服务端应答)
- 发送请求:(clinet链负责发送请求)
- headerExchangeChannel.request():
- 创建DefaultFuture类并返回,调用NettyClient发送请求.
- DefaultFuture:持有持有FUTURES:请求ID和Feature
- CHANNELS:请求ID和NettyClient。
- 调用default.get()进行阻塞等待。
- 接受应答:(Handler链负责接受请求或者应答)
- 服务端有数据返回时,回调用NettyClientHandler.channelRead()方法。
- 开始调用Handler的执行链。
- HeartbeatHandler:发送心跳应答
- AllChannelHandler:服务隔离思想,获取线程池异步执行
- DecodeHandler:编解码,Hession进行反序列化
- HeaderExchangeHandler:
- 服务端:
- 双工通信:处理请求,调用Filter执行链,最终调用Service方法。并将结果通过channel返回给客户端。
- 单工通信:直接处理请求
- 消费端:
- 根据请求ID获取对应的defaultFuture
- 设置Response
- 唤醒get方法()
跟进一步概括
- 单工通信:消费端注解配置。method:isReturn=false;
- 不需要返回值,减少一次服务端给客户端响应的TCP通信
- 不需要创建DefaultFeature
- 异步处理:
- netty本来就是异步通信,不等待返回结果。返回ResponseFuture设置到Rpc上下文。
- 需要返回结果时,调用ResponseFuture.get()阻塞等待即可
- 双端通信(消费端分两个流程发送请求和接受服务端应答)
- 发送请求:(clinet链负责发送请求)
- 调用defaultFuture.get()阻塞等待服务端返回。
- 创建DefaultFuture对象,并建立id和defaultFuture映射关系。
- 创建request对对象。包含id属性:请求流水号
- 接受应答:(Handler链负责接受请求或者应答)
- 服务端有数据返回时,回调用NettyClientHandler.channelRead()方法。
- 开始调用Handler的执行链。
- HeartbeatHandler:发送心跳应答
- AllChannelHandler:服务隔离思想,获取线程池异步执行
- DecodeHandler:编解码,Hession进行反序列化
- HeaderExchangeHandler:
- 服务端:
- 双工通信:处理请求,调用Filter执行链,最终调用Service方法。并将结果通过channel返回给客户端。
- 单工通信:直接处理请求
- 消费端:
- 根据请求ID获取对应的defaultFuture
- 设置Response
- 唤醒get方法()
详细流程(主要说明双工通信)
备注:
- return (Result) currentClient.request(inv, timeout).get()
- currentClient.request(inv, timeout)返回DefaultFeature对象。
- get()同步阻塞等待服务端响应。
- 服务端Netty通讯,有返回值的时候,服务端会回调Netty客户端的Read方法,会唤醒get等待的方法
双工通信
- currentClient.request(inv, timeout)调用Client和handler的执行链,最后返回DefaultFeature
- HeaderExchangeChannel.request():
- 创建DefaultFeature,持有两个全局变量映射关系,
- Map<Long, DefaultFuture> FUTURES:请求ID和Feature。
- Map<Long, Channel> CHANNELS:请求ID和NettyClient
- ID主要用来在Netty服务端回调回来的时候,通过ID找到对应的Feature,唤起get方法()。
- 静态代码块中开启守护线程。用来扫描每一次调用是否超时
- 创建DefaultFeature,持有两个全局变量映射关系,
- 调用NettyClient.send()发送请求
- 调用NettyChannel.send()
- NioSocketChannel.writeAndFlush(message):nettyApi,netty的Nio远程调用
- 发送数据到此结束,开始等待接收数据
- Netty回调write方法(调用Handler处理服务端响应)
- NettyClientHandler.channelRead():服务端有数据返回时,会调用此方法。服务端回传对象里面会包含请求ID。
- handler.received(channel, msg);
- MultiMessageHandler.received()
- HeartbeatHandler.received():服务端/客户端共用
- 设置这次心跳读的时间:nettyChannel中设置一个
- channel.setAttribute(KEY_READ_TIMESTAMP, System.currentTimeMillis());
- 如果是客户端:发送心跳应答。
- AllChannelHandler.received()
- 服务隔离思想,获取一个线程池
- ChannelEventRunnable.run()
- state==recieved
- 调用DecodeHandler
- state==recieved
- DecodeHandler.received():实现请求/响应编解码
- 返回解码数据
- DecodeRpcResult.decode()
- 获取序列化器Serialization,默认Hession。
- 反序列化
- 结果写入RpcResult的value
- 响应结果转为map写入AttachMent
- HeaderExchangeHandler.receive():【核心】DefaultFuture.receive()
- 如果是服务端收到消息(服务端)
- 双工通信:
- 处理请求:
- 创建根据请求ID创建相应对象Response
- 调用DubboProtocol中匿名ExchangeHandlerAdapter.reply()
- 根据映射关系找到DubboExporter,获取包装的Invoker[Invoker执行链接]
- 执行链:Filter1-》Filter2-〉invokerDelegete->AbstractProxyInvoker->wrapper->ServiceClass(被代理类)。
- 将请求结果,通过channel发送给客户端
- 处理请求:
- 单工通信:
- 直接处理消息
- 双工通信:
- 如果是客户端收到消息
- 设置response到DefaultFuture的全局变量,并唤醒get中的await
- DefaultFuture.receive():
- 根据ID找到对应请求对应的defaultFuture
- defaultFuture.doReceive():设置Response全局变量,唤醒get中的await。此时get()方法解除阻塞,获取到Response返回值。
- 如果配置了回调,则调用回调。(客户端配置了回调通知,则回调客户端配置的API,实际就是调用客户端配置的某个类的某个方法)
- 如果是服务端收到消息(服务端)
- 设置这次心跳读的时间:nettyChannel中设置一个
源码分析
发送请求
1.DubboInvoker.doInvoke
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
//@Method(name = "doKill",isReturn = false) 这样配置就是单工通讯
if (isOneway) {
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
//@Reference(check = false,methods = {@Method(name = "asynctoDo",async = true)}) 异步调用
} else if (isAsync) {
ResponseFuture future = currentClient.request(inv, timeout);
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {
//双工同步通讯
RpcContext.getContext().setFuture(null);
//get方法阻塞等待
return (Result) currentClient.request(inv, timeout).get();
}
}
}
2.HanderExchangeChannel.reqeust
public ResponseFuture request(Object request, int timeout) throws RemotingException {
if (closed) {
throw new RemotingException(this.getLocalAddress(), null, "Failed to send request " + request + ", cause: The channel " + this + " is closed!");
}
// create request.
//创建请求对象,里面包含ID流水号,服务端响应时候也会回传这个id
Request req = new Request();
req.setVersion(Version.getProtocolVersion());
req.setTwoWay(true);
req.setData(request);
//创建defaultFuture对象。构造函数里会简历全局ID和future映射关系。
DefaultFuture future = new DefaultFuture(channel, req, timeout);
try {
//发送请求
channel.send(req);
} catch (RemotingException e) {
future.cancel();
throw e;
}
return future;
}
3.DefaultFuture构造函数
public DefaultFuture(Channel channel, Request request, int timeout) {
this.channel = channel;
this.request = request;
this.id = request.getId();
this.timeout = timeout > 0 ? timeout : channel.getUrl().getPositiveParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
// put into waiting map.
//建立全局映射关系
FUTURES.put(id, this);
CHANNELS.put(id, channel);
}
接受响应
入口时NettyClientHandler.channelRead()方法。调用Handler的执行链(下面会有截图说明这个执行链)。 源码主要介绍核心处理
1.HeaderExchangeHandler.receive()
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) {
// handle request.
Request request = (Request) message;
if (request.isEvent()) {
handlerEvent(channel, request);
} else {
//如果双工通讯,则需要返回值
if (request.isTwoWay()) {
//这里会调用接口的具体方法
Response response = handleRequest(exchangeChannel, request);
//把消息走netty发送到客户端
channel.send(response);
} else {
//如果是单工通讯
handler.received(exchangeChannel, request.getData());
}
}
//如果是客户端接收到服务端的响应消息
} else if (message instanceof Response) {
//设置response到DefaultFuture的全局变量,并唤醒get中的await
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);
}
}
static void handleResponse(Channel channel, Response response) throws RemotingException {
if (response != null && !response.isHeartbeat()) {
DefaultFuture.received(channel, response);
}
}
2.Default.received():设置相应结果response,唤醒get方法。
public static void received(Channel channel, Response response) {
try {
//找到该请求对应的DefaultFuture对象
DefaultFuture future = FUTURES.remove(response.getId());
if (future != null) {
//设置response全局变量,并缓存get方法
future.doReceived(response);
} else {
logger.warn("The timeout response finally returned at "
+ (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
+ ", response " + response
+ (channel == null ? "" : ", channel: " + channel.getLocalAddress()
+ " -> " + channel.getRemoteAddress()));
}
} finally {
CHANNELS.remove(response.getId());
}
}
private void doReceived(Response res) {
lock.lock();
try {
response = res;
if (done != null) {
//唤醒get方法中的await
done.signal();
}
} finally {
lock.unlock();
}
if (callback != null) {
//如果配置了回调,则调用回调
invokeCallback(callback);
}
}
图解调用流程
总结:上面内容中,每个从业务流程和源码角度进行了详细分析,如果大家有疑问或者对文章排版任何方面有建议都可以留言评论,看到都会及时回复大家。
知识总结,分享不易,全文手敲,欢迎大家关注点赞评论收藏。
当然如果觉得文章写的不错,也可点击大赏按钮,深深的鼓励一下博主。哈哈!!!!