1)選擇滑動窗口
滑動窗口會導致一個時間點的數據會分佈到多個window裏來,其它跟滾動窗口沒區別
2)滑動數據重新匯聚
計算源是單個時間窗口內預匯聚的數據,不適用於滑動窗口的計算值,比如前面單個窗口計算出來p90,那麼滑動窗口的p90怎麼算?
需要把前面的時間窗口的現場保留下來傳到當前滑動窗口內纔可以繼續計算,具體代碼如下:
//拿到原始數據後,重新計算
@SuppressWarnings("unchecked")
public void accumulate(Object value) {
if (null == value) {
return;
}
HashMap<String, Object> mapVal = (HashMap<String, Object>) value;
//1)在歷史值和當前值中設置新的max值
Object maxObj = mapVal.get("max");
Object minObj = mapVal.get("min");
Object countObj = mapVal.get("count");
Object sumObj = mapVal.get("sum");
Object afObj = mapVal.get("af");
Object ccObj = mapVal.get("cc");
if (null == maxObj || null == minObj || null == countObj || null == sumObj || null == afObj
|| null == ccObj) {
return;
}
if (false == (ccObj instanceof JSONArray)) {
return;
}
JSONArray jsonArray = (JSONArray) ccObj;
int jsonSize = jsonArray.size();
if (0 == jsonSize) {
return;
}
this.max = Math.max(this.max, ((Number) maxObj).doubleValue());
this.min = Math.min(this.min, ((Number) minObj).doubleValue());
this.count += ((Number) countObj).intValue();
this.sum += ((Number) sumObj).doubleValue();
//每次都是直接替換
this.augmentFactor = ((Number) afObj).intValue();
//5)開始彙總以便後面計算各種95線之類的值
Integer[] intArray = new Integer[jsonSize];
intArray = jsonArray.toArray(intArray);
for (int index = 0; index < jsonSize; index++) {
countContainer[index] += intArray[index];
}
//6)over
}
3)海量tag的發現
如果上傳的metric tag數據非常多,怎麼去重是個問題,我採取的方案是
3.1)使用採樣率
@Override
public void filter(String metric, TreeMap<String, String> tagValues, boolean tagValuesEmpty) {
Random randomGenerator = RANDOM_THREAD_LOCAL.get();
if (0 == randomGenerator.nextInt(20)) {
//取5%的採樣率
ReportQueue.put(ReportQueue.METRIC_TAG,
new MetricAndTags(metric, tagValues, tagValuesEmpty));
//結束
} else {
}
}
3.2)布隆過濾器判重
//普通數據-bloom filter
private static Integer SIZE = 100 * 1000 * 1000;
private static Integer BITS = 20;
private static Integer HASH_FUNCTION = 1;
private static BloomFilter DATA_BLOOM_FILTER = new BloomFilter(SIZE, BITS, HASH_FUNCTION);
//哈希code-bloom filter
private static BloomFilter HASHCODE_BLOOM_FILTER = new BloomFilter(SIZE, BITS, HASH_FUNCTION);
public static synchronized boolean isNewKey(String data) {
Key dataKey = new Key(data.getBytes());
if (false == DATA_BLOOM_FILTER.membershipTest(dataKey)) {
//不存在就是真的不存在
return true;
}
//再做hashcode的2次判斷
if (false == HASHCODE_BLOOM_FILTER.membershipTest(hashCodeKey(data))) {
//不存在就是真的不存在
return true;
}
//(如果2次都說存在,也沒辦法了,這條數據丟棄)
//返回false表示不是new key
return false;
}
3.3)元數據冪等性保存到es
注意冪等性,之前存在的數據會被更新,而不是新增一條數據,因爲我們是保存元數據
具體就是設置請求體裏的upsert爲true
@Data
public class ExecutionMetricTagValue {
private Boolean doc_as_upsert=true;
private EsMetricTagValue doc;
}
3.4)限流防對遠程ES的流量衝擊
這個是構建一個 Guava對象
private static final RateLimiter RATE_LIMITER = RateLimiter.create(500);
//在JVM級別限流,防止對ES產生衝擊
RATE_LIMITER.acquire(1);
4)用戶配置數據拉取
用戶配置的一些規則,通過另外一個JVM級別的線程拉取到本地內存,這樣就可以不影響flink的計算速度
5)報警屏蔽週期
這是爲了防止報警洪災,實現思路
String res;
try {
SetParams setParams = new SetParams();
setParams.nx();
setParams.ex(alarmInterval);
//僅僅是當前timeSpan內有效,10s不影響30s 1m這種
res = JEDIS_CLUSTER.set(timeSpan + "_hubble_alarm_" + fullKey, "1", setParams);
} catch (Exception e) {
LOG.error(e.toString());
return;
}
if (null != res) {
//LOG.info("初次插入,可以報警");
主要就是這些,很難的點沒有,就是要注意各個細節
----------------------------------------------------------------------------------------------------------
其實我覺得報警系統的精髓在於閾值的設置上,傻乎乎的設置靜態值是沒有技術含量的,整個報警系統的精髓就在於自動設置報警閾值
所以接下來我會去研究這方面的技術,如果研究出來了我會發文章出來!
----------------------------------------------------------------------------------------------------------
下面放界面圖