相信我!

類別: IT

普瑞特有幸爭取到了為一個架構師的架構師工作的機會。這位架構師的架構師能夠設計出功能更強,效能更佳,比任何其它設計更優的系統。任何出自這個傢伙之手的設計或編碼,跟其它任何人設計的相似的元件相比,跟任何地方已經有了的相似系統相比,都會具有更全的功能,更強的能力,更堅不可破的效能。無人能企及。

回溯到我們要講的這個系統的設計之初,架構師的架構師命令說這個系統的通訊機制將採用JMS訊息傳送。並且,要準備兩個佇列;一個傳送普通訊息,一個傳送高優先順序訊息。其它應用軟體對這個新系統的服務的要求是,普通優先順序的訊息,系統收集後批量處理,高優先順序的訊息在使用者間直接傳送。這個兩個訊息佇列的活動情況都受應用軟體的監控,但高優先順序的訊息能跳到佇列的最前面以達到最快的響應。還有,由於這個系統將要去替代的系統是長久以來以bug多多和穩定性極差而聞名,所以,這些新系統必須要刀槍不入。它絕對不允許當機。

當普瑞特接受培訓瞭解這個系統時,他問架構師的架構師,當如果有這樣或那樣的錯誤發生時,系統會做什麼響應?

架構師的架構師高傲的宣稱,絕對不會有任何事情能導致系統當機。沒有任何錯誤能阻擋它執行。沒有任何情況能使它失去控制。它是無可阻擋的。它會絕對可靠,直到最後一分鐘。你要相信我!

普瑞特明白這位架構師的架構師對自己深信不疑。然而,事情並不是總會按計劃執行。

一天,普瑞特接到一個負責監控這個系統執行的人的電話:“你們的這個系統的日誌體積增長出現異常。你們的系統在過去24小時裡產生了以前一個月才會有的體積量,系統負載上沒有特別的增長;請檢查一下。”

普瑞特登入生產伺服器,發現了一排排按序號排列的日誌,每個日誌檔案都有20GB,全是這一天產生的!沒有任何編輯器能開啟這樣大的檔案。辦法只能是用grep搜尋裡面的異常。異常多的如火山洶湧。看起來系統會每秒鐘吐出數百次異常事件和堆疊資訊。用tail獲取日誌尾部片段,顯示所有的異常都相同:“無法訪問佇列:佇列控制器似乎已經停止執行。”

不幸的是,佇列控制器並不認可這種說法;它很健康,並在處理著訊息。

與此同時,客戶對系統效能的抱怨不斷升級。經理跑過來詢問怎麼回事。耳朵在冒煙。眼睛在噴火。普瑞特感覺到死神正在上面盯著他。

進一步調查顯示,雖然有一部分應用的例項在不斷的出現並丟擲大量的異常,但有些卻在順利的收發資訊、處理資料。

深挖之後,普瑞特發現處理訊息的外層程式碼是這樣寫的:

    Connection          conn;                    // injected via Spring    Queue               receiveQueueNormal;      // injected via Spring    Queue               receiveQueuePriority;    // injected via Spring    ...    Session session = conn.createSession(true,Session.SESSION_TRANSACTED);    MessageConsumer consumerNormal = session.createConsumer(receiveQueueNormal);    MessageConsumer consumerPriority = session.createConsumer(receiveQueuePriority);    ...    while (true) {    // AA: This will never fail: trust me!       try {             Message msg = consumerPriority.receiveNoWait();             if (msg == null) {                 msg = consumerNormal.receiveNoWait();                 if (msg == null) {                     continue;                 }             }             // process the message here       } catch (Throwable t) {          log.error("...",t);       }    }

進一步在佇列配置檔案裡發現問題:

    <policyEntry topic=">" producerFlowControl="true" memoryLimit="1mb">

當訊息的體積很小或訊息很少時,這段程式碼會呼嘯的執行,它能每秒鐘檢查無數次佇列看有沒有訊息可處理。

但隨著業務的增長,當訊息的體積增長到某種程度,訊息控制器就會耗盡記憶體,結果就會放棄一些訊息,但並不是放棄所有的連線(可能是採用了自我保護模式)。這個系統——設計和開發都基於不會出現意外情況的假設——仍然在快速的最大限度的利用著CPU,但現在卻因為無法和佇列管理器取得聯絡,每個迴圈都會丟擲異常日誌資訊。

沒有重新獲取連線的程式碼。沒有讓連續相同的日誌錯誤資訊做合併延遲輸出。沒有郵件傳送給支援人員說“不好的事情發生了!

出自架構師的架構師的原始碼裡只有一句孤單的註釋:”相信我!

相信我!原文請看這裡