就如Google總監說的,我還沒見過沒有bug的程序。
即使如此,你還是可以做得更好:採用預估、預演、持續改善的方式,可以讓你對你程序更有信心。
比如首先從產品經理那裏得知系統將來的在線人數、最大事務併發量以及使用環境等,並將之換算爲系統參數。接着嘗試編寫測試去模擬它,以驗證程序是否能勝任。最後如果程序發生錯誤,想辦法改善架構或程序。
預估
一個好的產品經理總是可以告訴你需要的信息,比如系統的規模、可能的在線人數、事務併發量等,你也可以從客戶那裏獲得這些信息。你需要做的就是應用一些數學,將之轉換爲你測試程序的依據。
比如我有一個公路測速的項目,從用戶那獲得這樣的信息:
- 即將有100個測速點,每個測速點都會有一個測速器。
- 測速器最快0.7s完成拍攝圖片和測速工作,並即時上傳圖片。
- 要完成的功能就是開發一個Web服務(圖片服務器,環境不支持ftp)來接受所有點的圖片上傳。
- 每張圖片300k;
這個web服務沒有任何業務,需要考慮的就是能否支持100個點同時上傳圖片?關鍵考慮的有帶寬、CPU以及硬盤。所以我們可以這樣預估:帶寬 = 100 * 300k * 2(大約值,單張圖片上傳的時間,單位爲秒) *sqrt(2)/(1024*0.7)=100M;硬盤容量=測速點的每日車流量*300k*100*天數,而CPU很少情況下需要在這裏考慮。
這一步很重要,而接下來的事情就簡單多了。
預演
編寫模擬程序,這個程序應該是多併發的。
使用java的併發工具包可以輕鬆實現,比如使用Executors.newFixedThreadPool(併發線程數)創建一個限定線程數的共享池,然後使用CountDownLatch類實現併發 。
ExecutorService executor = Executors.newFixedThreadPool(c); 。。。 private void concurrency() throws Exception{ log.debug("....................... start test ..................."); long sTime = System.currentTimeMillis(); final CountDownLatch ready = new CountDownLatch(c); final CountDownLatch start = new CountDownLatch(1); final CountDownLatch done = new CountDownLatch(c); final AtomicInteger success = new AtomicInteger(0); for (int i = 0; i < c; i++) { executor.execute(new Runnable() {
} ready.await(); start.countDown(); done.await(); long eTime = System.currentTimeMillis(); log.info("....................... time for spend is " + (eTime - sTime)/1000 + " ..................."); if(success.get()!= c) { log.warn("....................... count for files uploaded successly is " + success.get() + " .but it expect to be " + c); } else { log.info("....................... all the file have upload successly! ..................."); } sCount += success.get(); fCount += c - success.get(); Thread.sleep(1000); } |
代碼中的concurrency()函數支持併發調用uoload()方法c次,並記錄上傳失敗或者成功的次數。
持續改善
如果需要改善你的程序,首先你得知道它發生了什麼問題,着意味着你想要一些工具。
比如,上述程序中的代碼ExecutorService executor = Executors.newFixedThreadPool(c);之前被放在concurrency()方法的開頭,我使用while(true)循環中調用了該方法,期望它能一直正常運行1天一夜,但是它才10分鐘就發生錯誤了,結果截圖如下
我一直調試了這個程序幾個小時,一直沒有發現問題,後來打開jconsole工具(這是一個java自帶的監控工具),然後得到這樣的圖標。
我發現了問題:我的程序在持續的創建線程。
但是我代碼的意圖是創建一個設定線程數的共享池,這樣即使我循環無法次,它也只會創建和我設定的值一樣多的線程。我一開始就考慮了要避免浪費。
直到這裏,我又從頭仔細閱讀了代碼,我終於知道自己犯了很愚蠢的錯誤--就是那句代碼,它實現的是每次調用都創建一個線程池,所以線程數線性增加的。我將那句代碼被移出方法意外,問題就解決了。
除了線程數,通過工具你還可以看到程序使用的內存、CPU,創建的實例等等。你可以根據這些圖表來決定如何改善你的架構和程序。這個在下節在介紹。