dubbo事件通知机制(1)
此文已由作者岳猛授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html
一、使用方式
两个服务:
DemoService:真正要调用的服务
Notify:事件通知服务(用在consumer端)
provider:
1 package com.alibaba.dubbo.demo;
2
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;
2
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)的更多相关文章
- 9.5 dubbo事件通知机制
dubbo事件通知机制:http://dubbo.io/books/dubbo-user-book/demos/events-notify.html 一.使用方式 两个服务: DemoService: ...
- dubbo事件通知机制 (2)
此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 142 * 反射执行xxxService.onthrow方法:至少要有一个入参且第一个入参类型为T ...
- 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 异步最先诞生于操作系统的底层,在底层系统中,异步通过信号量 ...
随机推荐
- python manage.py runserver 0.0.0.0:8000
python manage.py runserver 这种命令行,可以在服务器端输入IP:8000直接访问 在 python manage.py runserver 127.0.01:8000 在服务 ...
- leetcode861
public class Solution { public int MatrixScore(int[][] A) { ); ].GetLength(); //判断最高位是否为1 ; i < r ...
- DB2 日期时间函数
db2日期时间函数 (DATE(TRIM(CHAR(DT#11Y))||'-'||TRIM(CHAR(DT#11M))||'-'||TRIM(CHAR(DT#11D))) BETWEEN DATE(' ...
- Spring Boot实践——多线程
多线程 Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程.使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor.而实际开发中任务一 ...
- vs启动项目提示Web 服务器被配置为不列出此目录的内容。
解决方法 确认网站或应用程序配置文件中的 configuration/system.webServer/directoryBrowse@enabled 属性已设置为 true. 配置一下web.con ...
- 【python】 time模块和datetime模块详解 【转】
一.time模块 time模块中时间表现的格式主要有三种: a.timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 b.struct_time时间元组,共 ...
- zookeeper分布式锁和服务优化配置
转自:https://www.jianshu.com/p/02eeaee4357f?utm_campaign=maleskine&utm_content=note&utm_medium ...
- 归纳整理Linux下C语言常用的库函数----内存及字符串控制及操作
在没有IDE的时候,记住一些常用的库函数的函数名.参数.基本用法及注意事项是很有必要的. 参照Linux_C_HS.chm的目录,我大致将常用的函数分为一下几类: 1. 内存及字符串控制及操作 2. ...
- MyBatis 动态SQL注意事项
- MySQL学习3---事务
MySQL 事务 MySQL 事务主要用于处理操作量大,复杂度高的数据. 在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务. 事务处理可以用来维护数据库的完整性,保证成批的 ...