ITKeyword,专注技术干货聚合推荐

注册 | 登录

dubbo注册部分源码分析与我思考的miniDubbo结构

herriman 分享于 2016-07-29

推荐:(未完成)Dubbo源码分析(七):Dubbo内核实现之基于SPI思想Dubbo内核实现

SPI接口定义 定义了@SPI注解 package com.alibaba.dubbo.common.extension;import java.lang.annotation.Documented;import java.lang.annotation.ElementTyp

  前面分别写了二篇文章,介绍dubbo的源码与模拟现场场景的结构与调用分析。目前还缺少注册与统计模块的分析,所以又抽空看了一下注册部分,并思考如何在特定场景下实现一个简单的soa的管理的minidubbo。 

    说到分析源码,我一般先网上找几篇看看,可是总看的云里雾里,而且dubbo升级后有些变化,所以还是自己硬啃一下。时间仓促欢迎斧正,我看的源码是2.5.3。 
一、dubbo注册分析 

    网上的源码最大的问题是没有一个完整的思维模型引导,所以我先介绍我所了解的注册部分的思维模型。注册就相当于一个购物平台,总不能买卖双方总是直接调用吧,所以可以这么理解:调用方如果配置注册中心,就注册自己,并从平台查询,可以得到自己想到的实现服务的被调用方地址,这个是订阅过程,会不断返回一些被调用方的URL,而这些个URL就是真正的被调用方了,存起来并按一定负载规则选择一个当成直接调用就OK了。而被调用方也是注册自己,并从平台得到..些URL重新暴露。(这里没明白为要得到那些URL并重新doChangeLocalExport,反正也是订阅并传入一个监听器,但不影响整体理解)。而注册时的数据存放可以是zookeeper,radis,也可以是数据库,但注册,订阅等业务逻辑都是调用端与被调用端来实现的。 

推荐:Dubbo介绍2- 源码分析,通过schema启动服务

前言 spring2.5以后,spring支持自定义schema扩展xml配置。具体的spring schema细节,本文就不多说了。这篇文章就拿provider为例,介绍dubbo是如何启动服务的。


    调用方的一个接口,从注册中心找到多个实现 
    当调用端直接配置被调用端时,用dubboProtocol的refer方法就生成invoker,而当配置为注册中心时,调用方的接口就要按registryProtocol提供的refer生成invoker,而且一个接口配置了多个注册中心,并且有多个提供方时,那就比较麻烦了。我买一件商品,那么多商家提供,那当然弱水三千只取一瓢了,但可以随机,也可以选择最便宜的。 
    registryProtocol与RegistryDirectory是两个核心的业务逻辑类,另外根据注册中心的不同还有些不同的注册器与注册器工厂,比如zookeeper,radis的。 
    我们看到,registryProtocol做refer的时候得到相应的注册器,比如zookeeper的,再做dorefer的时候三件事,先是注册registry.register(*),再是订阅,订阅就是我扔个回调对象给你,你有事了通知回调对象就行了,我就知道了。代码就是directory.subscribe(*)内部是registry.subscribe(url, this);。最后就是cluster.join(directory); 

    总结一下,得到注册中心的地址后,调用方用zookeeper注册器来注册一下,并传入一个对象RegistryDirectory向注册器订阅可用的url,注册器会有一个线程不断的查找zookeeper的节点得到可用的url来通知RegistryDirectory。RegistryDirectory拿到这些url就一个个得到invokers,这些invokers的持有者RegistryDirectory会生成一个实现了invoker的代理对象。而客户端以后就调用这个invoker的代理对象了,代理对象会用策略从一堆真正的invoker中拿一个来用的。 


--------------------------《代码》-------------------------- 
拿到一个注册中心的真正一堆url后,转化成一堆invoker的重要语句如下: 
//把订阅的url转化为invoker就是RegistryDirectory类中的refreshInvoker方法中的这句 
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ; 
//转化过程就是用注册中心告诉你的真正的一堆url,用各自的协议来refer一下,生成一堆invoker。 
invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl); 
invoker代理对象找出一个invoker来用就是FailoverClusterInvoker中的下面这句: 
Invoker<T> invoker = select(loadbalance, invocation, copyinvokers, invoked); 
----------------------------《END》------------------------ 


疑问是:有几个注册中心,如果每个中心都查到有实现,那就有几个代理对象吗?那又多一层选择吗?一个调用先选择一个代理对象,代理对象中又选择出一个真正的invoker?还是把这些全部合并到一起了?这个细节还没有看。发现ReferenceConfig中有:invoker = cluster.join(new StaticDirectory(u, invokers));也是集群策略。 

