对于node开发同学经常要处理异步请求,然后根据请求的结果或请求成功后的状态码做不同的策略处理,众多策略中最常用的一种就是重试策略。针对重试策略我们往往还需要设定一定的规则,如重试次数、重试时间间隔、总体超时时间、重试判定等。针对以上问题,这里推荐一个工具包:bluebird-retry。
bluebird-retry基本用法如下:var retry = require('bluebird-retry');function logFail() { console.log(new Date().toISOString()); throw new Error('bail');}retry(logFail, { max_tries: 4, interval: 500 });
结果如下:
2014-05-29T23:16:28.941Z2014-05-29T23:16:29.445Z2014-05-29T23:16:29.946Z2014-05-29T23:16:30.447ZError: operation timed out
以上代码逻辑是,如果logFail函数报出异常或出错,则重试四次,每次间隔500毫秒。
bluebird-retry除了能够捕获异常外,还支持promise形式:var Promise = require('bluebird');var retry = require('bluebird-retry'); var count = 0;function myfunc() { console.log('myfunc called ' + (++count) + ' times'); if (count < 3) { return Promise.reject(new Error('fail the first two times')); } else { return Promise.resolve('succeed the third time'); }}retry(myfunc).then(function(result) { console.log(result);});
最终结果:
myfunc called 1 timesmyfunc called 2 timesmyfunc called 3 timessucceed the third time
他对promise的支持就为我们的异步请求处理打开了大门,我们可以将bluebird
,request
,bluebird-retry
三者结合起来,针对异步请求做重试策略:
var Promise = require('bluebird');var request = Promise.promisifyAll(require('request'));var retry = require('bluebird-retry');var config = require('../config/config');var log = require('../log');module.exports = function(options, method) { options.timeout = options.timeout || config.requestTimeout; var predicate = options.predicate || function(error) { log.fatal('GET#' + method +'*retryError#-1#' + options.url + '#error:' + error.stack); return error && error.toString() && error.toString().indexOf('ETIMEOUT') > -1; }; delete options.predicate; return retry(function(){ log.notice('GET#' + method +'*retry#' + options.url); return request.getAsync(options); }, { max_tries: 3, interval: 200, backoff: 2, predicate: predicate//对错误过滤处理,只有符合条件的错误才会做重试策略,比如超时 });}
以上代码就是只针对超时错误做重试处理。
如果你还有其他场景,比如对返回结果判断status,如果status不为0则重试,那么你只需要在适当时候抛出异常即可。可以参考如下实例:function getToken(userId){ console.log("GET#getToken*start#userId:" + userId); log.notice("GET#getToken*start#userId:" + userId); return bluebirdRetry(function() {// 获取token,失败则重试三次 console.log("GET#getToken*retryStart#userId:" + userId); log.notice("GET#getToken*retryStart#userId:" + userId); return request.getAsync({ url: config.server.wxToken, timeout: config.requestTimeout }).then(function(response) { var result = JSON.parse(response.body); if (!result || result.returnValue !== 0) {// 如果status不为0则重试三次 console.log('GET#getToken*retryError#-1#url:' + config.server.wxToken + '#userId:' + user + '#error:' + (result && util.obj2ParamsString(result))); log.fatal('GET#getToken*retryError#-1#url:' + config.server.wxToken + '#userId:' + user + '#error:' + (result && util.obj2ParamsString(result))); throw new Error('getToken Error'); } else { return result } }); }, { max_tries: 3, interval: 200, backoff: 2 }) .then(function(result) { console.log("GET#getToken*end#userId:" + userId + "#token:" + util.obj2ParamsString(result)); log.notice("GET#getToken*end#userId:" + userId + "#token:" + util.obj2ParamsString(result)); return result; }) .catch(function(err) { log.fatal("GET#getToken*error#userId:" + userId + "#token:" + config.server.wxToken + '#' + err); log.fatal("GET#getToken*error#userId:" + userId + "#token:" + config.server.wxToken + '#' + err); return {}; });}
然而有时候并不是所有的情况都想做重试,有些错误情况需要即可失败以便通知开发人员,这时候只要抛出throw new bluebirdRetry.StopError('GetWXImg Error!');
即可,示例如下:
var retry = require('bluebird-retry');var i = 0;var err;var swing = function() { i++; console.log('strike ' + i); if (i == 3) { throw new retry.StopError('yer out'); } throw new Error('still up at bat');}; retry(swing, {timeout: 10000}).caught(function(e) { console.log(e.message)});
执行结果:
strike 1strike 2strike 3yer out
关于他的详细文档可以参考:
关于node中错误和异常的处理策略可以参考: