dubbo事件通知机制 (2)
此文已由作者赵计刚授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
142 * 反射执行xxxService.onthrow方法:至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果
143 */
144 private void fireThrowCallback(final Invoker<?> invoker, final Invocation invocation, final Throwable exception) {
145 final Method onthrowMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_METHOD_KEY));
146 final Object onthrowInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_THROW_INSTANCE_KEY));
147
148 //onthrow callback not configured
149 if (onthrowMethod == null && onthrowInst == null) {
150 return;
151 }
152 if (onthrowMethod == null || onthrowInst == null) {
153 throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onthrow callback config , but no such " + (onthrowMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
154 }
155 if (onthrowMethod != null && !onthrowMethod.isAccessible()) {
156 onthrowMethod.setAccessible(true);
157 }
158 Class<?>[] rParaTypes = onthrowMethod.getParameterTypes();
159 if (rParaTypes[0].isAssignableFrom(exception.getClass())) {
160 try {
161 Object[] args = invocation.getArguments();
162 Object[] params;
163
164 if (rParaTypes.length > 1) {
165 // onthrow(xx, Object[]) 两个参数:第一个参数接收exception,第二个接收所有的真实请求参数
166 if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) {
167 params = new Object[2];
168 params[0] = exception;
169 params[1] = args;
170 // onthrow(xx, Object... args) 多个参数:第一个参数接收exception,后边几个接收所有的真实请求参数
171 } else {
172 params = new Object[args.length + 1];
173 params[0] = exception;
174 System.arraycopy(args, 0, params, 1, args.length);
175 }
176 } else {
177 // onthrow(xx) 只有一个参数:接收exception
178 params = new Object[]{exception};
179 }
180 onthrowMethod.invoke(onthrowInst, params);
181 } catch (Throwable e) {
182 logger.error(invocation.getMethodName() + ".call back method invoke error . callback method :" + onthrowMethod + ", url:" + invoker.getUrl(), e);
183 }
184 } else {
185 logger.error(invocation.getMethodName() + ".call back method invoke error . callback method :" + onthrowMethod + ", url:" + invoker.getUrl(), exception);
186 }
187 }
188 }
从@Activate(group = Constants.CONSUMER)来看FutureFilter只用在consumer端;不管是同步调用还是异步调用,都会走FutureFilter。
原理:
首先走oninvoke(String name)方法;
然后走sayHello(String name)
最后根据同步还是异步分别走不同的逻辑。
其中同步很简单,看sayHello(String name)的返回结果RpcResult中是否有exception对象,如果有,执行onthrow(Throwable ex, String name);如果没有执行onreturnWithoutParam(String result)。
异步的操作:由于不知道provider什么时候回执行完毕,所以要添加回调等待provider端返回结果后,再执行onthrow(Throwable ex, String name)或者onreturnWithoutParam(String result),这种模式很重要,这是统计异步方法调用时间的一种非常好的模式。
重点看一下异步!
三、异步回调模式
1 private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
2 Future<?> f = RpcContext.getContext().getFuture();
3 if (f instanceof FutureAdapter) {
4 ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
5 // 3.1 调用服务之后:设置回调ResponseCallback对象到DefaultFuture中,当provider返回响应时,执行DefaultFuture.doReceived方法,该方法会调用ResponseCallback对象的done或者caught方法
6 future.setCallback(new ResponseCallback() {
7 public void done(Object rpcResult) {
8 if (rpcResult == null) {
9 logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
10 return;
11 }
12 ///must be rpcResult
13 if (!(rpcResult instanceof Result)) {
14 logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
15 return;
16 }
17 Result result = (Result) rpcResult;
18 if (result.hasException()) {
19 fireThrowCallback(invoker, invocation, result.getException());
20 } else {
21 fireReturnCallback(invoker, invocation, result.getValue());
22 }
23 }
24
25 public void caught(Throwable exception) {
26 fireThrowCallback(invoker, invocation, exception);
27 }
28 });
29 }
30 }
上述的future对象是DefaultFuture,这里首先new了一个ResponseCallback回调函数,设置到了DefaultFuture的ResponseCallback callback属性中。来看一下DefaultFuture类:
1 private volatile Response response;
2 private volatile ResponseCallback callback;
3
4 public boolean isDone() {
5 return response != null;
6 }
7
8 public void setCallback(ResponseCallback callback) {
9 if (isDone()) {
10 invokeCallback(callback);
11 } else {
12 boolean isdone = false;
13 lock.lock();
14 try {
15 if (!isDone()) {
16 this.callback = callback;
17 } else {
18 isdone = true;
19 }
20 } finally {
21 lock.unlock();
22 }
23 if (isdone) {
24 invokeCallback(callback);
25 }
26 }
27 }
1 private void invokeCallback(ResponseCallback c) {
2 ResponseCallback callbackCopy = c;
3 if (callbackCopy == null) {
4 throw new NullPointerException("callback cannot be null.");
5 }
6 c = null;
7 Response res = response;
8 if (res == null) {
9 throw new IllegalStateException("response cannot be null. url:" + channel.getUrl());
10 }
11
12 if (res.getStatus() == Response.OK) {
13 try {
14 // 返回正常,回调ResponseCallback回调函数的done方法
15 callbackCopy.done(res.getResult());
16 } catch (Exception e) {
17 logger.error("callback invoke error .reasult:" + res.getResult() + ",url:" + channel.getUrl(), e);
18 }
19 } else if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
20 try {
21 TimeoutException te = new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
22 // 如果超时,回调ResponseCallback回调函数的caught方法
23 callbackCopy.caught(te);
24 } catch (Exception e) {
25 logger.error("callback invoke error ,url:" + channel.getUrl(), e);
26 }
27 } else {
28 try {
29 RuntimeException re = new RuntimeException(res.getErrorMessage());
30 // 其他异常,回调ResponseCallback回调函数的caught方法
31 callbackCopy.caught(re);
32 } catch (Exception e) {
33 logger.error("callback invoke error ,url:" + channel.getUrl(), e);
34 }
35 }
36 }
从setCallback(ResponseCallback callback),如果此时provider端已经返回了响应(response!=null),则直接执行ResponseCallback回调函数中的done方法或者caught方法;否则,将上边创建的ResponseCallback实例赋值给DefaultFuture的ResponseCallback callback属性中。那么之后会在什么时候执行回调函数的方法呢?当consumer接收到provider的响应的时候!
1 public static void received(Channel channel, Response response) {
2 try {
3 DefaultFuture future = FUTURES.remove(response.getId());
4 if (future != null) {
5 future.doReceived(response);
6 } else {
7 logger.warn("The timeout response finally returned at "
8 + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date()))
9 + ", response " + response
10 + (channel == null ? "" : ", channel: " + channel.getLocalAddress()
11 + " -> " + channel.getRemoteAddress()));
12 }
13 } finally {
14 CHANNELS.remove(response.getId());
15 }
16 }
17
18 private void doReceived(Response res) {
19 lock.lock();
20 try {
21 response = res;
22 if (done != null) {
23 done.signal();
24 }
25 } finally {
26 lock.unlock();
27 }
28 // 调用回调函数
29 if (callback != null) {
30 invokeCallback(callback);
31 }
32 }
当provider返回响应时,会调用DefaultFuture.received(Channel channel, Response response)方法(9.3 客户端接收响应信息(异步转同步的实现)),此时会执行回调函数。事件通知的源码就分析完了!最后看一个回调模式的使用场景:统计异步方法的调用时间。
1 private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
2 Future<?> f = RpcContext.getContext().getFuture();
3 final long start = System.currentTimeMillis();
4 if (f instanceof FutureAdapter) {
5 ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
6 future.setCallback(new ResponseCallback() {
7 public void done(Object rpcResult) {
8 long cost = System.currentTimeMillis() - start;
9 }
10 });
11 }
12 }
上边的代码只是一个形式,实际上start时间需要在调用sayHello方法之前进行记录。
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 人力资源管理中的大数据应用之道
【推荐】 Android输入法弹出时覆盖输入框问题
dubbo事件通知机制 (2)的更多相关文章
- 9.5 dubbo事件通知机制
dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html 一.使用方式 两个服务: DemoService: ...
- dubbo事件通知机制(1)
此文已由作者岳猛授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos ...
- spring事件通知机制详解
优势 解耦 对同一种事件有多种处理方式 不干扰主线(main line) 起源 要讲spring的事件通知机制,就要先了解一下spring中的这些接口和抽象类: ApplicationEventPub ...
- muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制
目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...
- Spring事件通知机制
在上图中,调用 getApplicationEventMulticaster()方法,该方法返回的ApplicationEventMulticaster类型的对象applicationEventMul ...
- 重叠I/O之事件通知
在 Winsock 中,重叠 I/O(Overlapped I/O)模型能达到更佳的系统性能,高于select模型.异步选择和事件选择三种.重叠模型的基本设计原理便是让应用程序使 用一个重叠的数据 ...
- Android应用程序组件Content Provider的共享数据更新通知机制分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6985171 在Android系统中,应用程序组 ...
- 如何扩展分布式日志组件(Exceptionless)的Webhook事件通知类型?
写在前面 从上一篇博客高并发.低延迟之C#玩转CPU高速缓存(附示例)到现在又有几个月没写博客了,啥也不说,变得越来越懒了,懒惰产生了拖延后遗症. 最近一周升级了微服务项目使用的分布式日志组件Exce ...
- 深入理解nodejs的异步IO与事件模块机制
node为什么要使用异步I/O 异步I/O的技术方案:轮询技术 node的异步I/O nodejs事件环 一.node为什么要使用异步I/O 异步最先诞生于操作系统的底层,在底层系统中,异步通过信号量 ...
随机推荐
- js中call apply方法的使用介绍
js call call 方法 请参阅 应用于:Function 对象 要求 版本 5.5 调用一个对象的一个方法,以另一个对象替换当前对象. call([thisObj[,arg1[, arg2[, ...
- C++面试考点
1.下面程序在x64下结果 struct st { int a; long long b; double c; }; int main() { st s; cout << &s.a ...
- iOS离屏渲染
为什么会使用离屏渲染 当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前不能直接在屏幕中绘制,所以就需要屏幕外渲染被唤起. 屏幕外渲染并不意味着软件绘制,但是它意味着图层必须在被显示 ...
- 环境搭建:Vue环境搭建和项目初始化(windows)
1. 安装node.js 官网下载安装:https://nodejs.org/en/ 版本查看:node -v 注意:node版本最好新一点好,推荐6.0以上. 2. npm安装webpa ...
- dialog插件demo
基本操作 默认窗体 new Dialog('这是一个默认对话框').show(); 非模态对话框 new Dialog('非模态对话框,可以打开多个!',{modal:false}).show(); ...
- JeeSite入门介绍(一)
JeeSite特点:高效.高性能.强安全性属于开源.JavaEE快速开发平台:接私活的最佳助手: JeeSite是在Spring Framework基础上搭建的一个Java基础开发平台,以Spring ...
- Linux 正文处理命令及tar vi 编辑器
root 用户不受权限设置 如果文件有x 就可以执行 对目录的权限 r:可以查看目录下面的文件名 w:可以在该目录下新建,删除,修改文件名 x:可以cd进入目录 对于文件: 可执行的文件来说需要有可读 ...
- 通过在Oracle子表外键上建立索引提高性能
根据我的经验,导致死锁的头号原因是外键未加索引(第二号原因是表上的位图索引遭到并发更新).在以下两种情况下,Oracle在修改父表后会对子表加一个全表锁: 1)如果更新了父表的主键(倘若遵循关系数据库 ...
- linux ORACLE备份还原(EXP\IMP)
一.Oracle导入导出 1.Oracle的备份是Oracle操作中常见的工作,常见的备份方案有:逻辑备份(IMP&EXP命令进行备份).物理文件备份(脱机及联机备份).利用RMAN(Reco ...
- ie7下z-index失效问题解决方法
绝对定位元素的“有定位属性(relative或absolute)的父元素”在渲染层次时起到了主要作用,前面的被后面的覆盖了.解决办法就是给有定位属性的父元素设置z-index 解决办法: 父级元素加上 ...