我们知道,Dubbo 缺省协议采用单一长连接,底层实现是 Netty 的 NIO 异步通讯机制;基于这种机制,Dubbo 实现了以下几种调用方式:

  • 同步调用
  • 异步调用
  • 参数回调
  • 事件通知

同步调用

同步调用是一种阻塞式的调用方式,即 Consumer 端代码一直阻塞等待,直到 Provider 端返回为止;

通常,一个典型的同步调用过程如下:

  1. Consumer 业务线程调用远程接口,向 Provider 发送请求,同时当前线程处于阻塞状态;
  2. Provider 接到 Consumer 的请求后,开始处理请求,将结果返回给 Consumer;
  3. Consumer 收到结果后,当前线程继续往后执行。

这里有 2 个问题:

  1. Consumer 业务线程是怎么进入阻塞状态的?
  2. Consumer 收到结果后,如何唤醒业务线程往后执行的?

其实,Dubbo 的底层 IO 操作都是异步的。Consumer 端发起调用后,得到一个 Future 对象。对于同步调用,业务线程通过Future#get(timeout),阻塞等待 Provider 端将结果返回;timeout则是 Consumer 端定义的超时时间。当结果返回后,会设置到此 Future,并唤醒阻塞的业务线程;当超时时间到结果还未返回时,业务线程将会异常返回。

异步调用

基于 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

参数回调

参数回调有点类似于本地 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 也是一个问题。

事件通知

事件通知允许 Consumer 端在调用之前、调用之后或出现异常时,触发 oninvokeonreturnonthrow 三个事件。

可以通过在配置 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

delphi 异步 调用 带参数_Dubbo 关于同步/异步调用的几种方式相关推荐

  1. ASP调用带参数存储过程的几种方式

    最近有很多的朋友问到调用存储过程的问题,这里简单介绍几种ASP调用带参数存储过程的方法. 1. 这也是最简单的方法,两个输入参数,无返回值: set connection = server.creat ...

  2. C#线程调用带参数的方法 ~

    在 .NET Framework 2.0 版中,要实现线程调用带参数的方法有两种办法. 第一种:使用ParameterizedThreadStart. 调用 System.Threading.Thre ...

  3. 使用SqlDataSource调用带参数存储过程(获取不到数据?)

    最近被朋友问到一个SqlDataSource调用带参数存储过程为什么不成功,代码如下: string user_name = ((TextBox)this.DetailsView1.Rows[1].C ...

  4. ASP调用带参数存储过程的几种方式 (转)

    最近有很多的朋友问到调用存储过程的问题,这里简单介绍几种ASP调用带参数存储过程的方法. 1. 这也是最简单的方法,两个输入参数,无返回值: set connection = server.creat ...

  5. Ajax的异步同步原理以及js几种方式的实现

    Ajax的异步同步原理以及js几种方式的实现的个人详细解析 Ajax简介 Ajax的全称是:Asynchronous JavaScript And XML,指的是异步 JavaScript 及 XML ...

  6. delphi 异步 调用 带参数_如何在 Spring 异步调用中传递上下文

    什么是异步调用? 异步调用是相对于同步调用而言的,同步调用是指程序按预定顺序一步步执行,每一步必须等到上一步执行完后才能执行,异步调用则无需等待上一步程序执行完即可执行.异步调用指,在程序在执行时,无 ...

  7. ibatis教程之调用带参数的存储过程

    ibatis调用带参的存储过程,包括存储过程带有多个输入输出参数,整个开发过程如下: 1. 编写存储过程,以下存储过程分别带有三个输入参数,两个输出参数. CREATE PROCEDURE [dbo] ...

  8. Jquery ajax 访问调用带参数的服务方法!

    页面脚本中的写法: $.ajax({url: "http://localhost:3510/WebSite/WebService/ExceptionRecoder.asmx/SetExcep ...

  9. c# 多线程 调用带参数函数

    线程操作主要用到Thread类,他是定义在System.Threading.dll下.使用时需要添加这一个引用.该类提供给我们四个重载的构造函数(以下引自msdn). Thread (Paramete ...

最新文章

  1. mybatis-generator修改源码2
  2. Word自定义多级符号方法
  3. vue-cli2.0创建项目步骤
  4. Facebook 对 Memcache 伸缩性的增强
  5. 在unity向量空间内绘制几何(2):计算球体的表面坐标
  6. 同一个IP段ping不通同事的电脑
  7. UnityShader7:内置包含文件UnityCG.cginc与GG/HLSL语义
  8. 2017-2018-1 20155222 《信息安全系统设计基础》课上实验2、3
  9. poj:2455 Secret Milking Machine 秘密挤奶机(二分+最大流)
  10. ALOHA协议和CSMA协议
  11. 数学三次方的计算机符号,数学符号三次方
  12. C语言内存空间分布详解
  13. 数显之家快讯:【SHIO世硕心语】2021,写给自己的几段宽心话!
  14. FileZilla的下载与安装以及简单使用(有图解超简单)
  15. LATEX 罗马数字的输入
  16. 电子表格软件能解决什么问题?
  17. Day050--jQuery表单事件 轮播图 插件库 ajax
  18. css简单样式(旋转正方形、纸片旋转、轮播图3D、简单轮播图)
  19. 二阶魔方复原算法推算-Part2
  20. solr mysql 全文搜索_全文检索Solr集成HanLP中文分词

热门文章

  1. Eva.js 渲染Web页面动画
  2. 拖拽动态生成网页-VvvebJs
  3. VC++开发学习一(MFC中的CString类的常用的方法技巧介绍)
  4. python js 效率_Python,Node.js 哪个比较适合写爬虫?
  5. mysql导入报错1071_导入sql文件报错:1071 Specified key was too long; max key length is 767 bytes...
  6. java输入其他字符提示_Eclipse输入任意字母或指定字符出现提示框
  7. rabbitmq多个消费者同时接收_提升RabbitMQ消费速度的一些实践
  8. js正则验证输入表情
  9. [JLOI 2016]成绩比较
  10. 基于最大堆实现最大优先队列【代码】