2万字长文带你细细盘点五种负载均衡策略( 九 )


文章插图
 
既然是一个 SPI 接口,那我们可以自己扩展一个一模一样的算法,只是在算法里面加入一点输出语句方便我们观察情况 。怎么扩展 SPI 接口就不描述了,只要记住代码里面的输出语句都是额外加的,此外没有任何改动即可,如下:

2万字长文带你细细盘点五种负载均衡策略

文章插图
 
整个类如下图片所示,请先看完整个类,有一个整体的概念后,我会进行方法级别的分析 。
图片很长,其中我加了很多注释和输出语句,可以点开大图查看,一定会帮你更加好的理解一致性哈希在Dubbo中的应用:
改造之后,我们先把程序跑起来,有了输出就好分析了 。
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
服务端代码如下:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
其中的端口是需要手动修改的,我分别启动服务在20881和20882端口 。
项目中provider.xml配置如下:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
consumer.xml配置如下:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
然后,启动在20881和20882端口分别启动两个服务端 。客户端消费如下:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
运行结果输出如下,可以先看个大概的输出,下面会对每一部分输出进行逐一的解读 。
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
好了,用例也跑起来了,日志也有了 。接下来开始结合代码和日志进行方法级别的分析 。
首先是doSelect方法的入口:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
从上图我们知道了,第一次调用需要对selectors进行put操作,selectors的 key 是接口中定义的方法,value 是 ConsistentHashSelector 内部类 。
ConsistentHashSelector通过调用其构造函数进行初始化的 。invokers(服务端)作为参数传递到了构造函数中,构造函数里面的逻辑,就是把服务端映射到哈希环上的过程,请看下图,结合代码,仔细分析输出数据:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
从上图可以看出,当 ConsistentHashSelector 的构造方法调用完成后,8个虚拟节点在哈希环上已经映射完成 。两台服务器,每一台4个虚拟节点组成了这8个虚拟节点 。
doSelect方法继续执行,并打印出每个虚拟节点的哈希值和对应的服务端,请仔细品读下图:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
说明一下:上面图中的哈希环是没有考虑比例的,仅仅是展现了两个服务器在哈希环上的相对位置 。而且为了演示说明方便,仅仅只有8个节点 。假设我们有4台服务器,每台服务器的虚拟节点是默认值(160),这个情况下哈希环上一共有160*4=640个节点 。
哈希环映射完成后,接下来的逻辑是把这次请求经过哈希计算后,映射到哈希环上,并顺时针方向寻找遇到的第一个节点,让该节点处理该请求:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
还记得地址为 468e8565 的 A 服务器是什么端口吗?前面的图片中有哦,该服务对应的端口是 20882。
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
最后我们看看输出结果:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
和我们预期的一致 。整个调用就算是完成了 。
再对两个方法进行一个补充说明 。
第一个方法是 selectForKey,这个方法里面逻辑如下图所示:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
虚拟节点都存储在 TreeMap 中 。顺时针查询的逻辑由 TreeMap 保证 。看一下下面的 Demo 你就明白了 。
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
第二个方法是 hash 方法,其中的 & 0xFFFFFFFFL 的目的如下:
2万字长文带你细细盘点五种负载均衡策略

文章插图
 
&是位运算符,而 0xFFFFFFFFL 转换为四字节表现后,其低32位全是1,所以保证了哈希环的范围是 [0,Integer.MAX_VALUE]:


推荐阅读