源码分析Ambari的DAG是如何做的

我认为Ambari最有趣的地方之一是如何计算DAG(Directed acyclic graph,有向无环图)

简述

先简要概括一下Ambari是如何确定执行流程的:

ambari server会根据集群的元数据信息,在执行某一个Operation时候建立一个Stage DAG,根据这个DAG,划分不同的Stage,Stage间有执行顺序关系,必须按着顺序来;再根据每一个Stage,生成Command的DAG,将Command下发给ambari agent执行,第一个Stage的所有Command全部执行成功,才会执行第二个Stage,依次类推。


相关的类

当然,有很多细节需要补充,我们先认识几个类,按理解的重要性排序:

RoleGraphNode

第一个类:RoleGraphNode,它可以看成DAG的一个点

public class RoleGraphNode {
  public RoleGraphNode(Role role, RoleCommand command) {
    this.role = role;
    this.command = command;
  }
  private Role role;
  private RoleCommand command;
  private int inDegree = 0;
  private List<String> hosts = new ArrayList<String>();
  private Map<String, RoleGraphNode> edges = new TreeMap<String, RoleGraphNode>();
  public synchronized void addHost(String host) {
    hosts.add(host);
  }
  public synchronized void addEdge(RoleGraphNode rgn) {
    if (edges.containsKey(rgn.getRole().toString())) {
      return;
    }
    edges.put(rgn.getRole().toString(), rgn);
    rgn.incrementInDegree();
  }
  private synchronized void incrementInDegree() {
    inDegree ++;
  }
  public Role getRole() {
    return role;
  }
  public RoleCommand getCommand() {
    return command;
  }
  public List<String> getHosts() {
    return hosts;
  }
  public int getInDegree() {
    return inDegree;
  }

  Collection<RoleGraphNode> getEdges() {
    return edges.values();
  }
  public synchronized void decrementInDegree() {
    inDegree --;
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("("+role+", "+command +", "+inDegree+")");
    return builder.toString();
  }
}

一个DAG的节点有两部分构成,Role和RoleCommand,

(1) Role: Role defines the components that are available to Ambari. 说白了,就是component。
(2) RoleCommand:一个枚举类,包含:INSTALL,UNINSTALL,START,STOP,EXECUTE,ABORT,UPGRADE,SERVICE_CHECK,
  /**
   * Represents any custom command
   */
  CUSTOM_COMMAND,

  /**
   * Represents any action
   */
  ACTIONEXECUTE

前面提到,Stage需要按顺序执行,那DAG如何确定顺序呢?

数据结构课都有上过,既然是一个有向无环图,那么先找到所有入度为0的nodes,这些nodes可以被划分在第一个Stage中,然后去掉这些nodes,以这些nodes为起点的边的终点的nodes的入度要-1,再找到所有入度为0的nodes,那么这些nodes可以被划分为第二个Stage,依次类推,就可以确定有多少个Stage和各个Stage的执行顺序。

在代码中是如何体现的呢?

每个RoleGraphNode都可以表示为一个(role, command, inDegree)三元组,比如(datanode, install, 0);
每个RoleGraphNode都有该节点的边的信息(Map

RoleCommandOrder

第二个类:RoleCommandOrder,它可以理解成是如何生成DAG的graph的规则:

This class is used to establish the order between two roles.

判定规则函数实现如下:

  /**
   * Returns the dependency order. -1 => rgn1 before rgn2, 0 => they can be
   * parallel 1 => rgn2 before rgn1
   * 
   * @param rgn1 roleGraphNode1
   * @param rgn2 roleGraphNode2
   */
  public int order(RoleGraphNode rgn1, RoleGraphNode rgn2) {
    RoleCommandPair rcp1 = new RoleCommandPair(rgn1.getRole(),
        rgn1.getCommand());
    RoleCommandPair rcp2 = new RoleCommandPair(rgn2.getRole(),
        rgn2.getCommand());
    if ((this.dependencies.get(rcp1) != null)
        && (this.dependencies.get(rcp1).contains(rcp2))) {
      return 1;
    } else if ((this.dependencies.get(rcp2) != null)
        && (this.dependencies.get(rcp2).contains(rcp1))) {
      return -1;
    } else if (!rgn2.getCommand().equals(rgn1.getCommand())) {
      return compareCommands(rgn1, rgn2);
    }
    return 0;
  }

给定两个node:node1和node2
1. 如果node1 -> node2,则返回-1;
2. 如果node1和node2不存在先后关系,则返回0;
3. 反之,如果node2 -> node1,则返回1;

谁先谁后是根据command的类型决定:
INSTALL -> START -> EXECUTE -> SERVICE_CHECK -> STOP

  private int compareCommands(RoleGraphNode rgn1, RoleGraphNode rgn2) {
    // TODO: add proper order comparison support for RoleCommand.ACTIONEXECUTE

    RoleCommand rc1 = rgn1.getCommand();
    RoleCommand rc2 = rgn2.getCommand();
    if (rc1.equals(rc2)) {
      //If its coming here means roles have no dependencies.
      return 0;
    }

    if (independentCommands.contains(rc1) && independentCommands.contains(rc2)) {
      return 0;
    }

    if (rc1.equals(RoleCommand.INSTALL)) {
      return -1;
    } else if (rc2.equals(RoleCommand.INSTALL)) {
      return 1;
    } else if (rc1.equals(RoleCommand.START) || rc1.equals(RoleCommand.EXECUTE)
            || rc1.equals(RoleCommand.SERVICE_CHECK)) {
      return -1;
    } else if (rc2.equals(RoleCommand.START) || rc2.equals(RoleCommand.EXECUTE)
            || rc2.equals(RoleCommand.SERVICE_CHECK)) {
      return 1;
    } else if (rc1.equals(RoleCommand.STOP)) {
      return -1;
    } else if (rc2.equals(RoleCommand.STOP)) {
      return 1;
    }
    return 0;
  }

这里要明确的是:
除了上面的规则,还有一类规则信息是config,比如有role_command_order.json这个配置文件给定的规则,这类规则通常规定了不同component之间的顺序关系,可以是一个service间的不同component,比如:

"HIVE_SERVER-START": [
  "HIVE_METADATA_DATABASE-START"
],

也可以是不同service间的不同component,比如:

"KAFKA_MANAGER-START": [
  "ZOOKEEPER_SERVER-START"
],

再比如有service的metainfo.xml这个配置文件给定的规则,规定了service间的依赖关系:

<requiredServices>
    <service>YARN</service>
    <service>HIVE</service>
    <service>HDFS</service>
</requiredServices>


RoleGraph

第三个类:RoleGraph,通过给定的nodes来划分Stage,提供了如下方法:

(1) public void build(Stage stage)
给定一个Stage,建立一个DAG

(2) public List<Stage> getStages()
给定nodes,建立Stages

(3) private synchronized void removeZeroInDegreeNode(String role)
去除入度为0的node

(4) private Stage getStageFromGraphNodes(Stage origStage,
      List<RoleGraphNode> stageGraphNodes)
给定一个Stage,和一些nodes,重建一个新的Stage

(5) public String stringifyGraph()
字符串表示DAG

这里要明确的是:有两种DAG
1. Stage间的DAG:根据nodes建立Stage DAG,即不同Stage的DAG;
2. Stage内部的DAG:根据给定Stage内部的nodes建立Task DAG,即不同Command的DAG;


调用关系

下面需要介绍调用关系,这些类是如何串起来和它们是如何被其它类调用的。
等待更新…


下一节希望可以介绍Ambari的调度…

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值