RPC入门总结(九)Dubbo框架实现细节

转载:11. Dubbo原理解析-注册中心之接口介绍

转载:12. Dubbo原理解析-注册中心之基于dubbo协议的简单注册中心实现

转载:13. Dubbo原理解析-注册中心之Zookeeper协议注册中心

转载:14. Dubbo原理解析-集群&容错之Cluster

转载:15. Dubbo原理解析-集群&容错之目录服务Directory

转载:16. Dubbo原理解析-集群&容错之router路由服务

转载:17. Dubbo原理解析-集群&容错之负载均衡

一、Dubbo的注册中心

服务注册中心是Dubbo中的重要组成部分,服务的提供者将服务发布到注册中心,服务的使用着到注册中引用服务。Dubbo的注册中心提供了多种实现,其实现是基于dubbo的SPI的扩展机制的,使用者可以直接实现自己的注册中心

@SPI("dubbo")
public interface RegistryFactory {
   /**
    * 连接注册中心.
    * 连接注册中心需处理契约
    * 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。
    * 2. 支持URL上的username:password权限认证。
    * 3. 支持backup=10.20.153.10备选注册中心集群地址。
    * 4. 支持file=registry.cache本地磁盘文件缓存。
    * 5. 支持timeout=1000请求超时设置。
    * 6. 支持session=60000会话超时或过期设置。
    * @param url 注册中心地址,不允许为空
    * @return 注册中心引用,总不返回空
    */
    @Adaptive({"protocol"})
    RegistrygetRegistry(URL url);
}
RegistryFactory用来创建注册中心, 默认的注册中心是dubbo协议,由于阿里的注册中心并没有开源,dubbo协议注册中心只提供了一个简单实现。 开源dubbo的注册中心推荐使用zookeeper。这里我们主要去分析基于dubbo和zookeeper协议的注册中心实现及使用。服务接口定义

public interface RegistryService {
    void register(URL url);
    void unregister(URL url);
    void subscribe(URL url, NotifyListener listener);
    void unsubscribe(URL url, NotifyListener listener);
List<URL> lookup(URL url);
}
Register:注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则等数据。
注册需处理契约
     1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。
     2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。
     3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。
     4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。
     5. 允许URI相同但参数不同的URL并存,不能覆盖。
Unregister:取消注册
Subscribe:订阅符合条件的已注册数据,当有注册数据变更时自动推送
订阅需处理契
     1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。
     2. 当URL设置了category=routers,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。
     3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&version=1.0.0
     4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&group=*&version=*&classifier=*
     5. 当注册中心重启,网络抖动,需自动恢复订阅请求
     6. 允许URI相同但参数不同的URL并存,不能覆盖
     7. 必须阻塞订阅过程,等第一次通知完后再返回。
Unsubscribe:取消订阅
Lookup: 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,只返回一次结果

二、Dubbo的SimpleRegistryService实现

基于dubbo协议开源只是给出了默认一个注册中心实现SimpleRegistryService, 它只是一个简单实现,不支持集群,就是利用Map<String/*ip:port*/, Map<String/*service*/, URL>来存储服务地址,分为以下流程;

1. 注册中心启动

SimpleRegistryService本身也是作为一个dubbo服务暴露。

<dubbo:protocolport="9090" />
<dubbo:service interface="com.alibaba.dubbo.registry.RegistryService"ref="registryService" registry="N/A" ondisconnect="disconnect"callbacks="1000">
     <dubbo:methodname="subscribe"><dubbo:argument index="1" callback="true"/></dubbo:method>
     <dubbo:methodname="unsubscribe"><dubbo:argument index="1" callback="false"/></dubbo:method>
</dubbo:service>
<bean id="registryService"class="com.alibaba.dubbo.registry.simple.SimpleRegistryService" />
上面是暴露注册中心的dubbo服务配置,定义了注册中心服务的端口号,发布RegistryService服务。

 registry属性是”N/A”代表不能获取注册中心,注册中心服务的发布也是一个普通的dubbo服务的发布,如果没有配置这个属性它也会寻找注册中心,去通过注册中心发布,因为自己本身就是注册中心,直接对外发布服务,外部通过ip:port直接使用。
