服務器時間同步引起的"慘案"

背景

  不少時候咱們都不用特別的關心服務器時間的問題,好比後臺管理系統,若是服務器時間出錯頂多會在頁面獲取錯誤的時間而已,影響不大。但有些程序對時間很是敏感,不能出一丁點錯誤,今天要講的是去年發生在本身身邊的事:因爲時間同步問題引起了部門級故障,形成很是嚴重的後果。由於事件發生還不到一年,而且就在本身身邊,因此記憶猶新。老規矩,在講故事以前先了解一下背景,故事中 X 系統後臺的簡化架構以下: 程序員

  它包括 Client->Connector->Heartbeat 三個模塊(固然實際有不少模塊,這裏省去其餘模塊簡化架構,不影響問題的描述)

  • Client 是用來採集數據客戶端,安裝在公司全部的內部機器上,咱們稱之爲 Client 端。
  • Connector 起到 Client 端與後臺橋樑的做用,主要用來進行 Client 鏈接管理、數據透傳等。
  • Heartbeat 是心跳模塊,用來管理 Client 上報的心跳數據。同時它兼職時間服務器:爲 Client 提供統一時間服務。

通過

  上圖中 Client 模塊機器數量 > 萬臺,Connector 模塊機器數量 > 百臺,而 Heartbeat 模塊機器卻只有一臺,這是引起慘案的根本緣由。事情通過:服務器

  • 某天早上到公司,部門內部已經亂成一團。一番瞭解後才知道上述 Heartbeat 機器昨晚宕機了,不少心跳數據上報不上來。這時候的問題還只是 Client 上報不了心跳數據而已,並不影響正的常數據採集。
  • 運維開始在一臺新機器上部署 Heartbeat 模塊,並啓動(能自動註冊並被發現)。
  • 心跳數據陸續回升,可是隻到了正常數量的 60-70%。
  • 各個部門開始向咱們部門反映:本身部門的機器 頻繁產生 Client 的 core 文件(C++ 程序意外終止時產生的現場文件),並且是大面積的。
  • 開發組老大和相關人獲取 core 文件開始分析,一番定位後發現是由於 Client 出現了除零錯誤,Client 異常退出。異常處的代碼大體以下:
int g_lastReportTime = sometime;
void report() {
    int currentTime = getCurrentTime();
    if (somevalue / (currentTime - g_lastReportTime ) > threshold) {
        reportSomething();
        g_lastReportTime = currentTime;
    }
}
複製代碼

  從上面代碼能夠看出 currentTime - g_lastReportTime 在理論上是不會等於零的,可是因爲新啓的機器時間並未與 X 系統內部保持一致。致使 Client 時間回退,也就出現了 currentTime - g_lastReportTime = 0 的狀況。架構

  • 運維立馬想到是新 Heartbeat 機器時間問題,因爲咱們的 X 系統後臺都是 Linux 機器,經過 ntpdate 命令將新機器時間統一,並將該命令加入開機啓動項。
  • 心跳數據陸續恢復正常。

  以上過程花了將近 40多分鐘,形成了很是大的影響。後來部門對事故相關責任人進行了問責和所有門通報。此次事故看似偶然,其實必然,從心跳機器單點那一刻。器宕機形成的事故遠不及 Client core 形成的嚴重,能夠看出本次事故的主要緣由是因爲時間不一樣步形成的,但它也不是孤立的,是衆多因素的統一做用結果。本標題服務器時間同步引起的「慘案」並不誇張,但願能引發讀者這塊的注意和思考。併發

思考

  在事故發生的時候我也沒有想太多,畢竟當時做爲萌新也想不出來個因此然來。可是隨着工做經驗積累,慢慢的從此次事故中有了一些總結。此次事故主要引出如下四個問題:運維

