真*dubbo 源码分析(一)

这一部分主要是解析dubboxml 的标签,以及referenceBean的动态代理,注册zkclient 以及zk 的监听器

个人理解,如有不对还望指出,一同进步

 

首先,根据dubbo 自定义的继承于namespacehandlesupport 将会去解析 dubbo xml 里面的各个标签。 为什么会提交给namespacehandlesupport 呢?准确的说应该是dubbonamespacehandle 。  因为在dubbo的jar 包下面有个META-INF 下面有个spring.handlers 和spring.schemas, handler 里面写着交给哪个类去处理,schemas 顾名思义是标签的规则。

 

当运行dubbo 项目的时候, 他就会先跳到DubboNamespaceHandler 

 

这个里面的init()方法中 会把各种标签都交给DubboBeanDefinitionPaser 处理,每一个标签就会掉对应的’elementName’方法一次,这里elementName的意思是图上registerBeanDefinitionParser()方法的第一个参数。这里经过老师的讲解,觉得这里做的不好,因为全部交给DubboBeanDefinitionParser来做的话里面会有很多的判断。

用来判断很多的类型等等,所以觉得应该这样写比较好

 

每个标签交给不同的解析类去解析,可读性会好一点。

之后跳到DubbobeanDefinationParse中的parse方法

 

Element 就是dubbo xml 中的元素标签,parserContext是我们等会要注入的springioc容器

beanClass我刚开始以为是interface 后来发现实际上是dubbo标签的类型class com.alibaba.dubbo.config.ConsumerConfig比如这个, 实际上是上上图的registerBeanDefinitionParser()方法中第二个参数的 参数的类的全路径名。

如果我配置了一个<dubbo:reference id="userDao" interface="mydubboprovide.UserDao" check="false" />

那么beanClass就是class com.alibaba.dubbo.config.spring.ReferenceBean, id就是userDao

 

这边一个判断是看看springioc 容器中有没有和这个id 一样的被代理类, 没有的话就将id作为key,beanDefinition 作为value 注入了spring 容器中。

其他的属性 像interface也是通过这个方法将属性注入beanDefinition中,然后

 

从com.alibaba.dubbo.config.spring.ReferenceBean拿到所有的方法,找到已set方法开头的方法, 说白了 这就是set注入。 比如有个方法名称为setInteface的方法,如果这个方法的长度大于3并且以set开头,方法类型是公共的,方法有且只有一个参数, 那么他会获得这个方法的参数属性, 并且用’beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);拿到与之匹配的get方法。但是get方法就判断了是不是空,修饰符是否为public 判断完了continue;完全看不懂有什么用。

 

Set后面跟的方法名字不是图上那三个的话, 直接从标签上面取得,

之后会调到referenceBean 的 实现类InitializingBeanafterPropertiesSet()方法。顾名思义实在属性设置完成后调到这个方法的。这个方法我看了一下就是设置zk 注册地址,monitor 等等。然后继续走

实现类的FactoryBean getObject()方法。

这是做什么呢?实际上返回了一个ReferenceBean的代理实例。即对ReferenceBean的动态代理。走到get()方法里面发现里面有个appendPropertiesconfig)方法config是这个 <dubbo:reference singleton="true" interface="mydubboprovide.UserDao" generic="false" check="false" id="userDao" />

 

里面通过这个方法又开始找set方法, 和上面一样,只不过如果system里面有就调用method.invoke方法貌似。 (这个不太清楚)。