服务发布定义了回调接口, 这里定义了subscribe的第二个入参类暴露的回调服务供注册中心回调,用来当注册的服务状态变更时反向推送到客户端。
Dubbo协议的注册中心的暴露以及调用过程过程跟普通的dubbo服务的其实是一样的,可能跟绝大多数服务的不同的是在SimpleRegistryService在被接收订阅请求subscribe的时候,同时会refer引用调用方暴露的NotifyListener服务,当有注册数据变更时自动推送

2. 生产者发布服务

Dubbo协议向注册中心发布服务:当服务提供方,向dubbo协议的注册中心发布服务的时候,是如何获取,创建注册中心的,如何注册以及订阅服务的,下面我们来分析其流程。
看如下配置发布服务:

<dubbo:registry protocol=”dubbo” address="127.0.0.1:9090" />
<beanid="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
<dubbo:serviceinterface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
1. 指定了哪种的注册中心,是基于dubbo协议的,指定了注册中心的地址以及端口号
2. 发布DemoService服务,服务的实现为DemoServiceImpl
 
每个<dubbo:service/>在spring内部都会生成一个ServiceBean实例,ServiceBean的实例化过程中调用export方法来暴露服务

1. 服务发布的流程

1. 通过loadRegistries获取注册中心registryUrls

registry://127.0.0.1:9090/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.5.4-SNAPSHOT&owner=william&pid=7084&registry=dubbo×tamp=1415711791506
用统一数据模型URL表示:

  • protocol=registry表示一个注册中心url
  • 注册中心地址127.0.0.1:9090
  • 调用注册中心的服务RegistryService
  • 注册中心协议是registry=dubbo
2. 构建发布服务的URL

dubbo://192.168.0.102:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.4-SNAPSHOT&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=7084&side=provider×tamp=1415712331601

  • 发布协议protocol =dubbo
  • 服务提供者的地址为192.168.0.102:20880
  • 发布的服务为com.alibaba.dubbo.demo.DemoService
3. 遍历registryUrls向注册中心注册服务

给每个registryUrl添加属性key为export,value为上面的发布服务url得到如下registryUrl:

registry://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.5.4-SNAPSHOT&export=dubbo%3A%2F%2F192.168.0.102%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.5.4-SNAPSHOT%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26loadbalance%3Droundrobin%26methods%3DsayHello%26owner%3Dwilliam%26pid%3D7084%26side%3Dprovider%26timestamp%3D1415712331601&owner=william&pid=7084&registry=dubbo×tamp=1415711791506
4. 由发布的服务实例,服务接口以及registryUrl为参数,通过代理工厂proxyFactory获取Invoker对象,Invoker对象是dubbo的核心模型,其他对象都向它靠拢或者转换成它。
5. 通过Protocol对象暴露服务protocol.export(invoker)
通过DubboProtocol暴露服务的监听(不是此节内容)
通过RegistryProtocol将服务地址发布到注册中心,并订阅此服务

2. RegistryProtocol.export(Invoker)暴露服务

1. 调DubboProtocol暴露服务的监听
2. 获取注册中心getRegistry(Invoker)
URL转换, 由Invoker获取的url是registryURL它的协议属性用来选择何种的Protocol实例如RegistryProtocol, DubboProtocol或者RedisProtocol等等。 这里要通过URL去选择何种注册中心,所以根据registry=dubbo属性,重新设置url的协议属性得registryUrl

dubbo://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.5.4-SNAPSHOT&export=dubbo%3A%2F%2F192.168.0.102%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.5.4-SNAPSHOT%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26loadbalance%3Droundrobin%26methods%3DsayHello%26owner%3Dwilliam%26pid%3D5040%26side%3Dprovider%26timestamp%3D1415715706560&owner=william&pid=5040×tamp=1415715706529
RegistryFactory.getRegistry(url) 通过工厂类创建注册中心,RegistryFactory通过dubbo的spi机制获取对应的工厂类, 这里的是基于dubbo协议的注册中心,所以是DubboRegistryFactory
3. 获取发布url ,就是registryUrl的export参数的值

