• dubbo学习之事件通知实践


    目录

    今天主要给大家分享一下dubbo的事件通知机制。
    先看下dubbo中文官网的示例解释:事件通知

    在调用之前、调用之后、出现异常时,会触发 oninvoke、onreturn、onthrow 三个事件,可以配置当事件发生时,通知哪个类的哪个方法.
    
    • 1

    实践

    溪源目的是快速学习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) ? "溪~源" : "";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    ProviderApplication

    /**
         * 事件通知
         * @throws IOException
         */
        private static void notifyTest() throws IOException {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/notify-provider.xml");
            context.start();
            System.in.read();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    dubbo-demo-xml-consumer

    • 目录如下
      在这里插入图片描述

    ConsumerNotifyService

    扩展点

    1. oninvoke方法:
    	必须具有与真实的被调用方法sayHello相同的入参列表:例如,oninvoke(String name)
    2. onreturn方法:
    	2.1 至少要有一个入参且第一个入参必须与getUserName的返回类型相同,接收返回结果:例如,onReturnWithoutParam(String result);
    	2.2 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收getUserName入参的:例如, onreturn(String result, String name)
    3. onthrow方法:
    	3.1 至少要有一个入参且第一个入参类型为Throwable或其子类,接收返回结果;例如,onthrow(Throwable ex);
    	3.2 可以有多个参数,多个参数的情况下,第一个后边的所有参数都是用来接收getUserName入参的:例如,onthrow(Throwable ex, String name);
    4. 如果是consumer在调用provider的过程中,出现异常时不会走onthrow方法的,onthrow方法只会在provider返回的RpcResult中含有Exception对象时,才会执行。(dubbo中下层服务的Exception会被放在响应RpcResult的exception对象中传递给上层服务)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对于上面的解释,大家可能会存在疑惑,部分方法要求第一个参数为服务接口的返回值类型???约定大于配置???揭开迷底的方法就是debug源码设计实现逻辑~
    源代码:

    /**
     * @author wx
     * @date 2020/9/8 1:53 下午
     */
    public interface ConsumerNotifyService {
        /**
         * 调用之前
         * @param name
         */
        void onInvoke(String name);
    
        /**
         * 无参数:调用之后
         * @param result 参数用于接收 [事件通知]服务接口的方法返回值类型保持一致
         */
        void onReturnWithoutParam(String result);
    
        /**
         * 有参数:调用之后
         * @param result 第一个参数 接收 [事件通知]服务接口的方法返回值类型保持一致
         * @param name 第二个或者之后,与[事件通知]服务接口的方法入参保持一致
         */
        void onReturn(String result, String name);
    
        /**
         * 抛异常
         * @param ex
         * @param name
         */
        void onThrow(Throwable ex, String name);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    ConsumerNotifyServiceImpl

    /**
     * @author wx
     * @date 2020/9/8 1:59 下午
     */
    @Service
    public class ConsumerNotifyServiceImpl implements ConsumerNotifyService{
        @Override
        public void onInvoke(String name) {
            System.out.println("[事件通知]执行onInvoke方法,参数:" + name);
        }
    
        @Override
        public void onReturnWithoutParam(String result) {
            System.out.println("[事件通知]执行onReturnWithoutParam方法,返回结果:" + result);
        }
    
        @Override
        public void onReturn(String result, String name) {
            System.out.println("[事件通知]执行onReturn方法,参数:" + name + ", 返回结果:" + result);
        }
    
        @Override
        public void onThrow(Throwable ex, String name) {
            System.out.println("[事件通知]执行onThrow方法,参数:" + name + ", 异常信息:" + ex.getMessage());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    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);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行

    分别启动生产者和消费者,运行结果如图:
    分别执行了onInvoke方法和onReturn方法
    在这里插入图片描述

    源码

    关于dubbo的事件通知机制,源码实现基本上位于FutureFilter类中,先给大家贴一下类方法目录:
    在这里插入图片描述
    上面溪源提到为什么部分方法要约定第一个参数与接口方法返回值类型保持一致呢?下面揭开迷底,我们进入fireReturnCallback方法

    private void fireReturnCallback(final Invoker invoker, final Invocation invocation, final Object result) {
            ....//省略部分代码
            Object[] params;
            //获取方法参数类型
            Class[] rParaTypes = onReturnMethod.getParameterTypes();
            if (rParaTypes.length > 1) {
            //两个参数:第一个参数与真实方法getUserName方法返回结果类型相同,第二个接收所有的真实请求参数
                if (rParaTypes.length == 2 && rParaTypes[1].isAssignableFrom(Object[].class)) {
                    params = new Object[2];
                    // 真实方法的返回结果
                    params[0] = result;
                    //执行方法入参
                    params[1] = args;
                } else {
                //多个参数:第一个参数与真实方法getUserName结果类型相同,后边几个接收所有的真实请求参数
                    params = new Object[args.length + 1];
                    params[0] = result;
                    System.arraycopy(args, 0, params, 1, args.length);
                }
            } else {
            //只有一个参数:接收返回执行结果
                params = new Object[]{result};
            }
            try {
                onReturnMethod.invoke(onReturnInst, params);
            } catch (InvocationTargetException e) {
                fireThrowCallback(invoker, invocation, e.getTargetException());
            } catch (Throwable e) {
                fireThrowCallback(invoker, invocation, e);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    事件通知机制,底层实际上利用了反射机制实现类方法的调用。

    溪源初次接触dubbo本地存根机制,如文中存在错误之处,希望大家及时指正!

    源码传送门:https://github.com/stream-source/dubbo/tree/master/dubbo-demo

  • 相关阅读:
    开源AI搜索平台Search4All
    FreeRTOS中信号量与互斥锁区别
    商城项目03_人人前后端项目、逆向工程、common工程搭建、coupon以及各个微服务工程搭建
    12-Java 继承&&抽象类&&代码块(详解~)
    linux的文件系统
    [Linux]权限理解
    报白是什么意思?入驻抖音小店哪些类目需要报白?报白如何操作?
    Vector Search和专用Search Nodes:现已正式发布
    CFdiv2-Intersection and Union-(线段树+转化求贡献)
    docker-compose安装skywalking进行日志收集,性能监控和链路追踪!
  • 原文地址:https://blog.csdn.net/m0_67402026/article/details/126328135