再回到init()方法中 他将renference中的各个属性啊比如interface id interface中的method等等 放入一个map中然后去调用createproxy 方法,其实是将全局配置加载进map中然后传过去。createproxy 方法中会先看看是不是injvm的,如果是就本地调用。不是的话然后会看到有个loadRegistry方法。里面会将zk 地址放入到registries中。有个UrlUtils.parseURLs(address, map)方法这个会拼装出一个Url, 然后如果修改zk协议为registry协议。(就是url本来是zookeeper://.... 变成了 registry://....)并且是协议中多个属性 registry=zookeeper;

 

然后在给monitor和createproxy(map)方法中的map 放入url.

 

然后在调用 refprotocol;

 

Refprotocol运用了spi 技术。

这里url是registry 开头所以调到了registryProtocol的 refer方法。

 

,这里先看看有没有registry参数,会发现registry=zookeeper,然后移除registry参数,url变成了zookeeper://...

这里的registryFactory.getRegistry(url) 后面就是建立zk 链接了。

这里面首先会尝试着从缓存中看看有没有这个值,首次当然是没有

那么就调用createRegistry();

 

这个方法会调到ZookeeperRegistry();zookeeperTransporter 怎么来的呢? spi

注册中心挂掉也能调用服务

上面的super(url)往上翻会做什么呢?这边做了一个很重要的事情,这个一直往上翻,翻到AbstractRegistry类中。这就是为什么dubbo的注册中心挂掉还能够调用服务。

 public AbstractRegistry(URL url) {
        setUrl(url);
        // 启动文件保存定时器
        syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
//找到本地文件 文件名如下
        String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
        File file = null;
        if (ConfigUtils.isNotEmpty(filename)) {
            file = new File(filename);
            if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
                if(! file.getParentFile().mkdirs()){
                    throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
                }
            }
        }
        this.file = file;
// 将此文件加载到properties 中
        loadProperties();
//notify 一下
        notify(url.getBackupUrls());
    }


    private void loadProperties() {
        if (file != null && file.exists()) {
            InputStream in = null;
            try {
                in = new FileInputStream(file);
                properties.load(in);
                if (logger.isInfoEnabled()) {
                    logger.info("Load registry store file " + file + ", data: " + properties);
                }
            } catch (Throwable e) {
                logger.warn("Failed to load registry store file " + file, e);
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        logger.warn(e.getMessage(), e);
                    }
                }
            }
        }
    }

 protected void notify(URL url, NotifyListener listener, List<URL> urls) {
        if (url == null) {
            throw new IllegalArgumentException("notify url == null");
        }
        if (listener == null) {
            throw new IllegalArgumentException("notify listener == null");
        }
        if ((urls == null || urls.size() == 0) 
                && ! Constants.ANY_VALUE.equals(url.getServiceInterface())) {
            logger.warn("Ignore empty notify urls for subscribe url " + url);
            return;
        }
        if (logger.isInfoEnabled()) {
            logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
        }
        Map<String, List<URL>> result = new HashMap<String, List<URL>>();
        for (URL u : urls) {
            if (UrlUtils.isMatch(url, u)) {
            	String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
            	List<URL> categoryList = result.get(category);
            	if (categoryList == null) {
            		categoryList = new ArrayList<URL>();
            		result.put(category, categoryList);
            	}
            	categoryList.add(u);
            }
        }
        if (result.size() == 0) {
            return;
        }
        Map<String, List<URL>> categoryNotified = notified.get(url);
        if (categoryNotified == null) {
            notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
            categoryNotified = notified.get(url);
        }
        for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
            String category = entry.getKey();
            List<URL> categoryList = entry.getValue();
            categoryNotified.put(category, categoryList);
//notify 中 会将文件保存一下
            saveProperties(url);
            listener.notify(categoryList);
        }
    }

zookeeperTransporter.connect(url),这个跟了一下,结合addStateListener看 贼有意思。

zookeeperTransporter.connect(url)意思就是新建一zkclient

 

这边 很明显匿名内部类,clientsubscribeStateChange 会把这个监听器放入zkClient 的 一个Set 中,这个Set 专门用来存储注册进来的监听器。stateChanged 方法执行后会遍历Set,每个监听器都调用他的statechanged方法 handlenewSession 说明session 不一样了, 那不就是断了要重新连了么, statechanged 方法就会调到上上图的recover方法。我们回到进来的refer方法,往下走doRefer。

 

再往下就是zk 的notify 了。以后写

 

转载于:https://my.oschina.net/u/4042146/blog/2980098

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值