前言
今天主要給大家分享一下dubbo的事件通知機制。
先看下dubbo中文官網(wǎng)的示例解釋:事件通知。
在調(diào)用之前、調(diào)用之后、出現(xiàn)異常時,會觸發(fā) oninvoke、onreturn、onthrow 三個事件,可以配置當事件發(fā)生時,通知哪個類的哪個方法.
實踐
溪源目的是快速學習dubbo的相關機制,故定義的相同的接口和方法,采用分包的方式解耦合,便于后期維護。
先看服務接口
dubbo-demo-interface
目錄如圖
**
UserNotifyService **
/** * @author wx * @date 2020/9/8 1:44 下午 * 測試事件通知 */ public interface UserNotifyService { /** * 獲取用戶名字 * @param userId * @return */ String getUserName(String userId); }
dubbo-demo-xml-provider
目錄如下
notify-provider.xml
定義一個新的配置文件,用于配置事件通知相關bean。
UserNotifyServiceImpl
/** * @author wx * @date 2020/9/8 1:46 下午 */ @Service public class UserNotifyServiceImpl implements UserNotifyService { private static final String USER_ID = "1503892"; @Override public String getUserName(String userId) { if (StringUtils.isBlank(userId)) { throw new RpcException("userId is null"); } return USER_ID.equals(userId) ? "溪~源" : ""; } }
ProviderApplication
/** * 事件通知 * @throws IOException */ private static void notifyTest() throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/notify-provider.xml"); context.start(); System.in.read(); }
dubbo-demo-xml-consumer 目錄如下
ConsumerNotifyService
擴展點
1. oninvoke方法:
必須具有與真實的被調(diào)用方法sayHello相同的入?yún)⒘斜恚豪纾琽ninvoke(String name)
2. onreturn方法:
2.1 至少要有一個入?yún)⑶业谝粋€入?yún)⒈仨毰cgetUserName的返回類型相同,接收返回結果:例如,onReturnWithoutParam(String result);
2.2 可以有多個參數(shù),多個參數(shù)的情況下,第一個后邊的所有參數(shù)都是用來接收getUserName入?yún)⒌模豪纾?onreturn(String result, String name)
3. onthrow方法:
3.1 至少要有一個入?yún)⑶业谝粋€入?yún)㈩愋蜑門hrowable或其子類,接收返回結果;例如,onthrow(Throwable ex);
3.2 可以有多個參數(shù),多個參數(shù)的情況下,第一個后邊的所有參數(shù)都是用來接收getUserName入?yún)⒌模豪纾琽nthrow(Throwable ex, String name);
4. 如果是consumer在調(diào)用provider的過程中,出現(xiàn)異常時不會走onthrow方法的,onthrow方法只會在provider返回的RpcResult中含有Exception對象時,才會執(zhí)行。(dubbo中下層服務的Exception會被放在響應RpcResult的exception對象中傳遞給上層服務)
對于上面的解釋,大家可能會存在疑惑,部分方法要求第一個參數(shù)為服務接口的返回值類型???約定大于配置???揭開迷底的方法就是debug源碼設計實現(xiàn)邏輯~
源代碼:
/** * @author wx * @date 2020/9/8 1:53 下午 */ public interface ConsumerNotifyService { /** * 調(diào)用之前 * @param name */ void onInvoke(String name); /** * 無參數(shù):調(diào)用之后 * @param result 參數(shù)用于接收 [事件通知]服務接口的方法返回值類型保持一致 */ void onReturnWithoutParam(String result); /** * 有參數(shù):調(diào)用之后 * @param result 第一個參數(shù) 接收 [事件通知]服務接口的方法返回值類型保持一致 * @param name 第二個或者之后,與[事件通知]服務接口的方法入?yún)⒈3忠恢? */ void onReturn(String result, String name); /** * 拋異常 * @param ex * @param name */ void onThrow(Throwable ex, String name); }
ConsumerNotifyServiceImpl
/** * @author wx * @date 2020/9/8 1:59 下午 */ @Service public class ConsumerNotifyServiceImpl implements ConsumerNotifyService{ @Override public void onInvoke(String name) { System.out.println("[事件通知]執(zhí)行onInvoke方法,參數(shù):" + name); } @Override public void onReturnWithoutParam(String result) { System.out.println("[事件通知]執(zhí)行onReturnWithoutParam方法,返回結果:" + result); } @Override public void onReturn(String result, String name) { System.out.println("[事件通知]執(zhí)行onReturn方法,參數(shù):" + name + ", 返回結果:" + result); } @Override public void onThrow(Throwable ex, String name) { System.out.println("[事件通知]執(zhí)行onThrow方法,參數(shù):" + name + ", 異常信息:" + ex.getMessage()); } }
notify-consumer.xml
同理,消費者端也新建notify-consumer.xml文件,具體配置如圖:
ConsumerApplication
private static void notifyTest() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/notify-consumer.xml"); UserNotifyService notifyService = context.getBean(UserNotifyService.class); String userName = notifyService.getUserName("1503892"); System.out.println(userName); }
運行
分別啟動生產(chǎn)者和消費者,運行結果如圖:
分別執(zhí)行了onInvoke方法和onReturn方法
源碼
關于dubbo的事件通知機制,源碼實現(xiàn)基本上位于FutureFilter類中,先給大家貼一下類方法目錄:
上面溪源提到為什么部分方法要約定第一個參數(shù)與接口方法返回值類型保持一致呢?下面揭開迷底,我們進入fireReturnCallback方法
private void fireReturnCallback(final Invoker<?> invoker, final Invocation invocation, final Object result) { ....//省略部分代碼 Object[] params; //獲取方法參數(shù)類型 Class<?>[] rParaTypes = onReturnMethod.getParameterTypes(); if (rParaTypes.length > 1) { //兩個參數(shù):第一個參數(shù)與真實方法getUserName方法返回結果類型相同,第二個接收所有的真實請求參數(shù) if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) { params = new Object[2]; // 真實方法的返回結果 params[0] = result; //執(zhí)行方法入?yún)? params[1] = args; } else { //多個參數(shù):第一個參數(shù)與真實方法getUserName結果類型相同,后邊幾個接收所有的真實請求參數(shù) params = new Object[args.length + 1]; params[0] = result; System.arraycopy(args, 0, params, 1, args.length); } } else { //只有一個參數(shù):接收返回執(zhí)行結果 params = new Object[]{result}; } try { onReturnMethod.invoke(onReturnInst, params); } catch (InvocationTargetException e) { fireThrowCallback(invoker, invocation, e.getTargetException()); } catch (Throwable e) { fireThrowCallback(invoker, invocation, e); } }
事件通知機制,底層實際上利用了反射機制實現(xiàn)類方法的調(diào)用。
溪源初次接觸dubbo本地存根機制,如文中存在錯誤之處,希望大家及時指正!
源碼傳送門:https://github.com/stream-source/dubbo/tree/master/dubbo-demo
總結
到此這篇關于dubbo新手學習之事件通知實踐教程的文章就介紹到這了,更多相關dubbo事件通知內(nèi)容請搜索服務器之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/xuan_lu/article/details/108503765