只有在知道問題的情況下,編寫發現問題的測試才會變得簡單
![]()
1. 以前“計劃內的停機”很正常,現在則不被接受
2. 高可用性架構
2.1. CF系統不會遇到任何常見的單點失效問題
2.1.1. 硬件的每一部分都有冗余
2.1.1.1. CPU
2.1.1.2. 驅動器
2.1.1.3. 網卡
2.1.1.4. 電源
2.1.1.5. 網絡交換機
2.1.1.6. 風扇
2.1.2. 為了防止某個機架受到損壞或破壞,服務器甚至被分散安裝到不同的機架上
2.1.3. 如果發生火災、洪水、炸彈襲擊,位于48千米外的第2個機房可以隨時把系統接管過去
3. 集群配置的常見問題
3.1. 沒有足夠的心跳
3.2. 心跳數據和生產數據由相同的交換機傳輸
3.3. 服務器設置為使用物理IP地址而不是虛擬IP地址
3.4. 設計的軟件包之間混亂的依賴關系
4. 遭遇停機
4.1. 首要的任務都是恢復服務
4.2. 要先恢復服務,之后才是調查原因
4.2.1. 數百種疾病都能引起發燒
4.2.2. 為了判斷可能的病因,需要進行化驗或觀察,了解更多信息
5. 事后分析
5.1. 誰最后動的就賴誰
5.1.1. post hoc, ergo propter hoc
5.1.2. 由于在數據庫故障切換和維護之后,系統很快就失效了,因此疑點自然聚焦在故障切換操作上
5.2. 可靠的線索
5.2.1. 在停機事故發生時復制下來的服務器日志
5.2.1.1. 軟件事故的事后分析實際上更難完成,因為事件結束了,沒有留下可供分析的實物
5.2.2. RMI使得跨機器之間的通信方式就像在本機內部通信,但這種方式無法為通信調用設置超時時間,所以有一定的風險
5.2.2.1. 調用方很容易被其調用的遠程服務器中的問題拖垮
5.3. 不可靠的線索
5.3.1. 人們對所見之事的陳述
6. 原因
6.1. JDBC規范允許java.sql.Statement.close()拋出一個SQLException異常,所以代碼必須處理該異常
6.1.1. 如果在關閉statement時拋出異常,則數據庫連接不會被關閉,從而導致資源泄漏
6.1.2. 這個異常在正常情況下幾乎是不會被拋出來的
6.1.2.1. 當Oracle驅動程序在遇到IOException的情況下嘗試關閉數據庫連接時,例如執行數據庫故障切換,SQLException異常就會拋出
6.1.2.2. 在執行該statement語句進行一些網絡I/O操作時,會拋出SQLException異常。而在關閉該statement語句時也會拋出SQLException異常,因為驅動程序會嘗試告訴數據庫服務器釋放與該語句相關的資源
6.2. 假設JDBC連接是在故障切換之前創建的
6.2.1. 當數據庫服務器執行故障切換時,用于創建連接的數據庫服務器IP地址將從一臺主機變成另一臺主機,但TCP連接的當前狀態不會將數據庫主機地址轉變為第二個主機地址
6.2.2. 任何對套接字的寫入操作,最終都會拋出一個IOException異常
6.2.3. 意味著資源池中的每個JDBC連接都是能引發事故的“地雷”
6.3. 被迫停飛的原因只是一個未被捕獲的SQLException異常
7. 預防管用嗎
7.1. 指望著每一個像這樣的軟件缺陷最終都能被揪出來,就是天方夜譚
7.2. 除非其中一位代碼評審員知道Oracle JDBC驅動程序的內部實現細節,或者代碼評審團隊對每個Java方法都花費幾個小時來評審,否則是不會發現的
7.3. 只有在知道問題的情況下,編寫發現問題的測試才會變得簡單
7.3.1. 在定位了問題之后,該團隊在壓力測試環境中進行了測試,確實重現了同樣的差錯
7.4. 系統中的一個軟件缺陷可能會傳播到所有其他受影響的系統中
7.4.1. 每個企業內部都有相互關聯和相互依賴的系統
7.4.2. 不能允許軟件缺陷導致一連串的系統失效