这篇文章主要分析公平调度器的公平排序算法,基于hadoop-2.3.0-cdh5.0.0




首先,了解这几个概念:


  • 资源需求量:当前队列或者应用希望获得的资源的总量。
  • 最小份额:队列的最小共享量在配置中指定。应用的最小共享量为0。
  • 资源使用量:当前队列或者应用已经分配到的总资源。
  • 权值:队列的权重值在配置中指定。在开启sizebasedweight特性的情况下,应用的权重=(log2(资源需求量))*优先级*调整因子。优先级当前都是1,当应用运行超过5分钟,调整因子为3。




排序只需了解对于两个比较体的比较算法,然后对返回值升序排序。这里的比较体是队列或应用。


不说什么比较体,暂且称为队列吧,当然这个算法同样应用与队列内部应用的排序。




  • 首先,计算两个队列各自的资源使用量 < min(资源需求量,最小份额),即是否饥饿。
  • 然后,饥饿的队列优先。
  • 对于两者都饥饿的情况下,需要计算资源分配比,结果小者优先。资源分配比=资源使用量/min(资源需求量,最小份额, 1)
  • 当都不饥饿时,需要计算资源使用权值比,结果小者优先。资源使用权重比=资源使用量/权值
  • 如果资源分配比或权值比相等,先提交的优先。


公平排序算法决定的是谁可以先获得资源,在资源需求量一致的情况下,最小份额越大且资源使用量较小有较高的优先级来获得资源,保证不长时间处于饿死状态。注意权值是在两者都不饥饿的前提下起作用,即两个队列的资源使用量 >= min(资源需求量,最小份额).




下面看看相关代码吧


队列内部的应用排序算法默认采用fair排序,即与队列的排序相同。


/**
   * Compare Schedulables via weighted fair sharing. In addition, Schedulables
   * below their min share get priority over those whose min share is met.
   * 
   * Schedulables below their min share are compared by how far below it they
   * are as a ratio. For example, if job A has 8 out of a min share of 10 tasks
   * and job B has 50 out of a min share of 100, then job B is scheduled next,
   * because B is at 50% of its min share and A is at 80% of its min share.
   * 
   * Schedulables above their min share are compared by (runningTasks / weight).
   * If all weights are equal, slots are given to the job with the fewest tasks;
   * otherwise, jobs with more weight get proportionally more slots.
   */
  private static class FairShareComparator implements Comparator
    
    
     
     ,
      Serializable {
    private static final long serialVersionUID = 5564969375856699313L;

    @Override
    public int compare(Schedulable s1, Schedulable s2) {
      double minShareRatio1, minShareRatio2;
      double useToWeightRatio1, useToWeightRatio2;
      //计算两个队列各自的资源使用量 < min(资源需求量,最小份额),即是否饥饿。
      Resource minShare1 = Resources.min(RESOURCE_CALCULATOR, null,
          s1.getMinShare(), s1.getDemand());
      Resource minShare2 = Resources.min(RESOURCE_CALCULATOR, null,
          s2.getMinShare(), s2.getDemand());
      boolean s1Needy = Resources.lessThan(RESOURCE_CALCULATOR, null,
          s1.getResourceUsage(), minShare1);
      boolean s2Needy = Resources.lessThan(RESOURCE_CALCULATOR, null,
          s2.getResourceUsage(), minShare2);
      Resource one = Resources.createResource(1);
      //计算资源分配比,分母不会小于1
      minShareRatio1 = (double) s1.getResourceUsage().getMemory()
          / Resources.max(RESOURCE_CALCULATOR, null, minShare1, one).getMemory();
      minShareRatio2 = (double) s2.getResourceUsage().getMemory()
          / Resources.max(RESOURCE_CALCULATOR, null, minShare2, one).getMemory();
      //计算资源使用权值比
      useToWeightRatio1 = s1.getResourceUsage().getMemory() /
          s1.getWeights().getWeight(ResourceType.MEMORY);
      useToWeightRatio2 = s2.getResourceUsage().getMemory() /
          s2.getWeights().getWeight(ResourceType.MEMORY);
      int res = 0;
      //饥饿者优先
      if (s1Needy && !s2Needy)
        res = -1;
      else if (s2Needy && !s1Needy)
        res = 1;
      else if (s1Needy && s2Needy)
      //两者都饥饿的情况下,比较资源分配比
        res = (int) Math.signum(minShareRatio1 - minShareRatio2);
      else
        // Neither schedulable is needy
        //比较资源使用权值比
        res = (int) Math.signum(useToWeightRatio1 - useToWeightRatio2);
      if (res == 0) {
        // Apps are tied in fairness ratio. Break the tie by submit time and job
        // name to get a deterministic ordering, which is useful for unit tests.
        res = (int) Math.signum(s1.getStartTime() - s2.getStartTime());
        if (res == 0)
        //这里是如果二者同时启动的,就按字典序比较名称
          res = s1.getName().compareTo(s2.getName());
      }
      return res;
    }
  }




可以看出,前面可配置的几个参数中(最小份额和权值,其它的在运行过程中都是可变的),对排序结果影响的顺序是先最小份额,再权值。这些参数是配合着使用,还是只保留一个变量原则,还需要看应用的场景。


比如,你需要的是保证相对的公平,追求地位平等,当然也可以把他们的配置都做成一样的。但是这样却没有考虑到运行时的一些因素,如集群繁忙情况、每个队列的资源使用量以及需求量等都是可变因素。所以,最好是最小份额和权值配合着使用,可以遵循“谁愿意共享得越多,就可以获得更多的资源”的原则,即最小份额与权值成反比。


另外一种情况,你需要有一个等级分明、两级分化的环境,那就有人说了,怎么不直接用另一个调度器 Capacity Scheduler ?当然如果你不考虑它配置的苛刻“所有队列的容量之和应小于100”,它是可以满足的。而公平调度没有这个限制,无论多么不合理的配置都能够处理。好吧,说正题,这时最好统一最小份额配置,通过调节权值来体现两极分化。



参考资料:

  1. YARN ResourceManager调度器的分析
  2. Hadoop MapReduce Next Generation - Fair Scheduler