1. 背景
不管是场控,还是全链路监控。都需要设置规则,规则需要关注一些指标。比如 指标A > 10 就触发告警。再比如 (指标A > 10 && 指标B < 20) 就触发告警。再比如 (指标A > 10 || 指标B < 20) && (指标C > 15) 触发告警。所以需要设计一套模型来支撑这些指标间的运算逻辑。
2. 设计
基本表达式
基本表达式就是最小单元。 例如 A > 10。
可以抽象为三个部分
- 变量名 A
- 运算符 >
- 阈值 10
public class CompileBasicExpression {
private OpCode opCode;
private String paramName;
private BigDecimal thresholdValue;
}
举例子:
A > 10
{
"opCode": "LT",
"paramName": "A",
"thresholdValue": 10
}
B == 5
{
"opCode": "EQ",
"paramName": "B",
"thresholdValue": 5
}
复合表达式
复合表达式就是由多个基本表达式或者复合表达式组成。它可以表示 A > 10 && B == 10,
(A > 10 || B == 10) && C <= 2
它可以抽象为几个部分
- 基本表达式集合
- 复合表达式集合
- 表达式之间的逻辑操作符 (&& 或者 ||)
public class CompileComplexExpression {
private LogicOpCode logicOpCode;
private List<CompileComplexExpression> compileComplexExpressions;
private List<CompileBasicExpression> compileBasicExpressions;
}
举例子:
计算负责表达式
计算复杂表达式的过程其实就是 算例如 : ( A > 10 || B == 10) && (C <= 2) 的值
private static RunningComplexExpression calComplexExpression(CompileComplexExpression compileComplexExpression) {
List<RunningBasicExpression> runningBasicExpressions = new ArrayList<>();
List<RunningComplexExpression> runningComplexExpressions = new ArrayList<>();
try {
val compileComplexExpressions = ofNullable(compileComplexExpression.getCompileComplexExpressions()).orElse(new ArrayList<>());
val compileBasicExpressions = ofNullable(compileComplexExpression.getCompileBasicExpressions()).orElse(new ArrayList<>());
val logicOpCode = compileComplexExpression.getLogicOpCode();
if (compileBasicExpressions.isEmpty() && compileComplexExpressions.isEmpty()) {
// 没有任何表达式 默认不命中
return RunningComplexExpression.fail();
}
// 处理基本表达式
if (!compileBasicExpressions.isEmpty()) {
runningBasicExpressions.addAll(compileBasicExpressions.stream()
.map(RuleCalService::calBasicExpression)
.filter(RunningBasicExpression::isHit)
.collect(Collectors.toList()));
}
// 处理复杂表达式
if (!compileComplexExpressions.isEmpty()) {
runningComplexExpressions.addAll(compileComplexExpressions.stream()
.map(RuleCalService::calComplexExpression)
.filter(RunningComplexExpression::isHit)
.collect(Collectors.toList()));
}
int totalCount = compileBasicExpressions.size() + compileComplexExpressions.size();
// 综合判断条件
return judgeExpressionResult(
totalCount,
runningBasicExpressions,
runningComplexExpressions,
logicOpCode
);
} catch (Exception e) {
// 可以根据实际情况记录日志或者进行其他异常处理
e.printStackTrace();
return RunningComplexExpression.fail();
}
}
private static RunningComplexExpression judgeExpressionResult(int totalCount,
List<RunningBasicExpression> runningBasicExpressions,
List<RunningComplexExpression> runningComplexExpressions,
LogicOpCode logicOpCode) {
int hitCount = runningBasicExpressions.size() + runningComplexExpressions.size();
if (hitCount == 0 || (hitCount < totalCount && logicOpCode == LogicOpCode.AND)) {
return RunningComplexExpression.fail();
} else {
return RunningComplexExpression.success(runningBasicExpressions, runningComplexExpressions);
}
}
private static RunningBasicExpression calBasicExpression(CompileBasicExpression compileBasicExpression) {
val opCode = compileBasicExpression.getOpCode();
val paramName = compileBasicExpression.getParamName();
val thresholdValue = compileBasicExpression.getThresholdValue();
// 查询参数值
val paramValue = ParamQueryService.queryParamValue(paramName);
// 比较
boolean hit = CompareHelper.compare(opCode, paramValue, thresholdValue);
val runningBasicExpression = new RunningBasicExpression();
runningBasicExpression.setHit(hit);
runningBasicExpression.setParamValue(paramValue);
runningBasicExpression.setThresholdValue(thresholdValue);
runningBasicExpression.setOpCode(opCode);
runningBasicExpression.setParamName(paramName);
return runningBasicExpression;
}
public class CompareHelper {
public static boolean compare(OpCode opCode, BigDecimal paramValue, BigDecimal thresholdValue) {
switch (opCode) {
case EQ:
return paramValue.compareTo(thresholdValue) == 0;
case NEQ:
return paramValue.compareTo(thresholdValue) != 0;
case GT:
return paramValue.compareTo(thresholdValue) > 0;
case LT:
return paramValue.compareTo(thresholdValue) < 0;
case GTE:
return paramValue.compareTo(thresholdValue) >= 0;
case LTE:
return paramValue.compareTo(thresholdValue) <= 0;
default:
throw new IllegalArgumentException("不支持的运算符");
}
}
}
3. 源码
executor: sample rule executor, it is a basic executor (gitee.com)