dubbo同步/异步调用的方式
我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制;基于这种机制,Dubbo 实现了以下几种调用方式:
- 同步调用(默认)
- 异步调用
- 参数回调
- 事件通知
同步调用
同步调用是一种阻塞式的调用方式,即 Consumer 端代码一直阻塞等待,直到 Provider 端返回为止;
通常,一个典型的同步调用过程如下:
- Consumer 业务线程调用远程接口,向 Provider 发送请求,同时当前线程处于
阻塞状态; - Provider 接到 Consumer 的请求后,开始处理请求,将结果返回给 Consumer;
- Consumer 收到结果后,当前线程继续往后执行。
这里有 2 个问题:
- Consumer 业务线程是怎么进入
阻塞状态的? - Consumer 收到结果后,如何唤醒业务线程往后执行的?
其实,Dubbo 的底层 IO 操作都是异步的。Consumer 端发起调用后,得到一个 Future 对象。对于同步调用,业务线程通过Future#get(timeout),阻塞等待 Provider 端将结果返回;timeout则是 Consumer 端定义的超时时间。当结果返回后,会设置到此 Future,并唤醒阻塞的业务线程;当超时时间到结果还未返回时,业务线程将会异常返回。
异步调用
1、NIO future主动获取结果,返回结果放在RpcContext中
基于 Dubbo 底层的异步 NIO 实现异步调用,对于 Provider 响应时间较长的场景是必须的,它能有效利用 Consumer 端的资源,相对于 Consumer 端使用多线程来说开销较小。
异步调用,对于 Provider 端不需要做特别的配置。下面的例子中,Provider 端接口定义如下:
public interface AsyncService {
String goodbye(String name);
}
Consumer 配置
<dubbo:reference id="asyncService" interface="com.alibaba.dubbo.samples.async.api.AsyncService">
<dubbo:method name="goodbye" async="true"/>
</dubbo:reference>
需要异步调用的方法,均需要使用 <dubbo:method/>标签进行描述
Consumer 端发起调用
AsyncService service = ...;
String result = service.goodbye("samples");// 这里的返回值为空,请不要使用
Future<String> future = RpcContext.getContext().getFuture();
... // 业务线程可以开始做其他事情
result = future.get(); // 阻塞需要获取异步结果时,也可以使用 get(timeout, unit) 设置超时时间
Dubbo Consumer 端发起调用后,同时通过RpcContext.getContext().getFuture()获取跟返回结果关联的Future对象,然后就可以开始处理其他任务;当需要这次异步调用的结果时,可以在任意时刻通过future.get(timeout)来获取。
一些特殊场景下,为了尽快调用返回,可以设置是否等待消息发出:
sent="true"等待消息发出,消息发送失败将抛出异常;sent="false"不等待消息发出,将消息放入 IO 队列,即刻返回。
默认为false。配置方式如下:
<dubbo:method name="goodbye" async="true" sent="true" />
如果你只是想异步,完全忽略返回值,可以配置 return="false",以减少 Future 对象的创建和管理成本:
<dubbo:method name="goodbye" async="true" return="false"/>
此时,RpcContext.getContext().getFuture()将返回null。
整个异步调用的时序图如下:

