RPC-非阻塞通信下的同步API实现原理,以Dubbo为例
Netty在Java NIO领域基本算是独占鳌头,涉及到高性能网络通信,基本都会以Netty为底层通信框架,Dubbo 也不例外。以下将以Dubbo实现为例介绍其是如何在NIO非阻塞通信基础上实现同步通信的。
Dubbo为一种RPC通信框架,提供进程间的通信,在使用dubbo协议+Netty作为传输层时,提供三种API调用方式:
- 同步接口
- 异步带回调接口
- 异步不带回调接口
同步接口适用在大部分环境,通信方式简单、可靠,客户端发起调用,等待服务端处理,调用结果同步返回。这种方式下,在高吞吐、高性能(响应时间很快)的服务接口场景中最为适用,可以减少异步带来的额外的消耗,也方便客户端做一致性保证。

异步带回调接口,用在任务处理时间较长,客户端应用线程不愿阻塞等待,而是为了提高自身处理能力希望服务端处理完成后可以异步通知应用线程。这种方式可以大大提升客户端的吞吐量,避免因为服务端的耗时问题拖死客户端。

异步不带回调接口,一些场景为了进一步提升客户端的吞吐能力,只需发起一次服务端调用,不需关系调用结果,可以使用此种通信方式。一般在不需要严格保证数据一致性或者有其他补偿措施的情况下,选用这种,可以最小化远程调用带来的性能损耗。

来看一下Dubbo是如何实现这三种API的。核心代码在com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker,如下图对应的位置,属于协议层的实现部分。为方便大家可以准确定位代码所在位置,使用截图的方式,而不是直接贴代码了。

上文描述的是三种API方式,Dubbo里面通过参数isOneway、isAsync来控制,isOneway=true表示异步不带回调,isAsync=true表示异步带回调,否则是同步API。具体是如何控制,看以下代码:

isOneway==true时,客户端send完请求后,直接return一个空结果的RpcResult;isAsync==true时,客户端发起请求,设置一个ResponseFuture,直接return一个空结果的RpcResult,接下来当服务端处理完成,客户端Netty层在收到响应后会通过Future通知应用线程;最后是同步情况下,客户端发起请求,并通过get()方法阻塞等待服务端的响应结果。
异步API情况下,结合NIO模型比较好理解是如何实现的(当然需要先了解NIO的reactor模型),接下来重点理解下,这个get()阻塞方法是如何做到基于非阻塞NIO实现同步阻塞效果。
直接进入get()方法内部。

可以看到是利用Java的锁机制实现,循环判断是否收到响应,如果收到或者等待超时则返回。done的实例对象如下:
private final Lock lock = new ReentrantLock();
private final Condition done = lock.newCondition();
使用可重入锁ReentrantLock,获取一个Condition对象在其上做await操作。这里有await操作,何时被唤醒呢,有两个条件,第一个是等待timeout超时,默认dubbo是1s,第二个就是被其他线程唤醒,即收到了服务端的响应。

