簡介
線上有一個告警服務採用sparkstreaming+kafka的模式實時處理數據進行布控告警,10s一個批次,34個executor,每個4core,kafka有260個分區,採用直讀的方式並且打開了慢執行推測。
運行一段時間後,都沒有延遲,目前布控任務有1000個左右,每天4000萬的數據需要處理,昨晚業務突然添加了2000個布控任務,導致任務有積壓,導致告警延遲兩個多小時,經過排查,發現慢在獲取mpp的布控任務。
原處理流程
通過上述流程圖可以看出,每個task都需要獲取一個mpp的布控任務,因爲布控任務會被更新,所以每次處理數據的時候都需要全量獲取布控任務,這樣每一個批次,會有260個task,就需要讀取mpp260次,這肯定慢啊。寫代碼的小兄弟說,這個不這樣搞,那每個executor怎麼獲取布控任務,我說:用廣播變量啊,小兄弟說:我這布控任務是動態變化的啊,你這廣播變量是靜態不變的,,,,,,
如果spark這麼呆,估計早死了,我們其實可以動態的更新廣播變量,衆所周知,廣播變量是driver端獨有的,executor只有只讀權限,所以每次只能在driver端更新,所以原流程發現了兩個問題,均在生產環境遇到:
問題一:爲了更新布控信息,每個批次,每個task都讀取一遍mpp,一個批次讀取260,導致mpp壓力很大
解決方案:spark爲分佈式而生,分佈式協調是基本功,採用dirver端讀取定時更新布控信息,動態廣播變量,這樣每個executor都可以通過rpc獲取布控信息。
問題二:告警服務強依賴mpp,在mpp異常,或者cpu高的情況下,會阻塞告警,導致告警延遲
解決方案:採用緩存的機制,在dirver端採取內存緩存,一分鐘定時更新,可以設置jdbc查詢超時爲10s,如果遇到mpp異常,查詢不到數據的情況下,繼續使用上次的緩存信息,類似cap理論,在分佈式容錯的場景下嗎,保持服務可用,犧牲數據一致性。
優化後處理流程
// 動態廣播變量代碼
JavaInputDStream<ConsumerRecord<String, String>> stream = KafkaUtils.createDirectStream(jssc, locationStrategy,
consumerStrategy);stream.foreachRDD(rdd -> {
// 獲取offset
OffsetRange[] offsetRanges = ((HasOffsetRanges) rdd.rdd()).offsetRanges();
// 設置廣播變量
final Broadcast<List<ShinyDisposition>> instance = rdd.context().broadcast(QueryDataUtil.queryShinyDisposition(), ClassManifestFactory.classType(List.class));
List<ShinyDisposition> ShinyDispositionList = instance.value();
rdd.foreachPartition(new VoidFunction<Iterator<ConsumerRecord<String, String>>>() {
public void call(Iterator consumerRecordIterator) throws Exception {
//業務邏輯
}
});
// offset更新
((CanCommitOffsets) stream.inputDStream()).commitAsync(offsetRanges);
// 廣播更新
instance.unpersist(true);
});
QueryDataUtil.queryShinyDisposition()方法獲取mpp的數據,一分鐘更新一次內存緩存。
補充知識點
foreachRDD、foreachPartition和foreach的不同之處主要在於它們的作用範圍不同,foreachRDD作用於DStream中每一個時間間隔的RDD,foreachPartition作用於每一個時間間隔的RDD中的每一個partition,foreach作用於每一個時間間隔的RDD中的每一個元素。