此示例代码位于:https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-async
2、参数回调
参数回调有点类似于本地 Callback 机制,但 Callback 并不是 Dubbo 内部的类或接口,而是由 Provider 端自定义的;Dubbo 将基于长连接生成反向代理,从而实现从 Provider 端调用 Consumer 端的逻辑。
Provider 端定义 Service 和 Callback
public interface CallbackService {
void addListener(String key, CallbackListener listener);
}
public interface CallbackListener {
void changed(String msg);
}
Provider 端 Service 实现
public class CallbackServiceImpl implements CallbackService {
private final Map<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();
public CallbackServiceImpl() {
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
for (Map.Entry<String, CallbackListener> entry : listeners.entrySet()) {
try {
entry.getValue().changed(getChanged(entry.getKey()));
} catch (Throwable t) {
listeners.remove(entry.getKey());
}
}
Thread.sleep(5000); // timely trigger change event
} catch (Throwable t) {
t.printStackTrace();
}
}
}
});
t.setDaemon(true);
t.start();
}
public void addListener(String key, CallbackListener listener) {
listeners.put(key, listener);
listener.changed(getChanged(key)); // send notification for change
}
private String getChanged(String key) {
return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}
Provider 端暴露服务
<bean id="callbackService" class="com.alibaba.dubbo.samples.callback.impl.CallbackServiceImpl"/> <dubbo:service interface="com.alibaba.dubbo.samples.callback.api.CallbackService" ref="callbackService" connections="1" callbacks="1000">
<dubbo:method name="addListener">
<dubbo:argument index="1" callback="true"/>
<!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
</dubbo:method>
</dubbo:service>
这里,Provider 需要在方法中声明哪个参数是 Callback 参数。
Consumer 端实现 Callback 接口
CallbackService callbackService = ...;
callbackService.addListener("foo.bar", new CallbackListener() {
public void changed(String msg) {
System.out.println("callback1:" + msg);
}
});
Callback 接口的实现类在 Consumer 端,当方法发生调用时,Consumer 端会自动 export 一个 Callback 服务。而 Provider 端在处理调用时,判断如果参数是 Callback,则生成了一个 proxy,因此服务实现类里在调用 Callback 方法的时候,会被传递到 Consumer 端执行 Callback 实现类的代码。
上述示例代码位于:https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-callback
这种调用方式有点像消息的发布和订阅,但又有区别。比如当 Consumer 端 完成了Callback 服务的 export 后,如果后续重启了,这时 Provider 端就会调不通;同时 Provider 端如何清理掉这个 proxy 也是一个问题。
3、事件通知
事件通知允许 Consumer 端在调用之前、调用之后或出现异常时,触发 oninvoke、onreturn、onthrow 三个事件。
可以通过在配置 Consumer 时,指定事件需要通知的方法,如:
<bean id="demoCallback" class="com.alibaba.dubbo.samples.notify.impl.NotifyImpl" /> <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.samples.notify.api.DemoService" version="1.0.0" group="cn">
<dubbo:method name="sayHello" onreturn="demoCallback.onreturn" onthrow="demoCallback.onthrow"/>
</dubbo:reference>
其中,NotifyImpl 的代码如下:
public class NotifyImpl implements Notify{
public Map<Integer, String> ret = new HashMap<Integer, String>();
public void onreturn(String name, int id) {
ret.put(id, name);
System.out.println("onreturn: " + name);
}
public void onthrow(Throwable ex, String name, int id) {
System.out.println("onthrow: " + name);
}
}
这里要强调一点,自定义 Notify 接口中的三个方法的参数规则如下:
oninvoke方法参数与调用方法的参数相同;onreturn方法第一个参数为调用方法的返回值,其余为调用方法的参数;onthrow方法第一个参数为调用异常,其余为调用方法的参数。
上述配置中,sayHello方法为同步调用,因此事件通知方法的执行也是同步执行。可以配置 async=true让方法调用为异步,这时事件通知的方法也是异步执行的。特别强调一下,oninvoke方法不管是否异步调用,都是同步执行的。
事件通知的示例代码请参考:https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-notify
dubbo同步/异步调用的方式的更多相关文章
- Dubbo消费者异步调用Future使用
Dubbo的四大组件工作原理图,其中消费者调用提供者采用的是同步调用方式.消费者对于提供者的调用,也可以采用异步方式进行调用.异步调用一般应用于提供者提供的是耗时性IO服务 一.Future异步执行原 ...
- dubbo之异步调用
异步调用 基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小. 在 consumer.xml 中配置: <dubbo:reference ...
- .Net下的MSMQ(微软消息队列)的同步异步调用
一.MSMQ简介 MSMQ(微软消息队列)是Windows操作系统中消息应用程序的基础,是用于创建分布式.松散连接的消息通讯应用程序的开发工具.消息队列 和电子邮件有着很多相似处,他们都包含多个属性, ...
- jQuery同步/异步调用后台方法
$.ajax({ type: "Post", url: "UserManage.aspx/SubmitPage",//页面/方法名 data: "{' ...
- ajax 同步异步调用
- 限时购校验小工具&dubbo异步调用实现限
本文来自网易云社区 作者:张伟 背景 限时购是网易考拉目前比较常用的促销形式,但是前期创建一个限时购活动时需要各个BU按照指定的Excel格式进行选品提报,为了保证提报数据准确,运营需要人肉校验很多信 ...
- Dubbo扩展点应用之三异步调用
Dubbo不只提供了堵塞式的同步调用,同时提供了异步调用的方式.这种方式主要应用于提供者接口响应耗时明显,消费者端可以利用调用接口的时间去做一些其他的接口调用,利用Future模式来异步等待和获取结果 ...
- 循序渐进做项目系列(2):最简单的C/S程序——消息异步调用与消息同步调用
上篇博客 循序渐进做项目系列(1):最简单的C/S程序——让服务器来做加法 实现了一个最简单的C/S程序,即让服务器来做加法.当时为了通俗易懂采用了消息异步调用的方式.今天我们要采用消息同步调用的方式 ...
- 抓到Dubbo异步调用的小BUG,再送你一个贡献开源代码的机会
hello,大家好呀,我是小楼. 最近一个技术群有同学at我,问我是否熟悉Dubbo,这我熟啊~ 他说遇到了一个Dubbo异步调用的问题,怀疑是个BUG,提到BUG我可就不困了,说不定可以水,哦不.. ...
随机推荐
- JavaSE--java是值传递还是引用传递
引用是已定义的变量的别名. 按引用传递就是一个变量使用两个名称. 两个变量在内存中的地址都是一样的. 按值传递, 传递的是值的拷贝. C++中可以通过传引用来改变传入的参数的值.在C++中可以通过以下 ...
- Python说文解字_Python之多任务_01
Python 之 多任务: Python之多任务是现在多任务编程运用Python语言为载体的一种体现.其中涵盖:进程.线程.并发等方面的内容,以及包括近些年在大数据运算.人工智能领域运用强大的GPU运 ...
- 7.3 使用while 循环来处理列表和字典
# 7.3.1 在列表之间移动元素 # confirmed_users.py # 首先,创建一个待验证用户列表 # 和一个用于存储已验证用户的空列表 uncomfirmed_users = ['jam ...
- 6.44 以元素值为x的节点为根的子树的深度(递归实现)
题目:求二叉树中以元素值为x的节点为根的子树的深度 #include<cstdio> #include<malloc.h> typedef struct btree{ char ...
- vue项目 首页开发 part2
知识点回顾 git clone: 从远程服务器克隆一个一模一样的版本库到本地,复制的是整个版本库, 叫做clone.(clone是将一个库复制到你的本地,是一个本地从无到有的过程) 1. 创建分支 进 ...
- SpringBoot+SpringSecurity+jwt整合及初体验
原来一直使用shiro做安全框架,配置起来相当方便,正好有机会接触下SpringSecurity,学习下这个.顺道结合下jwt,把安全信息管理的问题扔给客户端, 准备 首先用的是SpringBoot, ...
- linux目录和安装目录学习
我一般会在/opt目录下创建 一个software目录,用来存放我们从官网下载的软件格式是.tar.gz文件,或者通过 wget+地址下载的.tar.gz文件 执行解压缩命令,这里以nginx举例 t ...
- rds分区实践
1.查看分区情况 SELECT PARTITION_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 'tab ...
- thinkCMF图片上传选择已上传图片
1.找到上传图片的模板页面 webuploader.html 在上传文件标签后面 添加 <li class=""><a href="#explorer& ...
- subprocess.Popen stdout重定向内容实时获取
python 打开一个新进程执行系统命令, test 执行完才能获取返回, test1 实时获取返回结果 import subprocess def test(cmd): p = subprocess ...