預防錯誤

Null 判斷

透過Null 判斷處理,可以避免後續的程式錯誤 像是前端透過HTTP傳送到後端的參數

有些人認為從DB 或 其他 API 取得的資料,不需要做Null判斷 這是一種錯誤的關念,因為你取得的查詢參數有可能是被造假的 甚至有可能DB的資料被刪除了

這時你沒加Null判斷,這部分的程式在被大量執行後 可能就會導致系統異常,甚至整個崩潰

參數轉型問題

前端透過HTTP傳送參數到後端,這時後端收到的參數皆為String格式 有些參數預期為數值型態,但只要傳入的參數為不允許的內容,將會導致型態轉換錯誤 Integer.parseInt("not integer")

這時可以使用Regular expressions來幫忙過濾不允許內容,防止型態轉換錯誤 public static boolean isInteger(String value) { Pattern pattern = Pattern.compile("^[-+]?\\d+$"); return pattern.matcher(value).matches(); }

SQL Injection參數攻擊

某些駭客會利SQL Injection來攻擊你的網站系統,有些弱點掃瞄工具也有此功能

如果你查詢DB的SQL是用字串組合的方式,那就有很大的機會被攻擊 String query_id = request.getParameter("id"); String name = request.getParameter("name"); String sql = "update user set name='"+name+"' where id='"+query_id+"'"; Statement stmt = conn.createStatement(); int updateCount = stmt.executeUpdate(sql);

這時候可以利用Regular expressions來幫忙過濾不允許內容,再使用PreparedStatement的方式 就能將被攻擊的風險降到最低 String query_id = request.getParameter("id"); String name = request.getParameter("name"); String sql = "update user set name=? where id=?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, name); stmt.setInt(2, Integer.parseInt(query_id)); int updateCount = stmt.executeUpdate();

Thread Safe

只要程式是在生命週期結束前,會被不同Thread 同時修改全域變數 就會存在None Thread Safe的問題

網路上的文章或書籍上,教的通常是用synchronized來解決 但這種方法僅適合在同一個jvm上執行的程式 而且在並發量大時,就會有效能的問題

我比較喜歡將這些有問題的程式,直接改寫成不使用全域變數的方式 既不會有None Thread Safe的問題,也不會有並發效能問題

而在不同jvm上執行的系統,無法用synchronized來解決None Thread Safe的問題 這時就需要使用Distributed locks的機制,有很多系統有提供 像是Redis、Infinispan、JGroups

Connection close

如果您的JDBC Connection 在程式執行結束前,都未呼叫close 的method 將會導致Connection無法被回收, 多執行幾次後DB或Connection Pool的連線資源就會被佔滿 進而丟出一些無法取得連線的錯誤訊息,導致系統異常

有時您有寫close, 但程式出現異常,導致未正常呼叫到close 的 method 最安全的寫法就是使用try catch finally的方式,來避免這種情形 Connection conn = null; try { } catch(Exception e) { } finally { if(conn != null) conn.close(); }

用了Spring Proxy的機制來關閉Connection,為何還是會出現 JDBC Connection 未關閉的問題?

您可以參考這篇文章來了解更深入的問題。

Object Close Method

InputStream、OutputStream、HTTP Connection、Socket... 除上方列的這些Object, 還有很多物件本身會提供close的方法 遇到這些,最好都需要自行呼叫Close,避免無法被gc 與connection 未關閉的情形

如果有使用到Graphics 這個物件來處理圖檔,需要自行呼叫dispose,否則會有無法被gc的問題

Connection Timeout

有很多程式取得資料是用網路連線的方式,像是: HTTP Connection、JDBC Connection、Socket、RPC、FTP、Redis... 只要是透過網路取得資料就會有網路連線問題,如果您使用的Client程式未設定Connection Timeout 就會因為網路問題或者連接主機不回應,而出現問題 造成Client端的Thread卡住,有問題功能執行多次之後 就會讓系統卡住無法回應

大部分的Timeout 設定,內網系統 10s 外網30s 就已經足夠 Timeout設太長,只會增加系統卡住的機會 當然還需要參考Server端系統的回應時間

有些Http Client library還有分 Read Timeout、Write Timeout 這些詳細的部分都需要參考相關文件,多了解如何正確的使用 才能避免無法預期的錯誤發生

Try Catch Finally

有些時候會因為硬體或網路環境出現異常,導致程式出現無法預期的錯誤,造成部分資源無法回收 用這個方式能夠讓系統的穩定性提高,印出錯誤訊息,更可加快問題排除。

範例比較

在同樣的運作邏輯,讓程式丟出異常,何種能夠提供較好的結果? 這邊提供一個簡單的範例來比較 (JVM 記憶體上限皆為64MB)

NonCatchError.java 未捕捉到異常 會直接讓主要的Thread崩潰

CatchError.java 捕捉到異常 雖然會印出異常,但是主要的Thread可以正常執行完

Last updated