registryProviderUrl=dubbo://10.33.37.7:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.4-SNAPSHOT&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&owner=william&pid=6976&side=provider×tamp=1415846958825
4. DubboRegistry.register(registryProviderUrl)

5. 构建订阅服务overrideProviderUrl,我们是发布服务

6. 构建OverrideListener它实现与NotifyLisener,当注册中心的订阅的url发生变化时回调重新export
7. registry.subscribe(overrideProviderUrl, OverrideListener), 注册器向注册中心订阅overrideProviderUrl,同时将Override Listener暴露为回调服务,当注册中心的overrideProviderUrl数据发生变化时回调,
注册器DubboRegistry的registry,subscribe, unRegistry, unSubscribe都类似, 是一个dubbo的远程服务调用

3. 消费者引用服务

消费者使用如下的配置指明服务:

<dubbo:registry protocol=”dubbo” address="127.0.0.1:9098" />
<dubbo:reference id="demoService"interface="com.alibaba.d ubbo.demo.DemoService"/>
1. 指定了哪种的注册中心,是基于dubbo协议的,指定了注册中心的地址以及端口号
2. 引用远程DemoService服务
每个<dubbo:reference/>标签spring加载的时候都会生成一个ReferenceBean。


如上图ReferenceBean实现了spring的FactoryBean接口, 实现了此接口的Bean通过spring的BeanFactory.getBean(“beanName”)获取的对象不是配置的bean本身而是通过FactoryBean.getObject()方法返回的对象,此接口在spring内部被广泛使用,用来获取代理对象等等。这里getObject方法用来生成对远程服务调用的代理

1. loadRegistries()获取配置的注册中心的registryUrls
2. 遍历registryUrls集合,给registryUrl加上refer key就是要引用的远程服务

[registry://127.0.0.1:9098/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=2484&refer=application%3Ddemo-consumer%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D2484%26side%3Dconsumer%26timestamp%3D1415879965901&registry=dubbo×tamp=1415879990670]
3. 遍历registryUrls集合,使用Protocol.refer(interface,regist ryUrl)的到可执行对象invoker
4. 如果注册中心有多个的话, 通过集群策略Cluser.join()将多个invoke r伪装成一个可执行invoker, 这里默认使用available策略
5. 利用代理工厂生成代理对象proxyFactory.getProxy(invoker)

三、Dubbo的Zookeeper协议注册中心

开源dubbo推荐的业界成熟的zookeeper做为注册中心, zookeeper是hadoop的一个子项目是分布式系统的可靠协调者,他提供了配置维护,名字服务,分布式同步等服务。

Dubbo服务提供者配置

<dubbo:registry protocol=”zookeeper” address="127.0.0. 1:2181" />
<beanid="demoService" class="com.alibaba.dubbo.demo.provi der.DemoServiceImpl"/>
<dubbo:serviceinterface="com.alibaba.dubbo.demo.DemoServi ce" ref="demoService"/>
Dubbo服务消费者配置

<dubbo:registry protocol=”zookeeper” address="127.0.0. 1:2181" />
<dubbo:referenceid="demoService"interface="com.alibaba.dubbo.demo.DemoService"/>
客户端获取注册器

服务的提供者和消费者在RegistryProtocol利用注册中心暴露(export)和引用(refer)服务的时候会根据配置利用Dubbo的SPI机制获取具体注册中心注册器

Registry registry = registryFactory.getRegistry(url);
这里的RegistryFactory是ZookeeperRegistryFactory看如下工厂代码

public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
    public Registry createRegistry(URL url) {
        return new ZookeeperRegistry(url, zookeeperTransporter);
    }
}
这里创建zookeepr注册器ZookeeperRegistry。ZookeeperTransporter是操作zookeepr的客户端的工厂类,用来创建zookeeper客户端,这里客户端并不是zookeeper源代码的自带的,而是采用第三方工具包,主要来简化对zookeeper的操作,例如用zookeeper做注册中心需要对zookeeper节点添加watcher做反向推送,但是每次回调后节点的watcher都会被删除,这些客户会自动维护了这些watcher,在自动添加到节点上去。
接口定义:

