此文已由作者岳猛授权网易云社区发布。

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

dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html

一、使用方式

两个服务:

  • DemoService:真正要调用的服务

  • Notify:事件通知服务(用在consumer端)

provider

1 package com.alibaba.dubbo.demo;

3 public interface DemoService {
4     String sayHello(String name);
5 }
1 public class DemoServiceImpl implements DemoService {
2     @Override
3     public String sayHello(String name) {
4         throw new RpcException("ex, param: " + name);//测试onthrow方法
5 //        return "Hello " + name;//测试onreturn方法
6     }
7 }

consumer

通知服务:Notify

1 package com.alibaba.dubbo.demo.consumer.eventnotify;

3 public interface Notify {
4     void oninvoke(String name); // 调用之前
5     void onreturnWithoutParam(String result); // 调用之后
6     void onreturn(String result, String name); // 调用之后
7     void onthrow(Throwable ex, String name);  // 出现异常
8 }
  1 package com.alibaba.dubbo.demo.consumer.eventnotify;
 2 
 3 public class NotifyService implements Notify {
 4     @Override
 5     public void oninvoke(String name) {
 6         System.out.println("======oninvoke======, param: " + name);
 7     }
 8 
 9     @Override
10     public void onreturnWithoutParam(String result) {
11         System.out.println("======onreturn======, result: " + result);
12     }
13 
14     @Override
15     public void onreturn(String result, String name) {
16         System.out.println("======onreturn======, param: " + name + ", result: " + result);
17     }
18 
19     @Override
20     public void onthrow(Throwable ex, String name) {
21         System.out.println("======onthrow======, param: " + name + ", exception: " + ex.getMessage());
22     }
23 }

xml配置:

1     <bean id="notifyService"  class="com.alibaba.dubbo.demo.consumer.eventnotify.NotifyService"/>
2     <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService">
3         <dubbo:method name="sayHello" timeout="60000" oninvoke="notifyService.oninvoke" onreturn="notifyService.onreturnWithoutParam" onthrow="notifyService.onthrow"/>
4     </dubbo:reference>

之后就可以运行Consumer启动类,之后调用demoService.sayHello(String name)了。

注意:

  • oninvoke方法:

    • 必须具有与真实的被调用方法sayHello相同的入参列表:例如,oninvoke(String name)

  • onreturn方法:

    • 至少要有一个入参且第一个入参必须与sayHello的返回类型相同,接收返回结果:例如,onreturnWithoutParam(String result)

    • 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收sayHello入参的:例如, onreturn(String result, String name)

  • onthrow方法:

    • 至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果;例如,onthrow(Throwable ex)

    • 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收sayHello入参的:例如,onthrow(Throwable ex, String name)

  • 如果是consumer在调用provider的过程中,出现异常时不会走onthrow方法的,onthrow方法只会在provider返回的RpcResult中含有Exception对象时,才会执行。(dubbo中下层服务的Exception会被放在响应RpcResult的exception对象中传递给上层服务)

二、源码解析

整个事件通知的逻辑都在FutureFilter中,来看一下源码:

