文档章节

dubbo rpc

hyssop
 hyssop
发布于 2016/08/19 18:38
字数 1905
阅读 399
收藏 7

dubbo中rpc通信算是重头戏。本文基于dubbo-rpc-api dubbo-rpc-default两个包重点了解下dubbo rpc的实现原理。

#1、代码示例 既然要说原理,首先就是把实现了rpc的代码示例放在这里。

DemoService service = new DemoServiceImpl();
		protocol.export(proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
		service = proxy.getProxy(protocol.refer(DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange")));
		assertEquals(service.getSize(new String[]{"", "", ""}), 3);

将其“肢解”,我们就能够对rpc有了一定的了解。 #1、生成实例 DemoService service = new DemoServiceImpl(); 我们知道平时我们在spring里面配置dubbo信息到spring,客户端在只有接口的情况下就能够调到实现类,就好像这行代码一样。你有没有问,客户端本来没这个类怎么就能调到实现类的呢?代理呗,您如是回答。那么我们继续。 #2、服务端发布自己的服务 ##2.1、生成代理

proxy.getInvoker(service, DemoService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange"))

生成代理的过程就这么简单,而其中包含的东西真的不少,大致步骤: ###1、URL类会解析传递进来的字符串,根据规则将其放置到URL的各个字段里面去。 "dubbo://127.0.0.1:9020/" + DemoService.class.getName() + "?codec=exchange"是传递进来的字符串,仔细看它就是"dubbo://127.0.0.1:9020/DemoServie?codec=exchange". ###2、proxy.getInvoker(实例,接口,URL)做的事情比较多。 首先这个proxy是一个适配器,这个如果您跟代码会发现完全知道到,是因为这个适配器是在ExtendLoader里面动态生成的,在调用getInvoker方法的时候其实是在调用适配器里面的该方法。适配器里面的该方法会根据您传递进来的URL去寻找真正的实现类。这里找到类也许您会认为是JavassistProxyFactory,非也,其实找到的是StubProxyFactoryWrapper类,并把JavassistProxyFactory放置到该类中来。知道了这个类那么就该调用真正实现类的getInvoker方法。

  public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
        return proxyFactory.getInvoker(proxy, type, url);
    }

这里就直接调用了JavassistProxyFactory里面的getInvoker方法。

  public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper类不能正确处理带$的类名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, 
                                      Class<?>[] parameterTypes, 
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

但看这个方法貌似就做了两件事情:生成wrapper实例,返回一个AbstractProxyInvoker实例。这里面的wrapper真的是不能小觑,打开实现类会发现作者在这个类的代码量很大。那么他在做什么?

 ret = makeWrapper(c);

它在包装实例。将之前的DemoServiceImpl实例包装成了一个wrapper返回。这个过程做了三件事情 首先:拼接了三个方法串。

方法串1
 public void setPropertyValue(Object o, String n, Object v) {
        com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl w;
        try {
            w = ((com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl.");
    }


方法串2
public Object getPropertyValue(Object o, String n) {
        com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl w;
        try {
            w = ((com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        if ($2.equals("threadName")) {
            return ($w) w.getThreadName();
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl.");
    }

方法串3

  public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl w;
        try {
            w = ((com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl) $1);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            if ("invoke".equals($2) && $3.length == 2) {
                return ($w) w.invoke((java.lang.String) $4[0], (java.lang.String) $4[1]);
            }
            if ("get".equals($2) && $3.length == 1) {
                return ($w) w.get((com.alibaba.dubbo.rpc.protocol.dubbo.support.CustomArgument) $4[0]);
            }
            if ("timestamp".equals($2) && $3.length == 0) {
                return ($w) w.timestamp();
            }
            if ("keys".equals($2) && $3.length == 1) {
                return ($w) w.keys((java.util.Map) $4[0]);
            }
            if ("getSize".equals($2) && $3.length == 1 && $3[0].getName().equals("[Ljava.lang.String;")) {
                return ($w) w.getSize((java.lang.String[]) $4[0]);
            }
            if ("getSize".equals($2) && $3.length == 1 && $3[0].getName().equals("[Ljava.lang.Object;")) {
                return ($w) w.getSize((java.lang.Object[]) $4[0]);
            }
            if ("echo".equals($2) && $3.length == 1 && $3[0].getName().equals("java.util.Map")) {
                return ($w) w.echo((java.util.Map) $4[0]);
            }
            if ("echo".equals($2) && $3.length == 1 && $3[0].getName().equals("java.lang.String")) {
                return ($w) w.echo((java.lang.String) $4[0]);
            }
            if ("enumlength".equals($2) && $3.length == 1 && $3[0].getName().equals("[Lcom.alibaba.dubbo.rpc.protocol.dubbo.support.Type;")) {
                return ($w) w.enumlength((com.alibaba.dubbo.rpc.protocol.dubbo.support.Type[]) $4[0]);
            }
            if ("enumlength".equals($2) && $3.length == 1 && $3[0].getName().equals("com.alibaba.dubbo.rpc.protocol.dubbo.support.Type")) {
                return ($w) w.enumlength((com.alibaba.dubbo.rpc.protocol.dubbo.support.Type) $4[0]);
            }
            if ("stringLength".equals($2) && $3.length == 1) {
                return ($w) w.stringLength((java.lang.String) $4[0]);
            }
            if ("sayHello".equals($2) && $3.length == 1) {
                w.sayHello((java.lang.String) $4[0]);
                return null;
            }
            if ("nonSerializedParameter".equals($2) && $3.length == 1) {
                w.nonSerializedParameter((com.alibaba.dubbo.rpc.protocol.dubbo.support.NonSerialized) $4[0]);
                return null;
            }
            if ("returnNonSerialized".equals($2) && $3.length == 0) {
                return ($w) w.returnNonSerialized();
            }
            if ("getThreadName".equals($2) && $3.length == 0) {
                return ($w) w.getThreadName();
            }
            if ("getbyte".equals($2) && $3.length == 1) {
                return ($w) w.getbyte(((Byte) $4[0]).byteValue());
            }
            if ("gerPerson".equals($2) && $3.length == 1) {
                return ($w) w.gerPerson((com.alibaba.dubbo.rpc.protocol.dubbo.support.Person) $4[0]);
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class com.alibaba.dubbo.rpc.protocol.dubbo.support.DemoServiceImpl.");
    }

然后、生成实例

ClassGenerator cc = ClassGenerator.newInstance(cl); 在这个实例里面加属性和方法(包括上面拼接的三个方法串)。 最后,生成cc的实例。将其转为Wrapper类型返回。 至此,我们可以这样理解,这样动态生成的Wrapper已经不是普通的Wrapper了,虽然普通的Wrapper也有方法invokeMethod。但是它并没有关联实际的实现类,作者在这块做了一个动态的Wrapper,代理了普通的Wrapper。 继续,别蒙,至此我们得到了一个AbstractProxyInvoker实例。 至此proxy.getInvoker()交代完毕。 ##2.2、protocol.export(Invoker<T> invoker) throws RpcException; 上文我们说这个参数invoker实际的类型是AbstractProxyInvoker。 接下来我们看看这个方法做什么了 ####1、protocol本身是protocol的适配器,会根据传入进来的URL找到真正实现类的Wrapper。这里为什么生成的是实现类的包装器,请读者去ExtendLoader里面一探究竟。Protocol的包装器类有以下两个 class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper 最终返回类型是ProtocolFilterWrapper


    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }
    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
        if (filters.size() > 0) {
            for (int i = filters.size() - 1; i >= 0; i --) {
                final Filter filter = filters.get(i);
                final Invoker<T> next = last;
                last = new Invoker<T>() {

                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

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

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(next, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    @Override
                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }
        return last;
    }

首先是创建一个过滤器链,将AbstractProxyInvoker实例层层包装 然后是调用protocol.export方法。 这个方法有是一个适配器代理找包装器的过程,最后发现没有包装器,直接返回了协议类DubboProtocol。该类的作用就是将Invoker转成Exporter,并调用dubbo根据这个url连接注册中心(或者直连)。 服务器端的内容就讲到这里。接下来我们关注客户端。 #3、客户端去获取服务 ##3.1、将请求的url包装成invoker protocol.refer(Class<T> type, URL url) 该方法返回ListenerInvokerWrapper类型的实例 ##3.2、获得invoker的代理。 proxy.getProxy(Invoker<T> invoker) throws RpcException proxy会根据ExtendLoader类获得这个类的wrapper:StubProxyFactoryWrapper。最终会调用该类的getProxy方法去获得代理。 1、调用具体的代理工厂创建一个invoker的代理类。这里面是JavassistProxyFactory类生成的代理。

  public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

##3.3、调用接口方法 service.getSize(null)。 调用接口方法就是去执行上面代理类的过程,具体过程大体是: ###1、进入InvokerInvocationHandler的invoke方法 ###2、invoker方法发现我们调用的不是简单的方法(hashCode,equals)等,那么进入invoker。

invoker.invoke(new RpcInvocation(method, args)).recreate()

####2.1、包装RpcInvocation类的实例。 ####2.2、调用ProtocolFilterWrapper类的invoke方法。 ####2.3、沿着过滤器链执行,知道执行真正的实现类invoker ####2.4、真正的invoker是DubboInvoker,它的doInvoke方法dubbo去找真正实现类去执行。这里面dubbo支持了同步和异步。

至此,调用端的逻辑交代完毕。

dubbo rpc的东西当然不止这些,其他别的东西还会陆续介绍。本文关注的是rpc 的主线,对代码的执行逻辑做一个交代。

© 著作权归作者所有

上一篇: 线程池
hyssop
粉丝 20
博文 102
码字总数 111521
作品 0
昌平
程序员
私信 提问
加载中

评论(1)

2
20PursueZ
一直想看源码
dubbo源码学习笔记----整体结构

dubbo核心包 config dubbo-config-api dubbo-config-spring remoting dubbo-remoting-netty dubbo-remoting-netty4 dubbo-remoting-mina dubbo-remoting-grizzly dubbo-remoting-p2p dubbo-......

春哥大魔王的博客
2018/01/13
270
2
聊聊dubbo的TPSLimiter

序 本文主要研究一下dubbo的TPSLimiter TPSLimiter dubbo-2.7.2/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/tps/TPSLimiter.java TPSLimiter定义了isAllowable方法......

go4it
06/23
53
0
聊聊dubbo的DubboSwaggerService

序 本文主要研究一下dubbo的DubboSwaggerService DubboSwaggerService dubbo-2.7.2/dubbo-rpc/dubbo-rpc-rest/src/main/java/org/apache/dubbo/rpc/protocol/rest/integration/swagger/Dubbo......

go4it
07/06
52
0
Dubbo 升级扩展 --Dubbo-G

Dubbo-G 详细介绍 Dubbo是一个被国内很多互联网公司广泛使用的开源分布式服务框架,即使从国际视野来看应该也是一个非常全面的SOA基础框架。作为一个重要的技术研究课题,在联想电商我们根据...

技术专家
2017/05/26
8.4K
23
聊聊dubbo的TimeoutFilter

序 本文主要研究一下dubbo的TimeoutFilter ListenableFilter dubbo-2.7.2/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java ListenableFilter声明实现Fil......

go4it
06/24
27
0

没有更多内容

加载失败,请刷新页面

加载更多

js如何控制table中的某一行动态置顶

两行代码搞定: $('#'+item.roadCode).fadeOut().fadeIn();//获取到需要置顶的行 $(".table").prepend($('#'+item.roadCode)); 其中,fadeOut()方法 作用 --- 从可见到隐藏 如下: prepend(......

码妞
59分钟前
4
0
四种解决Nginx出现403 forbidden 报错的方法

我是在在本地用虚拟机中通过yum安装nginx的,安装一切正常,但是访问时报403, 于是查看nginx日志,路径为/var/log/nginx/error.log。打开日志发现报错Permission denied,详细报错如下: 1....

dragon_tech
今天
3
0
获取RestResultResponse返回的值

Springboot项目,需要调其他服务的接口,返回值类型是RestResultResponse 打断点的结果集是这个 打印出来的getData(): [{id=3336b624-8474-4dd9-bd5b-c7358687c877, paraNo=104, para=Postpo...

栾小糖
今天
4
0
【小学】 生成10以内的加减法

#!/usr/bin/env python# coding: utf-8from random import randrange# 题目的最大数值R_MAX = 10# 生成的题目的数量R_PAGE = 70# 生成减法列表def get_sub_list():...

Tensor丨思悟
今天
11
0
JavaScript设计模式——适配器模式

  适配器模式是设计模式行为型模式中的一种模式;   定义:   适配器用来解决两个已有接口之间不匹配的问题,它并不需要考虑接口是如何实现,也不用考虑将来该如何修改;适配器不需要修...

有梦想的咸鱼前端
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部