此文已由作者赵计刚授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

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)的更多相关文章

  1. 9.5 dubbo事件通知机制

    dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html 一.使用方式 两个服务: DemoService: ...

  2. dubbo事件通知机制(1)

    此文已由作者岳猛授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos ...

  3. spring事件通知机制详解

    优势 解耦 对同一种事件有多种处理方式 不干扰主线(main line) 起源 要讲spring的事件通知机制,就要先了解一下spring中的这些接口和抽象类: ApplicationEventPub ...

  4. muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制

    目录 muduo网络库学习笔记(四) 通过eventfd实现的事件通知机制 eventfd的使用 eventfd系统函数 使用示例 EventLoop对eventfd的封装 工作时序 runInLoo ...

  5. Spring事件通知机制

    在上图中,调用 getApplicationEventMulticaster()方法,该方法返回的ApplicationEventMulticaster类型的对象applicationEventMul ...

  6. 重叠I/O之事件通知

      在 Winsock 中,重叠 I/O(Overlapped I/O)模型能达到更佳的系统性能,高于select模型.异步选择和事件选择三种.重叠模型的基本设计原理便是让应用程序使 用一个重叠的数据 ...

  7. Android应用程序组件Content Provider的共享数据更新通知机制分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6985171 在Android系统中,应用程序组 ...

  8. 如何扩展分布式日志组件(Exceptionless)的Webhook事件通知类型?

    写在前面 从上一篇博客高并发.低延迟之C#玩转CPU高速缓存(附示例)到现在又有几个月没写博客了,啥也不说,变得越来越懒了,懒惰产生了拖延后遗症. 最近一周升级了微服务项目使用的分布式日志组件Exce ...

  9. 深入理解nodejs的异步IO与事件模块机制

    node为什么要使用异步I/O 异步I/O的技术方案:轮询技术 node的异步I/O nodejs事件环 一.node为什么要使用异步I/O 异步最先诞生于操作系统的底层,在底层系统中,异步通过信号量 ...

随机推荐

  1. Azure Managed Disk操作

    Azure Managed Disk对原有的Page Blob进行了一次封装.使得Azure VM的Disk操作变得非常简单.本文将介绍实际操作中针对Manage Disk的一些操作. 一.创建Man ...

  2. Linux 驱动编程知识

    1.包含的头文件 1.1 GPIO相关操作 #include <asm/arch/gpio.h>

  3. 线程及同步的性能 – 线程池/ ThreadPoolExecutors/ ForkJoinPool

    线程池和ThreadPoolExecutors 虽然在程序中可以直接使用Thread类型来进行线程操作,但是更多的情况是使用线程池,尤其是在Java EE应用服务器中,一般会使用若干个线程池来处理来自 ...

  4. L2-023. 图着色问题(暴力)

    L2-023. 图着色问题 时间限制 300 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 图着色问题是一个著名的NP完全问题.给定无向图 G ...

  5. Celery-4.1 用户指南: Configuration and defaults (配置和默认值)

    这篇文档描述了可用的配置选项. 如果你使用默认的加载器,你必须创建 celeryconfig.py 模块并且保证它在python路径中. 配置文件示例 以下是配置示例,你可以从这个开始.它包括运行一个 ...

  6. 搭建node.js

    #node.js:概念介绍及安装 # python服务器.php ->aphche.java ->tomcat. iis # 它是一个可以运行JAVASCRIPTR 的运行环境 # 它可以 ...

  7. jackson 进行json与java对象转换 之四

    jackson简单使用,对象转json,json转对象,json转list   POJO序列化为json字符串: 准备一个POJO: @JsonIgnoreProperties(ignoreUnkno ...

  8. JS中substring()方法(用于提取字符串中介于两个指定下标之间的字符)

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. 重命名File

    File completeFile = new File(mFilePath + mFileName); if (completeFile.exists()) { File fileWithSuffi ...

  10. AngularJS分层开发

    为了AngularJS的代码利于维护和复用,利用MVC的模式将代码分离,提高程序的灵活性及可维护性. 1,前端基础层 var app=angular.module('appName',['pagina ...