高并發(fā)場景下 disk io 引發(fā)的高時延問題
該系統屬于長連接消息推送業(yè)務,某節(jié)假日推送消息的流量突增幾倍,瞬時出現比平日多出幾倍的消息量等待下推。
事后,發(fā)現生產消息的業(yè)務服務端因為某 bug,把大量消息堆積在內存里,在一段時間后,突發(fā)性的發(fā)送大量消息到推送系統。但由于流量保護器的上限較高,當前未觸發(fā)熔斷和限流,所以消息依然在流轉。
消息系統不能簡單地進行削峰填谷式的排隊處理,因為很容易造成消息的耗時長尾,所以在不觸發(fā)流量保護器的前提下,需要進行的并發(fā)并行地去流轉消息。
下圖是我司長連接消息推送系統的簡單架構圖,問題出在下游的消息生產方業(yè)務端。
其實更重要的原因是前些日子公司做成本優(yōu)化,把一個可用區(qū)里的一波機器從物理機遷移到阿里云內,機器的配置也做了閹割。突然想起曹春暉大佬的一句話:
沒錢做優(yōu)化,有錢加機器。
這樣兩個問題加起來,導致消息時延從 100ms 干到 3s 左右,通過監(jiān)控看到高時延問題持續(xù) 10 來分鐘。
分析問題
造成消息推送的時延飆高,通常來說有幾種情況,要么 cpu 負載高?要么 redis 時延高?要么消費rocketmq 慢?或者哪個關鍵函數處理慢 ?
通過監(jiān)控圖表得知,load 正常,且網絡 io 方面都不慢,但兩個關鍵函數都發(fā)生了處理延遲的現象,這兩個函數內除處理 redis 和 mq 的網絡 io 操作外,基本是純業(yè)務組合的邏輯,講道理不會慢成這個德行。
詢問基礎運維的同學得知,當時該幾個主機出現了磁盤 iops 劇烈抖動, iowait 也隨之飆高。
但問題來了,大家都知道通常來說 Linux 下的讀寫都有使用 buffer io,寫數據是先寫到 page buffer 里,然后由內核的 kworker/flush 線程將 dirty pages 刷入磁盤,但當臟寫率超過閾值 dirty_ratio 時,業(yè)務中的 write 會被堵塞住,被動觸發(fā)進行同步刷盤。
推送系統的日志已經是 INFO 級別了,雖然日志經過特殊編碼,空間看似很小,但消息的流程依舊復雜,不能不記錄,每次扯皮的時候都依賴這些鏈路日志來甩鍋。
阿里云主機普通云盤的 io 性能差強人意,以前在物理機部署時,真沒出現這問題。??
解決思路
通過監(jiān)控的趨勢可分析出,隨著消息的突增造成的抖動,我們只需要解決抖動就好了。上面有說,雖然是 buffer io 寫日志,但隨著大量臟數據的產生,來不及刷盤還是會阻塞 write 調用的。
解決方法很簡單,異步寫不就行了?。。?/p>
-
實例化一個 ringbuffer 結構,該 ringbuffer 的本質就是一個環(huán)形的 []byte數組,可使用 Lock Free 提高讀寫性能; -
為了避免 OOM, 需要限定最大的字節(jié)數;為了調和空間利用率及性能,支持擴縮容;縮容不要太頻繁,可設定一個空閑時間; -
抽象 log 的寫接口,把待寫入的數據塞入到ringbuffer里; -
啟動一個協程去消費 ringbuffer 的數據,寫入到日志文件里; -
當 ringbuffer 為空時,進行休眠百個毫秒; -
當 ringbuffer 滿了時,直接覆蓋寫入。
這個靠譜么?我以前做分布式行情推送系統也是異步寫日志,據我所知,像 WhatsApp、騰訊 QQ 和廣發(fā)證券也是異步寫日志。對于低延遲的服務來說,disk io 造成的時延也是很恐怖的。
覆蓋日志,被覆蓋的日志呢?異步寫日志,那 Crash 了呢?首先線上我們會預設最大 ringbuffer 為 200MB,200MB 足夠支撐長時間的日志的緩沖。如果緩沖區(qū)滿了,說明這期間并發(fā)量著實太大,覆蓋就覆蓋了,畢竟系統穩(wěn)定性和保留日志,你要哪個?
Crash 造成異步日志丟失?針對日志做個 metrics,超過一定的閾值才開啟異步日志。但我采用的是跟廣發(fā)證券一樣的策略,不管不顧,丟了就丟了。如果正常關閉,退出前可將阻塞日志緩沖刷新完畢。如果 Crash 情況,丟了就丟了,因為 golang 的 panic 會打印到 stderr。
另外 Golang 的垃圾回收器 GC 對于 ringbuffer 這類整塊 []byte 結構來說,掃描很是友好。Ringbuffer 開到 1G 進行測試,GC 的 Latency 指標趨勢無異常。
至于異步日志的 golang 代碼,我暫時不分享給大家了,不是因為多摳門,而是因為公司內部的 log 庫耦合了一些東西,真心懶得抽離,但異步日志的實現思路就是這么一回事。
結論
如有全量詳細日志的打印需求,建議分兩組 ringbuffer 緩沖區(qū),一個用作 debug 輸出,一個用作其他 level 的輸出,好處在于互不影響。還有就是做好緩沖區(qū)的監(jiān)控。
下面是我們集群中的北京阿里云可用區(qū)集群,高峰期消息的推送量不低,但消息的延遲穩(wěn)定在 100ms 以內。
讓技術也有溫度!關注我,一起成長~
資料分享,關注公眾號回復指令:
-
回復【加群】,和大佬們一起成長。 -
回復【000】,下載一線大廠簡歷模板。 -
回復【001】, 送你 Go 開源電子書。
