[設計原則] 保護本身

      軟件開發中的保護本身不是指我的行爲上的自我保護,而是指防犯別的模塊的錯誤卻最終表如今我所設計的模塊上。當某一模塊出現錯誤時,毫無疑問大多情形下模塊的做者將會被「招見」。出現軟件錯誤是正常的,但最爲鬱悶的是最後發現錯誤是別的模塊誤用而引發的。偶而這種情形的發生是能夠理解的,可是若是這種事情是屢見不鮮,則須要考慮經過設計來「保護本身」。提出「保護本身」這一設計原則,其目的不在於讓咱們變得不敢承擔責任,更不是鼓勵「扯皮」,而在於高效地解決問題以提升工做效率。
      下面以做者所從事過的一個真實的項目爲例來講明「保護本身」這一設計原則是如何起做用的。圖1示例了某項目在一個電信級設備上的部署圖,這一電信設備使用的是實時Linux操做系統(Monta Vista Linux和WindRiver Linux),圖中的SNMP Agent進程正是做者所在的項目組開發的,而Box庫是由其它的團隊所開發的。圖中的FDR(Function Definition Rule)是指一套函數定義規則,是Box庫在實現函數時應當遵照的準則。SNMP Agent所實現的功能是使得設備以外的網絡管理程序能夠經過SNMP Agent上的SNMP接口來管理電信設備。SNMP Agent收到設備外部發送過來的SNMP消息以後,將消息最後轉換爲函數調用的形式來存取信息,而這些函數的實現是位於Box庫中的,注意Box庫是由一個印度團隊開發的而不是中國團隊。讀者能夠想象到的是,因爲Box庫是被SNMP Agent進程調用的,所以,一旦Box庫中存在嚴重的缺陷,其最終將直接致使SNMP Agent進程崩潰。對於很多崩潰的情形,經過運用gdb工具查看崩潰時的調用棧能很快地找到問題的根源,這類問題或許也不算大,哪怕當即解決不了也仍是有點方向的。可是,在使用gdb工具查看調用棧時,若是發現任務的堆棧被破壞了(stack corrupted),那問題可就麻煩了,由於這種錯誤能夠說是行業內最難解決的問題。有讀者會想到經過使用日誌的方式將有助於接近問題的根源,這種作法的前提是問題容易重現,不然不可能運用打印調試日誌的方式讓軟件在用戶那運行,要知道不少問題在實驗室是沒法重現的,由於其所模擬的環境不如真實的那樣複雜。另外,採用日誌的方式並不經濟,由於它須要佔用大量的處理器時間,進而可能改變運行環境使得問題難以重現,所以這種方法有它極大的侷限性。另外,還有一個更爲重要的問題,這個完整的軟件是由兩個團隊開發的,一個問題的出現,究竟是出在SNMP Agent側呢?仍是Box庫側?在現實中,不可避免地因爲是SNMP Agent出現了崩潰,Box庫的開發人員天然但願SNMP Agent開發團隊去找問題。不難想象,當問題是出在Box庫側時,不管SNMP Agent如何努力都發現不了問題的根源,而最終將致使一個問題被拖延很長時間也毫無結果。若是出現的問題很是的嚴重和緊急,那就意味着這一問題對於高層管理人員具備很高的可見性,也可能高層都將關注問題的解決過程,那意味着SNMP Agent —— 做者所在的團隊,將面臨很大的壓力。讀者可能看到這會想,是否是做者把問題給假設得太嚴重了?不是,這是真實發生過的事情!