一、監控問題

  心跳機器宕機,心跳數據上不來竟然在次日上班的時候才被發現,整整超過 8 小時了。幸好該系統只是一個內部系統,若是是相似淘寶、天貓這種的,那形成的經濟損失和口碑影響可想而知了。據說在半夜監控已經報出異常,可是一個新來的同事定位並未發現問題,才致使了最後的蝴蝶效應。不過好像即便當場發現,若是在切換新機器時沒作好時間同步也會出現 core 事故。對於系統監控我有如下幾點建議:分佈式

  • 不要將重要模塊的監控交給新人,不少狀況下新人可能並不知道某個監控的重要性
  • 重要模塊的監控接收人不要出現「單點」,最好將主管也加入重要模塊的監控通知接收人中。
  • 多維度、多地點進行監控,通常一個系統都是一個總體,一旦某個模塊出現問題,其餘模塊也會隨之出現問題。若是咱們在多個維度、多個地方進行監控,即便某個地方發現不了,總有一個地方能被發現。

二、單點問題

  這是一個老生常談的話題,網上有一堆的解決方案,這裏不講普適性的只介紹下針對 X 系統的。因爲該系統的特殊性,Heartbeat 模塊並不能進行多機器部署。因此只能單點,那咱們只能祈禱單點機器不會發生故障了嗎?根據墨菲定律:若是你擔憂某種狀況發生,那麼它就更有可能發生。因此不要有僥倖心理,對於 X 系統 Heartbeat 的單點,雖然不能在短期內重構,但咱們仍是能夠作一些事情而不至於須要運維手動切換機器的。一種方案以下:學習

  在服務註冊中心對 Heartbeat 模塊機器進行監控,若是發現服務註冊中心沒有了心跳模塊超過必定時間。則啓動備用機器上的 Heartbeat 併發出告警。這樣就能及時的切換到備用 Heartbeat 不至於太匆忙忘這忘那。簡單結構圖以下:測試

三、代碼質量問題

  事故中的錯誤代碼是我簡化後的,通常不會出現出現這種低級錯誤,應該是一段邏輯處理後產生了相似的代碼,只是不夠直接沒被發現。這種狀況咱們怎麼保證代碼質量問題?我以爲能夠從三個方面考慮:spa

  • 程序員自測:程序員在寫完代碼後必定要進行充分自測,雖然不能 100% 保證不出錯,但起碼能發現大部分的邏輯錯誤和低級錯誤。不要想着會有測試幫你,本身就能夠偷懶,他們每每只在使用層面進行測試而不是在代碼層面。若是不給你配備專門的測試,那就更應該本身動手、豐衣足食了。
  • 代碼評審(CodeReview):都說不識廬山真面目,只緣生在此山中。代碼檢查也是,本身檢查本身的代碼,很難發現一些隱藏的錯誤。這時就要其餘人幫你檢查,也就是 CodeReview。固然,全部的代碼都進行 CodeReview 是一件費時費力的事,因此要有選擇的進行。一些核心模塊的代碼,必定要一遍又一遍的自測、CodeReview。至於 CodeReview 的方案試狀況而定。
  • 增長專業測試人員:(1) 和 (2) 只能解決代碼層面的錯誤,可是一些使用層面的、極端狀況下的錯誤 (1) 和 (2) 並不必定能發現。因此須要專業的測試人員和測試平臺對上線前的代碼進行充分測試。

四、分佈式或者集羣時間同步問題

  分佈式或者集羣的時間同步也是分佈式系統下須要解決的問題之一,拋開 X 系統事故中的場景不說。有不少場景是須要保持分佈式系統中各個節點(機器)上的時間一致的的,好比銀行交易系統,不能讓後一筆交易的時間比前一筆交易的時間早,不然會給用戶形成困擾。不一樣的分佈式系統有不一樣的解決方案,有的簡單有的複雜。簡單的像 X 系統同樣直接用 ntpdate 就能夠實現,複雜的參考博客園這篇文章《分佈式系統----時鐘同步》實現本身的同步系統。code

  固然,以上只是泛泛而談,權當拋磚引玉。不知道你所維護的系統中是否也存在相似的問題呢?早一點把已知的問題暴露出來,比藏着掖着要好的多。在出事故以前發現問題能贏得老闆對你好感,說不定還能升職加薪,而若是在出事以後再來解決問題那就只能背鍋咯。記得關注公衆號哦,記錄着一個 C++ 程序員轉 Java 的學習之路。

相關文章
相關標籤/搜索