flink内部计算指标的95线-99线等的实现
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,15年在某电商从0设计了一个通用的API监控系统,当时只是计算了成功率+平均耗时,没有算75,90,95,99,999,9999线,这次单位需要,所以促使我去思考这个问题,问了单位CAT维护人员,大致
千家信息网最后更新 2026年11月01日flink内部计算指标的95线-99线等的实现
15年在某电商从0设计了一个通用的API监控系统,当时只是计算了成功率+平均耗时,没有算75,90,95,99,999,9999线,这次单位需要,所以促使我去思考这个问题,问了单位CAT维护人员,大致了解了计算方式,跟我在18年7月份在单位内网BBS发表的文章思路是一致的,所以就直接写了下面的代码
PercentageCalculation.java
package com.ymm.computation.udf.define;import org.apache.flink.table.functions.AggregateFunction;import org.slf4j.Logger;import org.slf4j.LoggerFactory;//批量计算95line类似的数据public class PercentageCalculation extends AggregateFunction
PercentageAccumulator.java
package com.ymm.computation.udf.define;import org.slf4j.Logger;import org.slf4j.LoggerFactory;//!只针对时间来计算95线等,其它参数不要使用本类public class PercentageAccumulator { private static final Logger LOG = LoggerFactory .getLogger(PercentageAccumulator.class); public final static double PERCENT_50 = 0.5; public final static double PERCENT_75 = 0.25; public final static double PERCENT_90 = 0.1; public final static double PERCENT_95 = 0.05; public final static double PERCENT_99 = 0.01; public final static double PERCENT_999 = 0.001; public final static double PERCENT_9999 = 0.0001; public final static int PERCENT_COUNT = 7; private final static int[] SCALE = { // 1, // 2, // 4, // 8, // 16, // 32, // 64, // 128, // 256, // 512, // 1024, // 2048, // 4096, // 8192, // 16384, // 32768, // 65536 // }; private int[] countContainer = { // 0, //<=1 0, //<=2 0, //<=4 0, //<=8 0, //<=16 0, //<=32 0, //<=64 0, //<=128 0, //<=256 0, //<=512 0, //<=1024 0, //<=2048 0, //<=4096 0, //<=8192 0, //<=16384 0, //<=32768 0 //<=65536 }; private int positionByTwoDivision(int[] array, int begin, int end, int value) { int mid = (begin + end) >> 1; int midValue = array[mid]; int halfMidValue = midValue >> 1; //判断是否可以命中mid if (value > halfMidValue && value <= midValue) { return mid; } //没法命中,则根据大小来定 if (value <= halfMidValue) { if (mid - 1 < 0) {//没路可走的边界条件 return 0; } return positionByTwoDivision(array, begin, mid - 1, value); } else { return positionByTwoDivision(array, mid + 1, end, value); } } public int positionInValueArray(int val) { int length = SCALE.length; //如果大于最大值|小于等于最小值 if (val >= SCALE[length - 1]) { return length - 1; } else if (val <= SCALE[0]) { return 0; } //采用2分法来计算 return positionByTwoDivision(SCALE, 0, length - 1, val); } public void accumulate(Object value) { //转换为long值,int值够用了 Long longValue = (Long) value; int intValue = longValue.intValue(); //找到下标 int index = positionInValueArray(intValue); countContainer[index]++; } //确保在[1,MAX]范围内, //自然顺序 private int adjust(int input, int max) { if (input <= 1) { return 1; } else if (input >= max) { return max; } else { return input; } } private static final ThreadLocal STR_BUILDER_ThreadLocal = new ThreadLocal() { public StringBuilder initialValue() { return new StringBuilder(); } }; private static final String SEPARATOR = ":"; public String getValue() { //total int total = 0; int length = countContainer.length; for (int index = 0; index < length; index++) { total += countContainer[index]; } //如果total为0的异常情况 //注意是自然序---[1,total] int percent_9999_pos = adjust((int) (total * PERCENT_9999), total); boolean found_9999 = false; int percent_9999_value = Integer.MAX_VALUE; //999 int percent_999_pos = adjust((int) (total * PERCENT_999), total); boolean found_999 = false; int percent_999_value = Integer.MAX_VALUE; //99 int percent_99_pos = adjust((int) (total * PERCENT_99), total); boolean found_99 = false; int percent_99_value = Integer.MAX_VALUE; //95 int percent_95_pos = adjust((int) (total * PERCENT_95), total); boolean found_95 = false; int percent_95_value = Integer.MAX_VALUE; //90 int percent_90_pos = adjust((int) (total * PERCENT_90), total); boolean found_90 = false; int percent_90_value = Integer.MAX_VALUE; //75 int percent_75_pos = adjust((int) (total * PERCENT_75), total); boolean found_75 = false; int percent_75_value = Integer.MAX_VALUE; //50 int percent_50_pos = adjust((int) (total * PERCENT_50), total); boolean found_50 = false; int percent_50_value = Integer.MAX_VALUE; //开始遍历每一个元素,从后往前算 int scanned = 0; int left = PERCENT_COUNT; for (int index = length - 1; index >= 0; index--) { //当前没有值,无论如何也不会成为备选 if (0 == countContainer[index]) { continue; } //当前有值 scanned += countContainer[index]; //逐个判断 //9999线 if (false == found_9999 && scanned >= percent_9999_pos) { percent_9999_value = SCALE[index]; found_9999 = true; left--; } //999线 if (false == found_999 && scanned >= percent_999_pos) { percent_999_value = SCALE[index]; found_999 = true; left--; } //99线 if (false == found_99 && scanned >= percent_99_pos) { percent_99_value = SCALE[index]; found_99 = true; left--; } //95线 if (false == found_95 && scanned >= percent_95_pos) { percent_95_value = SCALE[index]; found_95 = true; left--; } //90线 if (false == found_90 && scanned >= percent_90_pos) { percent_90_value = SCALE[index]; found_90 = true; left--; } //75线 if (false == found_75 && scanned >= percent_75_pos) { percent_75_value = SCALE[index]; found_75 = true; left--; } //50线 if (false == found_50 && scanned >= percent_50_pos) { percent_50_value = SCALE[index]; found_50 = true; left--; } //全部都找到了就break if (0 == left) { break; } } //所有的值都算好了 //拿出来时先reset一下 StringBuilder stringBuilder = STR_BUILDER_ThreadLocal.get(); stringBuilder.delete(0, stringBuilder.length()); //开始挂各种数据,测试表明每秒几百万次执行 stringBuilder.append(percent_50_value); stringBuilder.append(SEPARATOR); stringBuilder.append(percent_75_value); stringBuilder.append(SEPARATOR); stringBuilder.append(percent_90_value); stringBuilder.append(SEPARATOR); stringBuilder.append(percent_95_value); stringBuilder.append(SEPARATOR); stringBuilder.append(percent_99_value); stringBuilder.append(SEPARATOR); stringBuilder.append(percent_999_value); stringBuilder.append(SEPARATOR); stringBuilder.append(percent_9999_value); //return return stringBuilder.toString(); } public void print() { for (int index = 0; index < this.countContainer.length; index++) { System.out.println(index + "->" + this.countContainer[index]); } }} 欢迎提出 优化建议,比如对GC更友好的优化建议!
这个函数的瓶颈在于stringbuilder.
单位
建议
数据
一致
成功
无论如何
下标
人员
代码
函数
参数
只是
够用
大小
思路
成功率
文章
方式
时间
月份
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
互联网黑科技
为想互联网科技怎么样
科技互联网企业
云信互联网科技有限公司
互联网科技战争
互联网科技开发公司
为想互联网科技
互联网科技公司官网
互联网与未来科技
百单互联网科技有限公司
赫美联合互联网科技
科技伦理 互联网公司
996高科技互联网公司
互联网科技创新的路径
世界互联网领先科技成果获奖
江苏互联网智能科技软件
互联网科技数字货币
华人运通互联网科技公司
互联网领先科技成果宣传视频
六子台科技互联网展会
互联网科技产业
新华互联网科技学校
科技赋能下的互联网金融
美国互联网科技
互联网科技集团
花神(南京)互联网科技有限公司
苏州铭阳互联网科技有限公司
佛山市拉法互联网科技有限公司
江苏苏星互联网科技有限公司
平安好医生打造互联网科技医疗