【场控&&全链路】简单规则引擎设计

165 阅读2分钟

1. 背景

不管是场控,还是全链路监控。都需要设置规则,规则需要关注一些指标。比如 指标A > 10 就触发告警。再比如 (指标A > 10 && 指标B < 20) 就触发告警。再比如 (指标A > 10 || 指标B < 20) && (指标C > 15) 触发告警。所以需要设计一套模型来支撑这些指标间的运算逻辑。

2. 设计

基本表达式

基本表达式就是最小单元。 例如 A > 10。

可以抽象为三个部分

  1. 变量名 A
  2. 运算符 >
  3. 阈值 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

它可以抽象为几个部分

  1. 基本表达式集合
  2. 复合表达式集合
  3. 表达式之间的逻辑操作符 (&& 或者 ||)
public class CompileComplexExpression {
    private LogicOpCode logicOpCode;
    private List<CompileComplexExpression> compileComplexExpressions;
    private List<CompileBasicExpression> compileBasicExpressions;
}

举例子:

image.png

image.png

计算负责表达式

计算复杂表达式的过程其实就是 算例如 : ( 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)