解答:今天又看了一会代码,明白了。当我们从afterPropertiesSet()开始追踪调用端对象的诞生时,直到看这个方法createProxy(Map<String, String> map),最终生成调用端的代理对象。 
--------------------------《代码》-------------------------- 
//加载所有的注册中心地址 
List<URL> us = loadRegistries(false); 
... 
for (URL url : urls) { 
//每一个注册中心生成一个invoker,放在一起。注意一个注册中心生成的是一个集群failover的invoker。 
        invokers.add(refprotocol.refer(interfaceClass, url)); 

... 
//把每个注册中心的invoker再组成一个集群。这里不是failoverCluster了,是AvailableCluster了。 
invoker = cluster.join(new StaticDirectory(u, invokers)); 
------------------------------《END》------------------------- 

   就是说:一个注册中心得到的是集群,而多个注册中心是集群的集群。但是集群策略不一样。其实之前已猜测到了,只是对refprotocol有点困惑,所有的注册中心怎么用同一个变量refprotocol来refer呢?不同的注册中心协议不一样啊,有zookeeper,radis等。当然我知道是spi进来的,但总不能用一个变量吧。突然想起之前的文章中我还分析过spi的特别之处。阿里的spi得到的是一个适配器,是用生成代码编译方式的,具体适配是那个类,要根据参数定的。那就对了,注册中心的URL是不一样的,所以得到不同的类。 
    此刻又一想,不对,注册协议只有一个RegistryProtocol,不同的注册中心导致注册协议refer时用的注册器不一样。真是忙了几天别的,思维有点断,还好理顺了! 


二、注册中的集群策略与负载均衡算法汇总与应用场景 
DUBBO中的宝贝越挖越多,这也是发现的一批重要知识点,在分布式环境中用的非常多,都总结到一起了,可以对比学习使用。 

1.负载均衡算法--只要有集群都需要,在电商业务中,比如对不同级别的用户,权重也可能有差别,很多地方用的到。 
1.1 RandomLoadBalance: 
    如果权重一样,就是简单的random,如果有权重,那把权重累加起来,从中得到一个随机数,在循环中不断累减随机数,如果出现负数就是要的值。这个算法没有去找证明,但我相信没问题,碰到了直接用。 
1.2 ConsistentHashLoadBalance: 
   利用TreeMap实现的一致性HASH算法 

1.3 RoundRobinLoadBalance:即轮询调度算法。比如有ABCD四个invoker,依次调用ABCD ABCD ABCD ABCD A...。如果加上权重稍微复杂一点,比如权重AC是2,BD是1,那次序是:ABCDAC ABCDAC ABCDAC.....。 

2.集群策略 
2.1 AvailableCluster:这个简单,从invokers中随便拿一个isAvailable()的invoker.拿的时候不需要负载均衡策略。 

2.2 FailoverCluster:从invokers中按照负载均衡策略拿出来,可用就OK。不可用的话循环拿其它没拿过的。通用用于读操作,这个失败了换下一个。 

FailfastCluster:从invokers中按照负载均衡策略拿出来一个,就用它了。不行就抛出异常。常用于非幂等性的写操作。非幂等就是一次操作与多次操作结果不一样。 

2.3 FailbackCluster:从invokers中按照负载均衡策略拿出来一个,如果执行不成功,那记录下来,产生一个定时线程不断的重试。一般用于消息通知。 

2.4 ForkingCluster:从invokers中按照负载均衡策略拿出来多个来,谁先有结果就是谁,用线程池来执行,把结果写入一个LinkedBlockingQueue,如果都是异常,把最后一个异常写入。主线程等待一定时间后用poll来取第一个。 
  这是并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。 

2.5 FailsafeCluster:有异常时,也返回一个正常的空对象new RpcResult()。这个是失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。 

2.6 BroadcastCluster:广播方式,就是从invokers中循环执行所有的invoker。大家一个接一个都来做一遍调用。 


三、我思考的miniDubbo结构: 

   感觉Dubbo比较庞大,我们的应用基本都是web应用,用户不多,都是内部比较安全,会相互调用,而且调用可以都是http,那能否针对这种情况写一个小的soa管理呢?主要是把已有的业务方便的包装成对外的服务,服务还可以统一注册并统计调用情况,时间与用量等。实际上调用方用http协议传方法名,参数和获得返回值,提供方就是找到实现类,反射调用一下。简单点,调用异常返回统一码。 
    

推荐:Dubbo源码分析(八):Javassist字节码技术生成代理

Java动态编程的作用:      通过配置生成代码,减少重复编码和维护成本       我们常用到的动态特性主要是反射,在运行时查找对象属性、方法,修改作用域,通过

  前面分别写了二篇文章,介绍dubbo的源码与模拟现场场景的结构与调用分析。目前还缺少注册与统计模块的分析,所以又抽空看了一下注册部分,并思考如何在特定场景下实现一个简单的soa的管理的mi

相关阅读排行


用户评论

游客

相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。