一次線上事故對“本地檔案佇列非同步使用”的思考
事故描述
事故現象是部分服務http請求無響應。事故從發生到恢復,接近3個小時,事故過程中重啟應用服務,只能堅持幾分鐘到十幾分鍾,在真正發現問題前透過不斷重啟服務例項來支撐,慶幸的是核心服務沒有出現無響應的事故。
最終分析為AMQ出現故障,現象是MQ客戶端sendMessage後等待響應,但一直在等待,AMQ監控埠ok,控制檯也可以開啟,由於緊急沒有具體分析,直接重啟AMQ服務,切換master,透過驗證服務全部恢復。
這次故障大部分服務都使用了AMQ,但除了一個核心服務沒受到明顯影響外,其他使用AMQ的服務都不同程度的收到了影響,服務不可用。
<strong>
事後透過分析這個核心服務正式使用了本地檔案佇列避免了事故放大,逃過一劫,當時如果這個核心服務也受到影響就可想而知了,事故間期正直商戶業務高峰期,客戶估計要炸了,公司也會受到很大的損失。
</strong>
<strong>
此次事故比較嚴重,就是因為使用了本地檔案佇列有效隔離故障,使得影響面不大。假設(當然不希望發生了)核心業務沒有使用本地檔案佇列來隔離故障,整個下單、收銀服務將不可用,商戶無法營業,損失應該是數量級的。
</strong>
此次事故也證明了我當時的這個架構思路的正確性,主要體現在隔離和降級。
說說這個核心服務使用“本地檔案佇列”
我開發出來這個元件在這個團隊使用一直很穩定效果也很好。
實際上這個團隊使用“本地檔案佇列”的姿勢並不是我期望的,本身使用方法並沒有明顯不妥,只是會延遲訊息的消費,但這中方法可以很好的且有效的隔離故障。
就是在傳送AMQ訊息的方法上新增了@AsyncExecutable
,所以在入AMQ前先入隊“本地檔案佇列”,然後“本地檔案佇列”消費者再把訊息生產到AMQ。
這裡正是利用率“本地檔案佇列”的優點,比較可靠,只依賴於本地檔案系統,不會有網路故障的特性,近似不會被阻塞。
在事故分析中,實際上AMQ對該核心服務也受到影響,但由於採用了“本地檔案佇列”作為一級佇列,有效的隔離了對AMQ的網路依賴,所以沒有放大事故。事故中“本地檔案佇列”中訊息被積累,沒有被消費,重啟服務後才被消費,原因後面再分析。
再說說此次事故中服務不可用的原因
AMQ出現故障,現象是MQ客戶端sendMessage後等待響應,但一直在等待。
AMQ Client訊息的生產和Tomcat共用worker執行緒
AMQ Client訊息的生產沒有超時機制
AMQ Client訊息的生產採用同步傳送,非同步傳送有一些問題場景不太合適。
所以基於以上資訊,AMQ訊息的生產阻塞了Tomcat worker執行緒,最終導致worker執行緒被耗光而服務不可用。
在事故中透過執行緒堆疊資訊和Tomcat執行緒使用數統計也確定了執行緒很快被耗光而請求被阻塞。
另一個服務中採用了非同步執行緒池來生產AMQ訊息,但拒絕策略採用了CallerRunsPolicy
, 也是執行緒池執行緒很快被耗光而再耗光Tomcat worker執行緒,最終導致服務不可用。
事故產生的原因就是程式碼中沒有有效做網路呼叫的隔離和降級。
上面提到的核心服務也受到影響,參考《》,也是因為“本地檔案佇列”拒絕策略採用了CallerRunsPolicy
,最終導致執行緒池執行緒很快被耗光,而使用“本地檔案佇列”消費排程主執行緒,消費排程主執行緒被阻塞而無法消費“本地檔案佇列”訊息並生產到AMQ。但這裡和其他服務不同的是,主業務和AMQ的訊息生產是隔離的,主業務生產訊息到“本地檔案佇列”就返回,並不直接依賴AMQ。
本文中提到了使用本地檔案佇列的隔離姿勢。
後面再介紹使用本地檔案佇列的降級姿勢。
作者:鐵湯
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1343/viewspace-2820526/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一次線上事故,我頓悟了非同步的精髓非同步
- 記一次RocketMQ消費非順序訊息引起的線上事故MQ
- ThreadLocal引起的一次線上事故thread
- 佇列思考二佇列
- Hyperf redis 非同步佇列使用Redis非同步佇列
- synchronized 中的同步佇列與等待佇列synchronized佇列
- 執行緒池運用不當的一次線上事故執行緒
- 記一次Lombok的Setter過載方法造成的事故及思考Lombok
- rsync同步和備份檔案到本地
- kafka線上事故Kafka
- 佇列順序性引發的思考佇列
- AQS佇列同步器AQS佇列
- 一次JVM記憶體問題導致的線上事故JVM記憶體
- 記一次自定義starter引發的線上事故覆盤
- 佇列:佇列線上程池等有限資源池中的應用佇列
- 使用Unison 同步檔案
- 處理線上RabbitMQ佇列阻塞MQ佇列
- 記一次 vue 的非同步更新佇列導致記憶體洩漏Vue非同步佇列記憶體
- Java 佇列同步器 AQSJava佇列AQS
- AbstractQueuedSynchronizer 佇列同步器(AQS)佇列AQS
- 自定義單連結串列佇列的基本介面函式(非迴圈佇列)佇列函式
- 非對稱加密演算法的思考加密演算法
- Redis 應用-非同步訊息佇列與延時佇列Redis非同步佇列
- 如何實現java檔案佇列下載Java佇列
- Java JUC 抽象同步佇列AQS解析Java抽象佇列AQS
- 佇列的併發使用佇列
- Java 中佇列的使用Java佇列
- 一次線上問題排查所引發的思考
- Goldengate 12.2新特性-自描述的佇列檔案Go佇列
- Laravel佇列使用Laravel佇列
- RocketMQ -- 訊息消費佇列與索引檔案MQ佇列索引
- iOS 多執行緒--GCD 序列佇列、併發佇列以及同步執行、非同步執行iOS執行緒GC佇列非同步
- 在使用 Laravel 佇列時 ,佇列會不停的執行Laravel佇列
- 從一次react非同步setState引發的思考React非同步
- 記一次線上事故,redis 的keys問題,cpu引起的效能問題Redis
- 第一次線上 OOM 事故,竟和 where 1 = 1 有關OOM
- 同步容器、併發容器、阻塞佇列、雙端佇列與工作密取佇列
- 使用ln同步檔案內容,支援忽略檔案