對於一個7*24小時無間斷的線上服務來說,在服役時間內難免會遇到一些fail,例如db斷開連線且短暫連線不上了, 下游的某個節點忽然掛了,運維部署上依賴的某一個東西不存在了等等場景。本文主要來討論一下這些場景使用怎樣的策略會比較好。
最簡單的方法,While(true) + sleep(固定時間) 不斷的重試,直到成功為止。這個方法的優點就是簡單,可依賴。缺點就是對於感知延遲要求比較嚴格的程式,會消耗大量的CPU,甚至因為一些不合理的邏輯導致CPU滿載等等情況發生.這種簡單粗暴的方法應用廣泛,並且能解決實際問題,在很多場合還是非常可取. 我們暫且叫這種策略為”粗暴法”.
我曾經在一個實時檔案抓取程式中(類似於scribe這樣的實時日誌傳輸方案),使用了這樣的策略,當fstat原始檔發現檔案不存在的時候,我會重試1000次,每次間隔sleep 10ms, 其間程式會輸出很多warnning資訊來支援一些報警等,重試完1000次之後(10s之後),將sleep間隔設定為固定時間,例如1s,在降低程式對CPU的消耗的同時,保證了一定的實時性,原始檔無論什麼時候出現都能夠確保在1s內cover進來,而且這樣的策略對於日誌切分場景也非常實用,普通的日誌切分(如切分nginx為每小時一個檔案,crontab每小時mv access.log access.log.$date再 kill -USR1等)程式能夠立馬感知到並作出相應的策略調整。我們暫且叫這種策略為”重試N次後,將間隔時間調整為最大的可接受值”.
再看看另外一種方法,最近看了下facebook scribe的原始碼(感興趣的自己google,大家可以姑且的認為是一個多下游的日誌轉發工具),他在下游死掉了之後選擇對sleep時間循序漸進的策略,每次將retryInterval *1.414; (sqrt(2)),再加上一個範圍隨機數(如1-100ms),同時來設定了一個最大值的方式來相對動態的判斷下游狀態. 為什麼一定要設定最大值呢?因為這個策略在異常時間久了之後,滯後性會非常大,當一場恢復時,可能不能及時感知,所以需要一個最大值做保證。我們暫且叫這種策略為“重試時間循序漸進, 且確保不大於最大可接受值“.
近兩年來使用zookeeper(以下簡稱zk)的公司越來越多,很多公司都用zk來做大型分散式系統的協調,他的模式類似於:下游通過在zk上註冊一個臨時節點,告訴大家,我活著呢, 上游通過watch這個節點的變化來感知下游的變化。模式很簡單,但是大家都是用zk是因為他提供了很多額外的東西,例如下游註冊的臨時節點在下游當機,或者網路不可達(反正就是掛了)等等情況下會自動清除,並且通過回撥函式實時讓上游程式感知,作出相應變化,當下遊活了之後,又註冊一個臨時節點宣稱自己活了,上游程式也能通過回撥函式實時感知。上游程式依賴zookeeper的一個Lib庫。對於上游程式來說,他是一個觀察者,套進設計模式就是觀察者模式,好萊塢有句名言. “不要給我打電話, 我會給你打電話”.我們暫且叫這種策略為“被動實時感知下游變化”。
先寫到這裡(也只想到了這些),後續有所想法再補充吧,也歡迎各位看官留言,過去的博文都長篇大論,以後儘量做到簡約不簡單吧。畢竟時間精力有限。