背景介绍
爬虫系统:一台是control server,其他的100台做crawler。server每天定时分发采集任务。
问题出现:由于目标采集任务比较大,准备增加10台crawler。期望在不改变原有任务分配规
则的基础上,同时优先分配任务少的机器上,而且能够对任务较少的机器进行平均分配(尽可
能保证机器有任务而不空跑)。
解决方案
方案一:对已经分配任务的每台机器统计任务数和task_sum,加上待分配任务数dis_num,平均分到
每一台机器,得到一个分发任务后的平均任务数avg_task_num,对于已分配任务数少于avg_task_num
的机器,就分配。分配数为avg_task_num减去每台机器上的已分配任务数。如果任务数足够,就会优先
分配给任务数少的机器。
分析:这个方案乍一看是很合适的,任务会合适的分发完,而且分发量也会被合适的计算到,不会
出现任务集中分发在某台机器上的情况。
但是,如果任务量不够多的情况下,就会出现不能平均分发的情况。假设下面五台机器的任务数量为
[50,50,50,0,0],机器4和5为新加入crawler系统的机器,任务数和task_sum为150,在待分配任务数
dis_num分别为200,100,50,10的情况下每台机器分配后的任务数情况如下:
待分配任务数 dis_num |
分配后平均任务数avg_task_num |
机器1 | 机器2 | 机器3 | 机器4 | 机器5 |
200 | 70 | 50+20 | 50+20 | 50+20 | 0+70 | 0+70 |
100 | 50 | 50+0 | 50+0 | 50+0 | 0+50 | 0+50 |
50 | 40 | 50+0 | 50+0 | 50+0 | 0+40 | 0+10 |
10 | 32 | 50+0 | 50+0 | 50+0 | 0+10 | 0+0 |
上表中,每台机器的任务数情况表示为a+b的形式,其中a为该机器上已分配的任务数,b为实际可分配到的
任务数。当待分配任务数(dis_num)为200和100的时候,我们发现这个方案还可以优先并平均分配任务;
但是当待分配任务数(dis_num)为50的时候,我们可以发现,该方案可以优先为没有任务的机器4和5分配
任务,但是任务数量有一定的偏差;当待分配任务数(dis_num)为50的时候,我们发现由于分配的先后顺
序,机器5分配到任务数为0。这样并不是我们期望的结果,我们期望的是,如果待分配任务数(dis_num)为
10,为了不让机器空跑,我们希望对机器4和5各分配5个任务。这个问题该怎么解决呢?
此时,可能你更容易想到的就是,添加条件判断,如果有数量为0的机器,优先处理,并且平均分配。但是,
如果待分配任务量足够多,我们又该怎么确定给这些任务数为0的机器优先分配多少?如果优先分配多了,最后
其他机器就少了,crawler的负载不够均衡,如果优先分配少了,剩下很多怎么办?难道再执行一次分配么?
这样的思路似乎总是存在一些瑕疵,如果转换成代码,最后都要加一些额外的if语句来判断,如果考虑的不够全
面,我们的任务调度可能就会有很大的隐患。
方案二:我们可以简单的把问题转换一下:
图一:正常情况下理想的任务分配
图二:新加机器情况下的理想任务分配
我们把任务分配比做连通的水桶加水的过程,假设水桶是相连通的(理想的任意情况下都连通),水桶中或多或
少的有一些冰块(理想冰块不会浮动,并且冰和水的体积仍为1:1)。在加水后,最后的水平面肯定是水桶中最低
的位置(读者自行脑补,人往高处走,水往低处流!)。
如果把水桶视为我们crawler机器上的任务池,冰块视为已经分配给crawler机器的任务,加水就可以视为我们的
任务分配,加水后的水平面就完全可以等价于任务分配完成后的最少任务数,我称之为最低水准线(min_line)。
对于上面的任务分配示意图,如果我们在分配前可以计算出这个最低水准线(min_line),我们就能很容易实现理
想的任务分配。每台机器的实际分配任务量为最低水准线min_line与已分配任务数的差值,如果差值为负数,则不
进行任务分配。这个min_line能计算出来吗?这个嘛,当然是可以的。
min_line计算过程描述
total_sum:参与分配的机器上已经分配的任务数之和(假设所有的机器参与了分配,初始化为所有机器的任务数和)
dis_num:待分配的任务数
t_num:实际参与任务分配的机器数量(假设所有的机器参与了分配,初始化为所有机器数)
avg_num:参与分配的机器在完成分配后的任务数
思路:
1.min_line实际上就是 最后真正参与分配的avg_num=(total_sum+dis_num)/t_num,假设所有机器参与了分配。
2.首先检查假设参与分配的机器的已分配任务数是否全部比avg_num小遍历每台机器,把每台机器的已分配任务和avg_num比较,
如果有机器的已分配任务数大于avg_num,那么可以说明这台机器肯定没有参与最后的分配,需要需要把这台机器剔除掉。
否则min_line=avg_num。在完成一次遍历后,重新计算avg_num,重新执行本步骤。
总结
我只是在尝试一种可以更好的调度任务的规则而已,就像流水一样,真正优先、均衡地分配任务(至少理论上是这样)!