YARN的約束化標籤支援
前言
在比較早期的時候,YARN就已經實現了具有分片功能的node label功能,以此確保應用資源的使用隔離。我們可以理解為這種標籤是單一維度的,假設我們有多維度標籤使用的需求時,這種node label就不是那麼好用了。當然,你可以說,我們可以構建多個標籤,一個節點同時賦予多個維度標籤,但是其實這同樣不會那麼好操作。今天筆者要闡述的就是YARN node label特性的升級版特性:Constraint nodel label(約束化標籤)。簡單的理解,我們可以把它理解為是一種個性化標籤。
Constraint node label
約束化標籤相較於原本標籤功能,它的靈活性大大提升了。
首先,它是支援多維度,多型別標籤。比如標籤可以是boolean型,int型或者string型。定義多個標籤型別是為了可以表示不同的維度含義,比如我們可以用boolean型別的標籤來表示某節點是否是期待的作業系統型別,比如!Windows的含義就是當前的作業系統不是Windows系統。int型別的我們可以表示節點磁碟數量資訊等。通過上述多型別的標籤表示,可以精確具體地描述出一個節點的特徵。
第二點,基於多維度標籤的表示式匹配。因為約束化標籤是支援多型別的,對應條件匹配模式當然也不僅僅是隻有int或not in這樣的關係。它還可以支援如下的條件操作原語文
1.包含關係判斷: in, not in
2.值大小判斷:<,>,>=,=,<=
3.條件運算操作關係:&&,//
通過以上3種條件的拼接組合,我們可以組合出非常靈活的條件判斷規則,比如下面這個例子:NUM_DISK > 3 && !WINDOWS上述條件表示的含義是節點需要同時滿足磁碟數大於3並且作業系統不是WINDOWS系統2個特徵。這個表示式很好的體現了約束化標籤中“約束”的含義。基於多維度的特徵定義和條件過濾,使用者可以做出更精確的標籤識別。
約束化標籤實現原理
下面我們來了解約束化標籤的具體實現。在程式碼實現層面,我們如何來設計並實現約束化標籤的功能呢?
要實現這個功能,我們首先要明確與約束化標籤相關的一些概念,然後定義出相應類以及內部方法。以下是目前YRAN約束化標籤對此的具體設計。
第一個,約束值類,主要包含實際值以及比較操作值,比如long型別的約束值類定義如下:
static class LongConstraintValue implements ConstraintValue {
// 標籤值
private long value = 0;
// 與外部值的比較操作運算子
private ExpressionCompareOp compareOp;
...
/**
* 與外部值的比較方法
*/
@Override
public boolean matches(ConstraintValue targetValue) {
assert (targetValue instanceof LongConstraintValue);
double nodeConstraintVal = ((LongConstraintValue) targetValue).value;
// 根據具體操作符做值的比較
switch (compareOp) {
case EQUAL:
return value == nodeConstraintVal;
case NOT_EQUAL:
return value != nodeConstraintVal;
case GREATER_OR_EQUAL:
return nodeConstraintVal >= value;
case GREATER_THAN:
return nodeConstraintVal > value;
case LESS_THAN:
return nodeConstraintVal < value;
case LESS_OR_EQUAL:
return nodeConstraintVal <= value;
default:
return false;
}
}
當然,對於string型別,就是EQUAL和NOT_EQUAL型別的。
第二個,操作符型別,就是程式碼裡裡提到的,目前總共我們會涉及到以下可能用到的操作符語義。
public enum ExpressionCompareOp {
LESS_THAN, // '<' or 'lt'
LESS_OR_EQUAL, // '<=' or 'le'
EQUAL, // '==' or 'eq'
NOT_EQUAL, // '!=' or 'ne' or 'ene' (for exists but not equal)
GREATER_OR_EQUAL, // '>=' or 'ge'
GREATER_THAN, // '>' or 'gt'
IN, // 'in'
NOT_IN, // 'not_in'
EXISTS, // no operand only if the name is specified
NOT_EXISTS, // '! <constrainName>'
}
第三個,約束比較型別,我們可以理解為它是一個策略類,它定義了不同型別值進行比較的方式,根據輸入不同的比較原語操作符。下面是此類的介面定義:
/**
* 定義了值進行比較的方式,根據輸入不同的比較原語操作.
*/
public interface ConstraintType {
/**
* 型別名
*/
String getConstraintTypeName();
/**
* 支援的操作原語
*/
Set<ExpressionCompareOp> getSupportedCompareOperation();
/**
* 根據輸入操作原語,返回具體約束值類
*/
ConstraintValue getConstraintValue(ExpressionCompareOp compareOp)
throws YarnException;
}
我們來看其中的一個具體子類,
public class LongConstraintType implements ConstraintType {
// 此型別策略可支援的所以操作符
private Set<ExpressionCompareOp> supportedOps =
new HashSet<>(Arrays.asList(ExpressionCompareOp.values()));
@Override
public String getConstraintTypeName() {
return ConstraintsUtil.LONG_CONSTRAINT_TYPE;
}
@Override
public Set<ExpressionCompareOp> getSupportedCompareOperation() {
return supportedOps;
}
@Override
public ConstraintValue getConstraintValue(ExpressionCompareOp compareOp) {
// 根據比較操作符,返回不同的約束值類,然後約束值裡再進行具體的比較操作
switch (compareOp) {
case IN:
return new LongSetConstraintValue(true);
case NOT_IN:
return new LongSetConstraintValue(true);
default:
return new LongConstraintValue(compareOp);
}
}
第四點,約束特徵表示式,約束特徵表示式可以理解為就是一條完整的表示式條件,它包括了各個子條件約束值例項,以及連線運算表示式(與,或關係),如下:
public class ConstraintExpressionList extends ConstraintExpression {
/**
* 操作運算子,與,或操作
*/
@Private
@Unstable
public static enum Operator {
AND, OR
}
private Operator operator;
// 包含的子表示式,因為可能存在巢狀的條件判斷
private List<ConstraintExpression> expressionList =
new ArrayList<ConstraintExpression>();
public ConstraintExpressionList(ConstraintExpression... expressions) {
this(Operator.AND, expressions);
}
public ConstraintExpressionList(Operator op,
ConstraintExpression... expressions) {
this.operator = op;
this.expressionList =
new ArrayList<ConstraintExpression>(Arrays.asList(expressions));
}
...
/**
* 條件匹配判斷
*/
@Override
public boolean evaluate(Map<String, ConstraintValue> nodeConstraintMappings) {
for (ConstraintExpression constraintExpression : expressionList) {
if (constraintExpression.evaluate(nodeConstraintMappings)) {
// 操作符如果是或關係,有表示式判斷成立即為成功
if (operator == Operator.OR) {
return true;
}
} else if (operator == Operator.AND) {
// 操作符如果是與關係,有表示式判斷不匹配即為成功
return false;
}
}
// 沒有條件判斷,直接比操作符
return (operator == Operator.AND);
}
因為在表示式中,存在巢狀條件的可能,所以這裡表示式有以下2兩類:
- LIST列表型別,LIST型別表示式裡面可能存在子列表型或者COMPARE型。
- COMPARE操作比較型。
比如下面這個表示式,就是LIST型別和COMPARE表示式的組合。
(NUM_DISK >= 3 && !WINDOWS) || JDK_VERSION >= 1.8
上述表示式,可以看做一個大LIST型別表示式 = (小LIST表示式(2個COMPARE表示式組成))+ COMPARE表示式。
所以在表示式定義這塊,最為複雜和重要的其實是表示式的解析這塊。由於篇幅有限,筆者這裡就不展開具體闡述了,解析類程式碼已上傳至文章末尾的連結處,感興趣的同學可以深入繼續深入瞭解瞭解,主要的一個思路方向是藉助於堆疊結構,從左往右解析約束表示式字串。
利用以上3大型別定義,就能構造出非常靈活的標籤描述以及特徵表示式的判斷了。
基於這些表示式的判斷,RM可以做約束標籤的節點篩選和過濾 ,後續的Container分配過程與普通node label的過程基本一致。
引用
[1].https://issues.apache.org/jira/browse/YARN-3409. Support Node Attribute functionality
[2].https://issues.apache.org/jira/secure/attachment/12937633/Node-Attributes-Requirements-Design-doc_v2.pdf
[3].https://github.com/linyiqun/hadoop-yarn/tree/master/ConstraintNodeLabel