【dubbo源码解析】--- dubbo中Invoker嵌套调用底层原理

本文对应源码地址:https://github.com/nieandsun/dubbo-study



1 dubbo中Invoker的重要性

上篇文章《【dubbo源码解析】— dubbo的服务注册与发现机制底层原理探析》讲解到在不加入注册中心逻辑之前,dubbo中消费端@Reference注解标注的bean进行方法调用的过程如下图所示:
在这里插入图片描述
假如你clone下来我上篇文章对应的代码,并跟了一下源码的话,那你肯定知道在引入注册中心后:

  • (1)其实在服务端 RegistryProtocol对象将Invoker交给具体的Protocol进行服务暴露时,传入的并不是原始的Invoker,而是将其包装成了一个InvokerDelegete对象(也是一个Invoker)

则当服务端被调用时,其实是先调用到这个InvokerDelegete对象的invoke方法—>然后再在InvokerDelegete对象内部调用原始Invoker的invoke方法 —> 最后才真正调用的ref对象的具体方法

  • (2)而在消费端其实也不是一下就由具体的Protocol获取到【可以调用远程服务的对象】,然后将其和【url】【interface】封装为一个Invoker。 其实在消费端也是在最外层包了一个XXXClusterInvoker。

也就是说在消费端@Reference注解标注的bean进行方法调用时,会先调用XXXClusterInvoker的invoke方法—》然后再调用由具体的Protocol获取到包装了【可以调用远程服务的对象】的Invoker的invoke方法 —》然后调用远程服务。

实际上我们真实dubbo开发中遇到的过滤器链负载均衡容错等组件的底层逻辑的具体实现都被封装成了一个个Invoker对象,串联在dubbo的整个RPC链条中 , 画个dubbo的RPC调用简图大致如下:
在这里插入图片描述
由此可知在dubbo中Invoker对象,非常重要!!!


2 dubbo RPC链条中代理对象的底层逻辑


2.1 以之前的文章为基础实现一个简单的dubbo服务端+消费端

如果看过前面几篇文章的话,相信对于下面的代码你肯定不会陌生:
服务端

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
//代理
ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

//注册中心服务--zk
final URL registryUrl = URL.valueOf("registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?registry=zookeeper");

//支持的协议:dubbo,http,hessian,rmi
URL serviceUrl = URL.valueOf("rmi://127.0.0.1:9001/com.nrsc.service.InvokerDemoService" + "?proxy=jdk"); //动态代理

@Test
public void serverRpc() throws IOException {
    InvokerDemoService service = new InvokerDemoServiceImpl("yoyo");
    serviceUrl = serviceUrl.setPort(9001);
    URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl.toFullString());
    //暴露服务
    Invoker<InvokerDemoService> serviceInvoker = proxy.getInvoker(service, InvokerDemoService.class, newRegistryUrl);
    Exporter<InvokerDemoService> exporter = protocol.export(serviceInvoker);
    System.out.println("server 启动协议:" + serviceUrl.getProtocol());
    // 保证服务一直开着
    System.in.read();
    exporter.unexport();
}

消费端

@Test
public void clientRpc() {
    Invoker<InvokerDemoService> referInvoker = protocol.refer(InvokerDemoService.class, registryUrl);
    //获取【可以调用远程服务的对象】的代理对象,当调用此对象的方法时会走代理逻辑,调用referInvoker的invoke方法
    InvokerDemoService service = proxy.getProxy(referInvoker);
    String result = service.sayHello(registryUrl.getProtocol() + "调用");
    System.out.println(result);
}

2.2 代理对象的底层逻辑

上面的代码消费端和服务端肯定是可以调通的,这里有兴趣的可以自己测试一下。但是下面的这行代码的底层逻辑究竟是什么呢?

String result = service.sayHello(registryUrl.getProtocol() + "调用");

其实它底层的逻辑,我在《【dubbo源码解析】— dubbo的服务暴露+服务消费(RPC调用)底层原理深入探析》这篇文章里讲过。以动态代理为例,当使用代理对象service调用方法时,它会走InvokerInvocationHandler中的invoke方法(动态代理的逻辑) —> 然后在该方法里调用Invoker的invoke方法,其源码如下:

public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }
        //调用Invoker的invoke方法
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}

这里可以注意到,其实它底层就是将service调用的方法(method)和具体的参数(args)封装成一个RpcInvocation对象,然后拿着这个封装的参数来调用Invoker的invoke方法(再底层又用到了反射,有兴趣的可以看一下,挺简单的)。

实际上这个RpcInvocation对象的构建不仅可以使用method和args,它还有如下构造方法
在这里插入图片描述

由此可知我们其实可以直接构建一个RpcInvocation对象,然后拿着它直接调用Invoker的invoke方法,来直接拉通整个dubbo的RPC调用流程 。


2.3 跳过代理对象利用直接构造的RpcInvocation对象拉通整个RPC链条

以上图中黄色箭头指定的构造方法构造RpcInvocation对象,并进行消费端调用的demo:

@Test
public void clientInvoker() {
    Invoker<InvokerDemoService> referInvoker = protocol.refer(InvokerDemoService.class, registryUrl);
    //直接构造RpcInvocation对象
    Invocation invocation = new RpcInvocation(
            "sayHello", new Class<?>[]{String.class}, new Object[]{"invoke直接调用"}, null);
    //拿着构造的RpcInvocation对象直接调用referInvoker的invoke方法
    Result result2 = referInvoker.invoke(invocation);
    System.out.println(result2.getValue());
}

测试结果如下:
在这里插入图片描述


3 dubbo中Invoker嵌套调用底层原理

dubbo中Invoker的嵌套调用底层原理基本如下 —过滤器链负载均衡容错机制等其底层逻辑都是按照下面的逻辑被封装嵌套到dubbo的整个RPC链条中。
在这里插入图片描述
这里简单提供一个dubbo的Invoker嵌套调用示例:

private Invocation invocation = new RpcInvocation(
        "sayHello", new Class<?>[]{String.class}, new Object[]{"invoke调用"}, null);

@Test
public void clientInvokerWrapper() throws IOException {
    //获取包装了【可以调用远程服务的对象】的Invoker
    Invoker<InvokerDemoService> referInvoker = protocol.refer(InvokerDemoService.class, registryUrl);
    //对包装了【可以调用远程服务的对象】的Invoker再嵌套一个Invoker
    Invoker<InvokerDemoService> wrapperInvoker = new Invoker<InvokerDemoService>() {
        private Invoker<InvokerDemoService> invoker = referInvoker;

        @Override
        public Class<InvokerDemoService> getInterface() {
            return invoker.getInterface();
        }

        @Override
        public Result invoke(Invocation invocation) throws RpcException {
            System.out.println("invoker嵌套调用");
            return invoker.invoke(invocation);
        }

        @Override
        public URL getUrl() {
            return invoker.getUrl();
        }

        @Override
        public boolean isAvailable() {
            return false;
        }

        @Override
        public void destroy() {
        }
    };
    //拿着invocation调用最外层Invoker的invoke方法,由于最外层的Invoker里有包装了【可以调用远程服务的对象】的Invoker,
    //所以可以现在最外层的Invoker的invoke方法里先做些事情再调用包装了【可以调用远程服务的对象】的Invoker的invoke方法
    Result result3 = wrapperInvoker.invoke(invocation);
    System.out.println(result3.getValue());
}

简单看一下测试结果:
在这里插入图片描述


4 dubbo的RPC调用简图最终效果

在这里插入图片描述


end!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值