signal信号一发出,上文循环检测内的await操作会立即返回,下一次isDone判断会变成true,直接跳出循环。
仔细看代码会发现,被唤醒的地方还有一个是在DefaultFuture内部有一个超时轮询检测的线程,这个线程主要是处理响应超时后触发资源回收、记录异常日志等操作。
private static class RemotingInvocationTimeoutScan implements Runnable {
public void run() {
while (true) {
try {
for (DefaultFuture future : FUTURES.values()) {
if (future == null || future.isDone()) {
continue;
}
if (System.currentTimeMillis() - future.getStartTimestamp() > future.getTimeout()) {
// create exception response.
Response timeoutResponse = new Response(future.getId());
// set timeout status.
timeoutResponse.setStatus(future.isSent() ? Response.SERVER_TIMEOUT : Response.CLIENT_TIMEOUT);
timeoutResponse.setErrorMessage(future.getTimeoutMessage(true));
// handle response.
DefaultFuture.received(future.getChannel(), timeoutResponse);
}
}
Thread.sleep(30);
} catch (Throwable e) {
logger.error("Exception when scan the timeout invocation of remoting.", e);
}
}
}
}
static {
Thread th = new Thread(new RemotingInvocationTimeoutScan(), "DubboResponseTimeoutScanTimer");
th.setDaemon(true);
th.start();
}
可能会有疑问,这个触发操作为何不直接在get()方法内部检测到超时直接调用DefaultFuture.received(Channel channel, Response response)来清理,而是要额外开启一个后台线程。
单独启动一个超时线程有两个好处:
- 提高超时精度
get()方法内部的轮询有一个timeout,每次超时唤醒的时间间隔至少是timeout时长,最差的情况可能会等待2*timeout作出超时反应。在超时轮询线程中,每隔30ms遍历检测一次,可以很大程度的提升超时精度。
2. 提升性能,降低响应时间
剥离超时处理逻辑到一个单独线程,可以减少对业务线程的时间占用,这个超时后的处理对应用来说并无直接作用,完全可以放到后台异步去处理。另外单独在一个线程中,实际上有批量处理的表现。
以上是就NIO通信基础上实现三种API调用的实现原理,或许有更多优于Dubbo的处理方式,可以拿出来讨论。
RPC-非阻塞通信下的同步API实现原理,以Dubbo为例的更多相关文章
- 【MPI学习4】MPI并行程序设计模式:非阻塞通信MPI程序设计
这一章讲了MPI非阻塞通信的原理和一些函数接口,最后再用非阻塞通信方式实现Jacobi迭代,记录学习中的一些知识. (1)阻塞通信与非阻塞通信 阻塞通信调用时,整个程序只能执行通信相关的内容,而无法执 ...
- 用Java实现非阻塞通信
用ServerSocket和Socket来编写服务器程序和客户程序,是Java网络编程的最基本的方式.这些服务器程序或客户程序在运行过程中常常会阻塞.例如当一个线程执行ServerSocket的acc ...
- 利用Python中SocketServer 实现客户端与服务器间非阻塞通信
利用SocketServer模块来实现网络客户端与服务器并发连接非阻塞通信 版权声明 本文转自:http://blog.csdn.net/cnmilan/article/details/9664823 ...
- 关于socket阻塞与非阻塞情况下的recv、send、read、write返回值---部分内容可能不确切,待讨论
1.阻塞模式与非阻塞模式下recv的返回值各代表什么意思?有没有区别?(就我目前了解阻塞与非阻塞recv返回值没有区分,都是 <0:出错,=0:连接关闭,>0接收到数据大小,特别:返回值 ...
- 【python】网络编程-SocketServer 实现客户端与服务器间非阻塞通信
利用SocketServer模块来实现网络客户端与服务器并发连接非阻塞通信.首先,先了解下SocketServer模块中可供使用的类:BaseServer:包含服务器的核心功能与混合(mix-in)类 ...
- Java NIO Socket 非阻塞通信
相对于非阻塞通信的复杂性,通常客户端并不需要使用非阻塞通信以提高性能,故这里只有服务端使用非阻塞通信方式实现 客户端: package com.test.client; import java.io. ...
- UE4 Socket多线程非阻塞通信
转自:https://blog.csdn.net/lunweiwangxi3/article/details/50468593 ue4自带的Fsocket用起来依旧不是那么的顺手,感觉超出了我的理解范 ...
- TCP非阻塞通信
一.SelectableChannel SelectableChannel支持阻塞和非阻塞模式的channel 非阻塞模式下的SelectableChannel,读写不会阻塞 SelectableCh ...
- socket异步通信-如何设置成非阻塞模式、非阻塞模式下判断connect成功(失败)、判断recv/recvfrom成功(失败)、判断send/sendto
socket异步通信-如何设置成非阻塞模式.非阻塞模式下判断connect成功(失败).判断recv/recvfrom成功(失败).判断send/sendto 博客分类: Linux Socket s ...
随机推荐
- dataTables 使用整理
官方网站:http://www.datatables.net/ 简介:DataTables是一个jQuery的表格插件.这是一个高度灵活的工具,依据的基础逐步增强,这将增加先进的互动控制,支持任何HT ...
- IEEE1588协议简介
IEEE1588协议,又称PTP(precise time protocol,精确时间协议),可以达到亚微秒级别时间同步精度,于2002年发布version1,2008年发布version2. IEE ...
- 将Maven的Web项目部署到windows的Tomcat里
这里我用的是win7和tomcat8,且tomcat8下载的是压缩包而非安装文件. 第一步:先将tomcat8安装服务,cmd里更改目录为tomcat8(即压缩包解压后的那个文件夹)的bin文件夹, ...
- Ubuntu字符界面输入密码始终提示错误 login incorrect 解决办法
方法1. 先输入用户名按enter,然后ctrl+alt+f7 切换回图形界面,然后再切换到字符界面输密码 方法2.先输入用户名按enter,然后关闭小键盘上的numlock按键再打开,再输入密码 f ...
- Akka Cluster简介与基本环境搭建
akka集群是高容错.去中心化.不存在单点故障以及不存在单点瓶颈的集群.它使用gossip协议通信以及具备故障自动检测功能. Gossip收敛 集群中每一个节点被其他节点监督(默认的最大数量为 ...
- Linux第七节随笔-中 /date / ln /
4.date link 作用:显示或设定系统的日期与时间 参数: -d<字符串> 显示字符串所指的日期与时间.字符串前后必须加上双引号. -s<字符串> 根据字符串来设置日期与 ...
- lambda表达式不使用委托(delegate) 用FUNC
lLambda不使用delegate关键字,而使用 Lambda运算符 => goes to l 1.Func<int,string> getInput = (int age ...
- DEDECMS最新5.7版在Windows下的Memcache安装
一,织梦后台后台设置进入系统后台,在[系统基本参数]下面的"性能选项"卡当中,关于memcache进行如下配置: cfg_memcache_enable : 是否启用memcach ...
- Java ArrayIndexOutOfBoundsException: Exception Hierarchy
- K:正则表达式之进阶
子表达式: 前面所介绍的关于重复匹配的知识,其重复匹配的字符只作用于紧挨着的前一个字符而言,而有时候要将一个集体(姑且用该概念进行称呼)进行重复多遍的进行匹配,则使用前面所介绍的知识,其是无法做到的. ...