圖1
     除此以外,從業務邏輯來看SNMP Agent相對的穩定,由於它只是翻譯收到的SNMP消息並最終調用Box庫中的函數完成消息的處理,而Box庫會因業務的演進而頻繁地變更,於是Box庫出錯的可能性也更大。那如何經過設計來介定當堆棧被破壞這類嚴重的問題出現時,應當由哪一個團隊去負責追查問題的根源呢?或者說,SNMP Agent團隊如何經過設計來保護本身以避免「背黑鍋」呢?這種防範是有意義的,它不僅意味着問題出現時能快速的介定責任團隊,也能夠避免沒有必要的團隊扯皮,說到底就是能提升工做效率。
     做者所提出來的第一個解決方案的思路是,可否經過設計一種方法記錄SNMP Agent程序當前正在運行哪一部分的代碼?固然,這種信息只要足於區分是運行Box庫中的代碼或是SNMP Agent中的就好了。最爲直觀的作法是,每調用Box庫中的函數時,在調用以前打印一行日誌,在調用返回後又打印一行日誌。這種方法在前面說起了,可能會存在必定的性能問題,但其思路仍是對的,只是須要採用其它的更爲高效的方式替代日誌記錄。做者想到了Linux中的共享內存!Linux中的共享內存有一個特色,一個程序若是分配了一塊共享內存,若是程序不主動對其刪除,則即便是程序出現了崩潰其中的內容也一直保存在那不會被更改,固然操做系統進行過了重啓的話則除外。
     如此一來,經過設計能夠將調用Box函數以前和以後的信息經過使用一個×××變量的方式記錄程序是不是在調用Box庫,固然,這個×××變量是位於SNMP Agent所建立的共享內存中的。好比設置整型值3表示將要調用Box庫中的某一個函數,而在調用完了這一函數後將這一值設置成4。當SNMP Agent出現崩潰後,在其下一次啓動的初始化階段時(咱們的系統有進程管理程序,發現一個進程出現崩潰之後,又會自動重啓它),經過打開同一個共享內存塊就能夠知道上一次程序出錯時,是不是出如今調用Box庫其間,若是是則記錄一個錯誤日誌。好比,若是發現整型變量的值爲3說明崩潰是發生在Box庫內的,則記錄一條錯誤日誌。固然,設計考慮到了多線程的問題,且以線程爲單位來設計的。有了這種方法,當出現問題時,經過查看日誌就能很快地定位責任團隊,且幾乎徹底不失程序的執行效率。
     這種設計方案在操做中存在必定的運做效率問題。好比,即便是Box庫的問題,這一問題一旦在測試部門發現,則必定會將缺陷提交給SNMP Agent團隊,SNMP Agent團隊在查看日誌後,發現是Box庫形成的問題,因而將缺陷轉交給了Box庫團隊。能不能又經過設計讓Box庫一出現問題就讓測試部門將缺陷提交給Box庫團隊呢?圖2展現了進一步的設計方案。
圖2
     在這一方案中,增長了一個新的Proxy進程,這個進程的程序也是由SNMP Agent團隊開發的。SNMP Agent進程和Proxy進程之間採用了IPC(Inter-Process Communication,即進程間通信)進行通信,就是將前一方案中SNMP Agent直接調用Box庫函數的形式轉化成了經過進程間通信將這一調用發送給Proxy進程,Proxy進程再經過FDR接口調用Box案。固然上一方案中採用共享內存記錄哪一部分代碼正被調用的方法被運用到了Proxy進程上,以幫助介定問題是發生在Proxy進程自己仍是在Box庫內。在這種方案中,其中很重要的一個內容是,Proxy進程的邏輯很是的簡單,即接受來自SNMP Agent的消息而後調用Box庫函數並經過消息將Box庫函數所返回結果傳給SNMP Agent,其邏輯不會由於項目的不斷變化而變化,除非FDR發生了變化。可能這種方案剛開始部署時,Proxy會有必定的缺陷,但通過必定的時期穩定後,一旦出現Proxy崩潰的問題,則極有可能就是Box庫所引發的了,而這種情形下測試部門能夠直接將缺陷提交給Box庫團隊。
     至此,相信讀者已明白了「保護本身」這一設計原則是如何起做用的了,可能有的讀者也會有一點靈感去解決本身項目中正面臨的相似惱人問題。另外,不知讀者注意到了沒有,這裏所談的共享內存方案,其實能夠做爲一種通機制去輔助縮小出現問題時程序的出錯範圍。前面也提到是經過一個位於共享內存中整型變量來記錄程序的運行點的,而這個變量能夠定義232個值用於表示程序的不一樣執行點,固然應用程序須要在合適的地方對其設置不一樣的值來指示程序運行到了什麼地方。
相關文章
相關標籤/搜索