小言_互联网的博客

dubbo学习之事件通知实践

342人阅读  评论(0)

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

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

实践

溪源目的是快速学习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方法:
	必须具有与真实的被调用方法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对象中传递给上层服务)

对于上面的解释,大家可能会存在疑惑,部分方法要求第一个参数为服务接口的返回值类型???约定大于配置???揭开迷底的方法就是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);
}

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());
    }
}

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);

    }

运行

分别启动生产者和消费者,运行结果如图:
分别执行了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);
        }
    }

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

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

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


转载:https://blog.csdn.net/xuan_lu/article/details/108503765
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场