1 /**
  2  * EventFilter
  3  */
  4 @Activate(group = Constants.CONSUMER)
  5 public class FutureFilter implements Filter {
  6 
  7     protected static final Logger logger = LoggerFactory.getLogger(FutureFilter.class);
  8 
  9     public Result invoke(final Invoker<?> invoker, final Invocation invocation) throws RpcException {
 10         final boolean isAsync = RpcUtils.isAsync(invoker.getUrl(), invocation);
 11 
 12         //1 调用服务之前:执行xxxService.oninvoke方法
 13         fireInvokeCallback(invoker, invocation);
 14         //2 调用服务
 15         Result result = invoker.invoke(invocation);
 16         //3 调用服务之后
 17         if (isAsync) {
 18             asyncCallback(invoker, invocation);
 19         } else {
 20             syncCallback(invoker, invocation, result); 21         }
 22         //4 返回调用结果
 23         return result;
 24     }
 25 
 26     private void syncCallback(final Invoker<?> invoker, final Invocation invocation, final Result result) {
 27         if (result.hasException()) {
 28             //3.1 调用服务之后:如果返回结果异常信息(注意:如果是consumer自己throw的异常,会在2的时候直接抛走,不会走到这里),直接执行xxxService.onthrow方法
 29             fireThrowCallback(invoker, invocation, result.getException());
 30         } else {
 31             //3.2 调用服务之后:如果返回值正常,执行xxxService.onreturn方法
 32             fireReturnCallback(invoker, invocation, result.getValue());
 33         }
 34     }
 35 
 36     private void asyncCallback(final Invoker<?> invoker, final Invocation invocation) {
 37         Future<?> f = RpcContext.getContext().getFuture();
 38         if (f instanceof FutureAdapter) {
 39             ResponseFuture future = ((FutureAdapter<?>) f).getFuture();
 40             // 3.1 调用服务之后:设置回调ResponseCallback对象到DefaultFuture中,当provider返回响应时,执行DefaultFuture.doReceived方法,该方法会调用ResponseCallback对象的done或者caught方法
 41             future.setCallback(new ResponseCallback() {
 42                 public void done(Object rpcResult) {
 43                     if (rpcResult == null) {
 44                         logger.error(new IllegalStateException("invalid result value : null, expected " + Result.class.getName()));
 45                         return;
 46                     }
 47                     ///must be rpcResult
 48                     if (!(rpcResult instanceof Result)) {
 49                         logger.error(new IllegalStateException("invalid result type :" + rpcResult.getClass() + ", expected " + Result.class.getName()));
 50                         return;
 51                     }
 52                     Result result = (Result) rpcResult;
 53                     if (result.hasException()) {
 54                         fireThrowCallback(invoker, invocation, result.getException());
 55                     } else {
 56                         fireReturnCallback(invoker, invocation, result.getValue());
 57                     }
 58                 }
 59 
 60                 public void caught(Throwable exception) {
 61                     fireThrowCallback(invoker, invocation, exception);
 62                 }
 63             });
 64         }
 65     }
 66 
 67     /**
 68      * 反射执行xxxService.oninvoke方法:必须具有与真实的被调用方法sayHello相同的入参列表。
 69      */
 70     private void fireInvokeCallback(final Invoker<?> invoker, final Invocation invocation) {
 71         final Method onInvokeMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_METHOD_KEY));
 72         final Object onInvokeInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_INVOKE_INSTANCE_KEY));
 73 
 74         if (onInvokeMethod == null && onInvokeInst == null) {
 75             return;
 76         }
 77         if (onInvokeMethod == null || onInvokeInst == null) {
 78             throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onreturn callback config , but no such " + (onInvokeMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
 79         }
 80         if (onInvokeMethod != null && !onInvokeMethod.isAccessible()) {
 81             onInvokeMethod.setAccessible(true);
 82         }
 83         // 获取真实方法sayHello传入的参数
 84         Object[] params = invocation.getArguments();
 85         try {
 86             onInvokeMethod.invoke(onInvokeInst, params);
 87         } catch (InvocationTargetException e) {
 88             fireThrowCallback(invoker, invocation, e.getTargetException());
 89         } catch (Throwable e) {
 90             fireThrowCallback(invoker, invocation, e);
 91         }
 92     }
 93 
 94     /**
 95      * 反射执行xxxService.onreturn方法:至少要有一个入参,接收返回结果
 96      */
 97     private void fireReturnCallback(final Invoker<?> invoker, final Invocation invocation, final Object result) {
 98         final Method onReturnMethod = (Method) StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_METHOD_KEY));
 99         final Object onReturnInst = StaticContext.getSystemContext().get(StaticContext.getKey(invoker.getUrl(), invocation.getMethodName(), Constants.ON_RETURN_INSTANCE_KEY));
