我认为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的调度…