問題來源
最近搞了一個線上服務,涉及到網絡請求、圖檔處理、檔案讀寫等流程,為了解決函數嵌套,我用了async的waterfall方法。結果上線後發現非常不穩定,估計有至少1/4的通路都沒有成功。至此,方才明白穩定可靠服務的重要性。
仔細排查了一下日志,發現有下面幾個問題
-
程式中的error一定要處理-
每個過程要考慮失敗的情形第一條程式中的error特别重要,多數情況下是因為忽略了對異常的處理,導緻服務不可用。
第二條是對程式可靠性的保證,一個簡單粗暴的方法是,隻要流程出錯,就重試。
Solution
我們知道,使用waterfall可以保證一序列函數執行的順序,如果函數執行失敗了,會直接中斷處理流程。比較可靠的辦法是,
重試流程。
這就用到了
async中的retry方法。
var async = require('async');
async.waterfall[
function(callback) {
async.retry({times:5, interval:1000}, function(cb) {
do_task();
var some_err = '';
var some_result = '';
cb(some_err, some_result);
}, function(err, result) {
callback(err, result);
});
},
function(param, callback) {
//類似流程......
},
], function(err, result) {
});
上面的代碼中,最外層的是waterfall,順序執行流程。在每個函數中,可以再加上retry函數,其用法如下:
retry(opts, task, callback)
其中
opts是一個對象,表示重試相關參數,times字段表示重試次數,interval參數表示重試間隔,如果opts隻放一個整數,則表示times,interval預設為0。
task表示要執行的任務,該任務帶有一個回調函數,在執行完成後必須調用,例如上面示例中的cb函數。
callback是retry的回調函數,即retry完成後,對結果進行後續處理。
async中的
回調函數參數更像是一種
通知機制,就是說在函數執行完成後,傳參到下一流程。例如,在retry過程中的cb,還有waterfall中的callback。
在上面的例子中,callback是外層流程waterfall的回調函數,每個函數執行完成後,通過它将參數傳遞給後續流程。而cb就是retry的回調函數,每次任務執行完成後,由它傳參,并決定是否重試。
這樣,就能實作一個完整的流程控制,并且在每個過程當中,進行出錯重試設計。當然,把retry拿到外層,對waterfall整個進行重試設計,也是可行的。隻是需要處理好業務流程。
nodejs的異步特性在面對複雜業務時,有一些劣勢。盡管有async、Promise等機制可以處理調用嵌套問題。但在業務上,還是需要下一些功夫做好流程設計。
參考
https://github.com/caolan/async
文檔
async_demo
Async.retry executes immediately before waiting for interval