代码全集
源码回顾
调度中心触发任务之后,他的调用链如下
RemoteHttpJobBean> executeInternal > XxlJobTrigger > trigger ,
通过之前的分析xxl-job 源码解读 (二) , 我们可以了解到,xxl-job他的路由策略主要发生在trigger这个方法中
上面的代码主要讲了分片广播这个策略的实现以及xxl-job的其他路由策略的调用位置在哪里。
ExecutorRouteStrategyEnum枚举类
这个是xxl-job路由策略非常重要的一个类, 该类通过枚举的方式,把路由key, 和策略实现类进行了一个聚合、
分片广播
通过源码回顾,我们可以清晰的看到,当系统判断当前任务的路由策略是分片广播时, 就会遍历执行器的集群机器列表,
给每一台机器都发送执行消息,分片总数为集群机器数量,分片标记从0开始,上面的代码已经非常清楚了,此处不再赘述。
第一个
由上面对ExecutorRouteStrategyEnum的分析,我们可以看到,该策略对应的是 这个ExecutorRouteFirst执行策略类。 主要看routeRun 这个方法
最后一个
直接 从执行机集群列表的list里面取最后一个,源码如下
轮循
主要看ExecutorRouteRound这个类里面的代码
总结:这里主要是通过一个ConcurrentHashMap来记录每个任务对应的执行次数,维护一个count值, 通过这个count值 对集群机器大小求于,得到list下标。
最终得到轮循的那台机器。
问题: 既然次数是通过维护一个count值,通过list.get(count%size) 的这种方式获取机器地址,为啥要用求于的方式? 而不是用直接list.get(count)这种方式?
随机
随机这个策略比较简单,通过在集群列表的大小内随机拿出一台机器来执行,比较简单,此处不再赘述
最不经常使用 (LFU)
单个JOB对应的每个执行器,使用频率最低的优先被选举
总结: 通过一个HashMap存储每个任务对应的 机器执行次数,然后通过hashMap value排序, 得到执行次数最小的那个,也就是得到了最不经常使用的那台机器
最近最久未使用(LRU)
单个JOB对应的每个执行器,最久为使用的优先被选举 , 此处使用的是linkedHashMap来实现LRU算法的。通过linkedHashMap的每次get/put的时候会进行排序,最新操作的数据会在最后面。 从而取第一个数据就代表是最久没有被使用的
总结:此处主要是利用了LinkedHashMap的排序特性, get/put时都会对资源重排。 最久没有被操作过的数据,就会排在前面。
一致性Hash
在讲这个策略之前,先说一下一致性Hash算法 ,
先构造一个长度为2^32的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 2^32-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值计算得到其Hash值(其分布也为[0, 2^32-1]),接着在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。
例:
为什么需要一致性hash
比如你有 N 个 cache 服务器(后面简称 cache ),那么如何将一个对象 object 映射到 N 个 cache 上呢,你很可能会采用通用的方法计算 object 的 hash 值,然后均匀的映射到到 N 个 cache ;hash(object)%N
一切都运行正常,再考虑如下的两种情况;
1 一个 cache 服务器 m down 掉了(在实际应用中必须要考虑这种情况),这样所有映射到 cache m 的对象都会失效,怎么办,需要把 cache m 从 cache 中移除,这时候 cache 是 N-1 台,映射公式变成了 hash(object)%(N-1) ;
2 由于访问加重,需要添加 cache ,这时候 cache 是 N+1 台,映射公式变成了 hash(object)%(N+1) ;
1 和 2 意味着什么?这意味着突然之间几乎所有的 cache 都失效了。对于服务器而言,这是一场灾难,洪水般的访问都会直接冲向后台服务器;
再来考虑第三个问题,由于硬件能力越来越强,你可能想让后面添加的节点多做点活,显然上面的 hash 算法也做不到。
有什么方法可以改变这个状况呢,这就是 consistent hashing…
源码分析:
分组下机器地址相同,不同JOB均匀散列在不同机器上,保证分组下机器分配JOB平均;且每个JOB固定调度其中一台机器;
这个地方使用的Hash方法是作者自己写的,因为String的hashCode可能重复,需要进一步扩大hashCode的取值范围
题外扩展
问题:
添加节点D
添加节点D,但是节点D在A和B的中间,仅能分担一点点的压力,
所以需要引入虚拟节点的思想,解决一致性hash算法分布不均导致负载不均的问题。一个真实节点对应若干个虚拟节点,当key被映射到虚拟节点上时,则被认为映射到虚拟节点所对应的真实节点上。
例:
节点A: A1 , A2 , A3 , A4
节点B: B1 , B2 , B3 , B4
节点C: C1, C2, C3, C4
虚拟节点分散,假如A服务器宕机了,那么A1,A2,A3,A4,虚拟节点失效,新来的请求会分散到服务器B,和服务器C一起分摊,解决了由于节点失效造成的负载不均的问题。
故障转移
这个策略比较简单,遍历集群地址列表,如果失败,则继续调用下一台机器,成功则跳出循环,返回成功信息
忙碌转移
这个策略更上面那个故障转移的原理一致,只不过不同的是,故障转移是判断机器是否存活, 二忙碌转移是想执行器发送消息判断该任务对应的线程是否处于执行状态。
看一下执行器那边的idleBeat代码实现
领取专属 10元无门槛券
私享最新 技术干货