@SPI("zkclient")
public interface ZookeeperTransporter {
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    ZookeeperClient connect(URL url);
}
默认采用zkClient, dubbo源码集成两种zookeeper客户端,除了zkClient还有一个是curator

ZookeeperRegistry注册器的实现

1.构造器利用客户端创建了对zookeeper的连接,并且添加了自动回复连接的监听器

zkClient = zookeeperTransporter.connect(url);
    zkClient.addStateListener(new StateListener() {
            public void stateChanged(int state) {
                if (state ==RECONNECTED)
                   recover();
          }
});
2.注册url就是利用客户端在服务器端创建url的节点,默认为临时节点,客户端与服务端断开,节点自动删除
zkClient.create(toUrlPath(url),url.getParameter(Constants.DYNAMIC_KEY,true));      
3.取消注册的url,就是利用zookeeper客户端删除url节点

zkClient.delete(toUrlPath(url));

4. 订阅url, 功能是服务消费端订阅服务提供方在zookeeper上注册地址,

这个功能流程跟DubboRegister不一样, DubboRegister是通过Dubbo注册中心实现SimpleResgiter在注册中心端,对url变换、过滤筛选然后将获取的provierUrl(提供者ulr)利用服务消费者暴露的服务回调在refer。

由于这里注册中心采用的是zookeeper,zookeeper不可能具有dubbo的业务逻辑,这里对订阅的逻辑处理都在消费服务端订阅的时候处理
1) 对传入url的serviceInterface是*,代表订阅url目录下所有节点即所有服务,这个注册中心需要订阅所有
2) 如果指定了订阅接口通过toCategoriesPath(url)转换需要订阅的url

如传入

url consumer://10.33.37.8/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=providers,configurators,routers&dubbo=2.5.4-SNAPSHOT&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=4088&side=consumer×tamp=1417405597808

转换成:

urls/dubbo/com.alibaba.dubbo.demo.DemoService/providers,/dubbo/com.alibaba.dubbo.demo.DemoService/configurators, /dubbo/com.alibaba.dubbo.demo.DemoService/routers
3) 设配传入的回调接口NotifyListener,转换成dubbo对zookeeper操作的ChildListener

4) 以/dubbo/com.alibaba.dubbo.demo.DemoService/providers为例创建节点:

zkClient.create(path, false);
但是一般情况下如果服务提供者已经提供服务,那么这个目录节点应该已经存在,Dubbo在Client层屏蔽掉了创建异常。 5) 以/dubbo/com.alibaba.dubbo.demo.DemoService/providers为例给节点添加监听器,返回所有子目录
List<String> children = zkClient.addChildListener(path, zkListener);
        if (children !=null) {urls.addAll(toUrlsWithEmpty(url, path,hildren));}
        toUrlsWtihEmpty用来配置是不是需要订阅的url,是加入集合
6) 主动根据得到服务提供者urls回调NotifyListener,引用服务提供者生成invoker可执行对象
5. 取消订阅url, 只是去掉url上的注册的监听器

四、Dubbo的集群和路由

1.Dubbo集群

Dubbo作为一个分布式的服务治理框架,提供了集群部署,路由,软负载均衡及容错机制,Dubbo Cluster将Directory中的多个Invoker伪装成一个Invoker, 对上层透明,包含集群的容错机制。


Cluster接口定义

@SPI(FailoverCluster.NAME)
public interface Cluster {
    @Adaptive
    <T> Invoker<T>join(Directory<T> directory) throws RpcException;
}
Cluster可以看做是工厂类, 将目录directory下的invoker合并成一个统一的Invoker,根据不同集群策略的Cluster创建不同的Invoker。

我们来看下默认的失败转移,当出现失败重试其他服务的策略,这个Cluster实现很简单就是创建FailoverCluseterInvoker对象:

