1. 复合指标/叶子指标概念介绍
这里全链路监控中的指标来举例子。
指标 : [外呼未知异常占比]
指标 : [外呼未知异常量]
指标 : [外呼拨打量]
他们之间的关系是
外呼未知异常占比 = 外呼未知异常量 / 外呼拨打量。
那么 外呼未知异常占比 称之为复合指标,外呼未知异常量 / 外呼拨打量 称之为叶子指标。
对于叶子指标来说:叶子指标能够直接从es里面计算出来。
对于复合指标来说:es里面不能计算出来,需要计算出它依赖的叶子指标,然后在应用层进行计算。
2. aviator 快速入门
引入依赖
<dependency>
<groupId>com.googlecode.aviator</groupId>
<artifactId>aviator</artifactId>
<version>5.1.4</version>
</dependency>
使用
public static void main(String[] args) {
String expression = "( a + b ) * c";
Expression compileExpression = AviatorEvaluator.compile(expression);
// 获得表达式里面的所有参数
// [a, b, c]
System.out.println(compileExpression.getVariableNames());
// 运算
Object result = compileExpression.execute(
new HashMap<String, Object>() {
{
put("a", 1);
put("b", 2);
put("c", 3);
}
}
);
// 打印结果 9 (2 + 1) * 3 = 9
System.out.println(result);
}
3. 解决复合指标
所以当我们拿到一个复合指标时候,应该获得其表达式。然后提取表达式里面的叶子指标指标名字,然后通过叶子指标的指标名字去查询指标值,然后做运算。
但是上面的前提是建立在复合指标所依赖的指标都是叶子指标,要是复合指标所依赖的指标也是复合指标呢?
举个例子 :m = a / b , 其中 b = c + d , 那么上面的算法就不能解决这种问题。要解决这种问题的话 其实用递归就好了。
给出伪代码:
public class TestMain {
private static final Map<String, Metrics> metricsMap = new HashMap<>();
/**
* mock 指标元信息数据
*/
static {
metricsMap.put("m", new Metrics("m", "a + b"));
metricsMap.put("a", new Metrics("a", null));
metricsMap.put("b", new Metrics("b", "c + d"));
metricsMap.put("c", new Metrics("c", null));
metricsMap.put("d", new Metrics("d", null));
}
public static void main(String[] args) {
System.out.println(calMetrics("m"));
}
/**
* 指标计算
* @param metricsName
* @return
*/
public static Object calMetrics(String metricsName) {
Metrics metrics = mockGetMetricsByMetricsName(metricsName);
if (metrics.getCalExpression() == null) {
// 如果是叶子指标 直接计算
return mockQueryMetricsValue(metricsName);
} else {
// 不是叶子指标
// 表达式
String calExpression = metrics.getCalExpression();
Expression compileExpression = AviatorEvaluator.compile(calExpression);
// 提取所有依赖的指标
List<String> dependencyMetricsNames = compileExpression.getVariableNames();
// 递归计算所依赖的指标
Map<String, Object> dependencyMetricsValues = dependencyMetricsNames
.stream()
.collect(Collectors.toMap(
dependencyMetricsName -> dependencyMetricsName,
TestMain::calMetrics
));
// 计算
return compileExpression.execute(dependencyMetricsValues);
}
}
/**
* mock 通过指标名字查询指标元信息
*
* @param metricsName
* @return
*/
public static Metrics mockGetMetricsByMetricsName(String metricsName) {
return metricsMap.get(metricsName);
}
/**
* mock 通过指标名字查询指标值
*
* @param metricsName
* @return
*/
public static Object mockQueryMetricsValue(String metricsName) {
switch (metricsName) {
case "a":
return 1;
case "c":
return 3;
case "d":
return 4;
default:
return null;
}
}
}
@Data
@RequiredArgsConstructor
class Metrics {
/**
* 指标名字
*/
private final String metricsName;
/**
* 复合指标的计算表达式
*/
private final String calExpression;
}