搜索系统12:从solrj学习负载均衡的实现

我下面分析的solrj的版本是5.3.1,先看一下它的请求处理过程。


我们重点分析下这个LBHttpSolrClient.request方法,这个版本中有两个request方法,我这只分析request(final SolrRequest request, String collection)这个方法,通过这个就能看出原理了。

先看源码的注释,了解下这个方法的处理过程。
Tries to query a live server. A SolrServerException is thrown if all servers are dead.
If the request failed due to IOException then the live server is moved to dead pool and the request is
retried on another live server. After live servers are exhausted, any servers previously marked as dead
will be tried before failing the request.

翻译一下:

尝试去查询一个活着的服务器。如果所有服务器都挂了就抛一个SolrServerException。如果一个请求由于抛出了IOExceptiond而失败,那么这个活着的服务器就被移到了僵尸服务器池子里,并且这次请求会再尝试其它活着的服务器。在用完所有活着的服务器后,把前面标记为僵尸服务器中取出一个来尝试,直到请求失败。


这个处理是合理的,简单来说就是要尝试所有服务器,用一个dead pool来维护僵尸服务器。下面看一下代码实现,

一、尝试活着的服务器

timeAllowedNano、timeOutTime变量与isTimeExceeded方法是处理超时的,我们暂不设置超时,则isTimeExceeded永远不会为true,就是可忽略这几行。


int count = counter.incrementAndGet() & Integer.MAX_VALUE;

这个是计数器,每次来了就加1。但后面的按位与Integer.Max_VALUE有些奇怪,经验知道这是处理integer越界的。但到底是怎么算出来的?

先学习下AtomicInteger的知识,AtomicInteger的最大值是 Integer.MAX_VALUE的2倍( Integer把负值移到正值的范围),超过最大值后就变成负值最的大值,见下图。这时再去取模结果就会为负数,就错了。


按位与的处理是怎么解决这个问题的?先看一下Integer.MIN_VALUE与Integer.MAX_VALUE的区别(Integer.toBinaryString(Integer.MAX_VALUE)):

10000000000000000000000000000000
1111111111111111111111111111111

这就是说Integer的负值区,第一位二进制是1而正值区第一位二进制是0,Integer.MAX_VALUE+1=Integer.MIN_VALUE,AtomicInteger也是同样的道理。而AtomicInteger是Integer的二倍,当AtomicInteger的值为负值时,二进制形式就是前面多一个1,再与上Integer.MAX_VALUE(01111111111111111111111111111111),就是保留正数总数,去掉负数(前面的1)。这样,再取模的时候就一定是正数了。

这样的算法我想一定是有C语言背景的程序员写的,位操作用得很流畅。如果是我,那我就这样简单处理了。

int count = counter.incrementAndGet();
if(count= Integer.MAX_VALUE){
counter.set(0);
}
到最大值了就重新计数。

返回正题,现在取得了可用的服务器ServerWrapper wrapper,不费话,调用服务器request方法查数据。如果catch到SolrException或Exception,就直接返回了,这种应该是程序异常。如果catch到SolrServerException并且是IOException,这表示是SolrServer的性能有问题了,用moveAliveToDead加入到僵尸的服务池里(先从活着的里移除)。


二、 catch到SolrServerException或活着的服务为空时就要尝试僵尸的服务器。

如上忽略isTimeExceeded方法。wrapper.standard初始化时是true,后面是判断得到上面尝试过的服务器的情况,就退出本次循环。我暂时未分析到什么时候会出现这种情况,先忽略。再往下就是正常的请求,正常返回了就从僵尸池的移到活动的池子。如果再热抛出异常,就抛给客户端了。


这里已经有活动服务器池与僵尸服务器池的切换的功能了。上面代码有种情况是第一步每次请求都是活动的服务器,僵尸服务器没有机会被调用就正常返回的话,就会出现僵尸服务器不会自动恢复。所以在moveAliveToDead方法中有一个startAliveCheckExecutor方法会启动一个线程,定时去检测每台僵尸服务是否恢复,恢复就切换一下。


下面看一下检测的方法:

这里最关键的一行就zombieServer.client.query(solrQuery) , 这里就是去请求服务器返回结果。这个请求是不带core(collection)名称的,如果solr没有配defaultCore的话,这里就会报404错误,可能会导致即使是活着的服务也不会从僵尸队列移除。为什么说是可能呢?因为上文中的第二步就是catch到异常时会尝试僵尸队列,如果这个执行在检测代码之前,那么真正活着的服务就已经被出僵尸队列了。

查代码知道这个check方法是一分钟执行一次,每次如果catch到异常failedPings加一。再看catch中的 if (!zombieServer.standard && zombieServer.failedPings >= NONSTANDARD_PING_LIMIT) 这句话,NONSTANDARD_PING_LIMIT值是5,zombieServer.standard总共有四处引用,第一处是初始化为true,第二处是addZombie方法里置为false,第三处与第四处就是request方法与这里的查询。综合上面代码就知道这里其本都是false(在僵尸队列)。这句if的判断就是说,连续5分钟检测到这个服务都不可用,就会从僵尸队列移除,也就是被永久移除了。



最后我们再用流程图来总结一下solrj负载均衡的过程:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值