public class FailoverCluster implements Cluster {
    public final static String NAME ="failover";
    public<T> Invoker<T> join(Directory<T> directory)throws RpcException{
        return new FailoverClusterInvoker<T>(directory);
    }
}

1)AvailableCluster: 获取可用的调用。遍历所有Invokers判断Invoker.isAvalible,只要一个有为true直接调用返回,不管成不成功
2)BroadcastCluster: 广播调用。遍历所有Invokers, 逐个调用每个调用catch住异常不影响其他invoker调用
3)FailbackCluster: 失败自动恢复, 对于invoker调用失败, 后台记录失败请求,任务定时重发, 通常用于通知
4)FailfastCluster: 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性操作
5)FailoverCluster: 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟
  • (1)   目录服务directory.list(invocation) 列出方法的所有可调用服务
  • (2)   根据LoadBalance负载策略选择一个Invoker
  • (3)   执行invoker.invoke(invocation)调用
  • (4)   调用成功返回
  •             调用失败小于重试次数,重新执行从3)步骤开始执行
  •             调用次数大于等于重试次数抛出调用失败异常
6)FailsafeCluster: 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。
7)ForkingCluster: 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
8)MergeableCluster: 分组聚合, 按组合并返回结果,比如菜单服务,接口一样,但有多种实现,用group区分,现在消费方需从每种group中调用一次返回结果,合并结果返回,这样就可以实现聚合菜单项

2. 集群目录服务Directory

集群目录服务Directory, 代表多个Invoker, 可以看成List<Invoker>,它的值可能是动态变化的,比如注册中心推送变更。集群选择调用服务时通过目录服务找到所有服务。

Directory的接口定义

public interfaceDirectory<T> extends Node {
    //服务类型
    Class<T>getInterface();
    //列出所有服务的可执行对象
    List<Invoker<T>>list(Invocation invocation) throws RpcException;
}
Directory有两个具体实现:

  1. StaticDirectory: 静态目录服务, 它的所有Invoker通过构造函数传入, 服务消费方引用服务的时候, 服务对多注册中心的引用,将Invokers集合直接传入 StaticDirectory构造器,再由Cluster伪装成一个Invoker,StaticDirectory的list方法直接返回所有invoker集合。
  2. RegistryDirectory: 注册目录服务, 它的Invoker集合是从注册中心获取的, 它实现了NotifyListener接口实现了回调接口notify(List<Url>)。比如消费方要调用某远程服务,会向注册中心订阅这个服务的所有服务提供方,订阅时和服务提供方数据有变动时回调消费方的NotifyListener服务的notify方法NotifyListener.notify(List<Url>) 回调接口传入所有服务的提供方的url地址然后将urls转化为invokers, 也就是refer应用远程服务


3. Dubbo集群路由

Router服务路由, 根据路由规则从多个Invoker中选出一个子集AbstractDirectory是所有目录服务实现的上层抽象, 它在list列举出所有invokers后,会在通过Router服务进行路由过滤。Router接口定义:

public interface Router extendsComparable<Router> {
    URL getUrl();
<T> List<Invoker<T>> route(List<Invoker<T>>invokers, URL url, Invocation invocation)throws RpcException;
}


ConditionRouter: 条件路由
我们这里简单分析下代码实现具体功能参考官方文档
条件表达式以 => 分割为whenRulethenRule
 
ConditionRouter创建,构造器初始

1)从url根据RULE_KEY获取路由条件、路由内容
2)rule.indexOf("=>") 分割路由内容
3)分别调用parseRule(rule) 解析路由为whenRulethenRules
 
ConditionRouter执行route方法

1)如果url不满足when条件即过滤条件, 不过滤返回所有invokers
2)遍历所有invokers判断是否满足then条件, 将满足条件的加入集合result
3)Result不为空,有满足条件的invokers返回
4)Result为空, 没有满足条件的invokers, 判断参数FORCE_KEY是否强制过滤,如果强制过滤则返回空, 否则返回所有即不过滤
 
ScriptRouter: 脚本路由
通过url的RULE_KEY参数获取脚本内容,然后通过java的脚本引擎执行脚本代码。
 