100 
101         //not set onreturn callback
102         if (onReturnMethod == null && onReturnInst == null) {
103             return;
104         }
105 
106         if (onReturnMethod == null || onReturnInst == null) {
107             throw new IllegalStateException("service:" + invoker.getUrl().getServiceKey() + " has a onreturn callback config , but no such " + (onReturnMethod == null ? "method" : "instance") + " found. url:" + invoker.getUrl());
108         }
109         if (onReturnMethod != null && !onReturnMethod.isAccessible()) {
110             onReturnMethod.setAccessible(true);
111         }
112 
113         Object[] args = invocation.getArguments();
114         Object[] params;
115         Class<?>[] rParaTypes = onReturnMethod.getParameterTypes();
116         if (rParaTypes.length > 1) {
117             // onreturn(xx, Object[]) 两个参数:第一个参数与真实方法sayHello方法返回结果类型相同,第二个接收所有的真实请求参数
118             if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) {
119                 params = new Object[2];
120                 params[0] = result; // 真实方法的执行结果
121                 params[1] = args;   // 真实方法sayHello传入的参数
122             // onreturn(xx, Object... args) 多个参数:第一个参数与真实方法sayHello方法返回结果类型相同,后边几个接收所有的真实请求参数
123             } else {
124                 params = new Object[args.length + 1];
125                 params[0] = result; // 真实方法的执行结果
126                 System.arraycopy(args, 0, params, 1, args.length);
127             }
128         } else {
129             // onreturn(xx) 只有一个参数:接收返回执行结果
130             params = new Object[]{result}; // 执行结果
131         }
132         try {
133             onReturnMethod.invoke(onReturnInst, params);
134         } catch (InvocationTargetException e) {
135             fireThrowCallback(invoker, invocation, e.getTargetException());
136         } catch (Throwable e) {
137             fireThrowCallback(invoker, invocation, e);
138         }
139     }
140 
141     /**

免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 Dubbo与Hadoop RPC的区别
【推荐】 git subrepo

dubbo事件通知机制(1)的更多相关文章

  1. 9.5 dubbo事件通知机制

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

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

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 142      * 反射执行xxxService.onthrow方法:至少要有一个入参且第一个入参类型为T ...

  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. python manage.py runserver 0.0.0.0:8000

    python manage.py runserver 这种命令行,可以在服务器端输入IP:8000直接访问 在 python manage.py runserver 127.0.01:8000 在服务 ...

  2. leetcode861

    public class Solution { public int MatrixScore(int[][] A) { ); ].GetLength(); //判断最高位是否为1 ; i < r ...

  3. DB2 日期时间函数

    db2日期时间函数 (DATE(TRIM(CHAR(DT#11Y))||'-'||TRIM(CHAR(DT#11M))||'-'||TRIM(CHAR(DT#11D))) BETWEEN DATE(' ...

  4. Spring Boot实践——多线程

    多线程 Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程.使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor.而实际开发中任务一 ...

  5. vs启动项目提示Web 服务器被配置为不列出此目录的内容。

    解决方法 确认网站或应用程序配置文件中的 configuration/system.webServer/directoryBrowse@enabled 属性已设置为 true. 配置一下web.con ...

  6. 【python】 time模块和datetime模块详解 【转】

    一.time模块 time模块中时间表现的格式主要有三种: a.timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 b.struct_time时间元组,共 ...

  7. zookeeper分布式锁和服务优化配置

    转自:https://www.jianshu.com/p/02eeaee4357f?utm_campaign=maleskine&utm_content=note&utm_medium ...

  8. 归纳整理Linux下C语言常用的库函数----内存及字符串控制及操作

    在没有IDE的时候,记住一些常用的库函数的函数名.参数.基本用法及注意事项是很有必要的. 参照Linux_C_HS.chm的目录,我大致将常用的函数分为一下几类: 1. 内存及字符串控制及操作 2. ...

  9. MyBatis 动态SQL注意事项

  10. MySQL学习3---事务

    MySQL 事务 MySQL 事务主要用于处理操作量大,复杂度高的数据. 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务. 事务处理可以用来维护数据库的完整性,保证成批的 ...