ScriptRouter创建,构造器初始化
1)从url获取脚本类型javascript, groovy等等
2)从url根据RULE_KEY获取路由规则内容
3)根据脚本类型获取java支持的脚本执行引擎
 
ScriptRouter执行route方法
1)执行引擎创建参数绑定
2)绑定执行的参数
3)执行引擎编译路由规则得到执行函数CompiledScript
4)CompiledScript.eval(binds) 根据参数执行路由规则
 
Dubbo提供了ConditionRouterFactory, ScriptRouterFactory来创建对应的路由,路由的规则从url的RULE_KEY参数来获取,路由规则可以通过监控中心或者治理中心写入注册中心
RegistryFactory registryFactory =ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry =registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181");
registry.register(URL.valueOf("condition://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule="+ URL.encode("http://10.20.160.198/wiki/display/dubbo/host = 10.20.153.10=> host = 10.20.153.11") + "));
 
Dubbo也支持通过FileRouterFactory从文件读取路由规则,将读取的规则设置到url的RULE_KEY参数上, 文件的后缀代表了路由的类型,选择具体的路由工厂 ConditionRouterFactory,ScriptRouterFactory来创建路由规则。

五、Dubbo的负载均衡

LoadBalance负载均衡, 负责从多个 Invokers中选出具体的一个Invoker用于本次调用,调用过程中包含了负载均衡的算法,调用失败后需要重新选择。
LoadBalance接口定义

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance{
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers,URL url, Invocation invocation)throws RpcException;
}
类注解@SPI说明可以基于Dubbo的扩展机制进行自定义的负责均衡算法实现,默认是随机算法
方法注解@Adaptive说明能够生成适配方法
Select方法设配类通过url的参数选择具体的算法, 在从invokers集合中根据具体的算法选择一个invoker

1. RandomLoadBalance: 随机访问策略,按权重设置随机概率,是默认策略
1)获取所有invokers的个数
2)遍历所有Invokers, 获取计算每个invokers的权重,并把权重累计加起来,每相邻的两个invoker比较他们的权重是否一样,有一个不一样说明权重不均等
3)总权重大于零且权重不均等的情况下
按总权重获取随机数offset = random.netx(totalWeight);
遍历invokers确定随机数offset落在哪个片段(invoker上)

4)权重相同或者总权重为0, 根据invokers个数均等选择

invokers.get(random.nextInt(length))

2. RoundRobinLoadBalance:轮询,按公约后的权重设置轮询比率
1)获取轮询key  服务名+方法名
获取可供调用的invokers个数length
设置最大权重的默认值maxWeight=0
设置最小权重的默认值minWeight=Integer.MAX_VALUE
2)遍历所有Inokers,比较出得出maxWeight和minWeight
3)如果权重是不一样的
根据key获取自增序列,自增序列加一与最大权重取模默认得到currentWeigth
遍历所有invokers筛选出大于currentWeight的invokers
设置可供调用的invokers的个数length
4)自增序列加一并与length取模,从invokers获取invoker
  
3. LeastActiveLoadBalance: 最少活跃调用数, 相同的活跃的随机选择,
活跃数是指调用前后的计数差, 使慢的提供者收到更少的请求,因为越慢的提供者前后的计数差越大。
活跃计数的功能消费者是在ActiveLimitFilter中设置的

1)获取可调用invoker的总个数
初始化最小活跃数,相同最小活跃的个数
相同最小活跃数的下标数组
等等
2)遍历所有invokers, 获取每个invoker的获取数active和权重
找出最小权重的invoker
如果有相同最小权重的inovkers, 将下标记录到数组leastIndexs[]数组中
累计所有的权重到totalWeight变量
3)如果invokers的权重不相等且totalWeight大于0
按总权重随机offsetWeight = random.nextInt(totalWeight)
计算随机值在哪个片段上并返回invoker

4)如果invokers的权重相等或者totalWeight等于0,均等随机

5. ConsistentHashLoadBalance:一致性hash, 相同参数的请求总是发到同一个提供者,当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页