...
|
...
|
@@ -4,37 +4,94 @@ |
|
|
const cache = global.yoho.cache.master;
|
|
|
const _ = require('lodash');
|
|
|
const logger = global.yoho.logger;
|
|
|
const config = global.yoho.config;
|
|
|
const helpers = global.yoho.helpers;
|
|
|
|
|
|
let ONE_DAY = 60 * 60 * 24;
|
|
|
const MAX_QPS = config.maxQps;
|
|
|
|
|
|
let pages = {
|
|
|
'/product/\\/pro_([\\d]+)_([\\d]+)\\/(.*)/': 5,
|
|
|
'/product/list/index': 5
|
|
|
};
|
|
|
|
|
|
function urlJoin(a, b) {
|
|
|
if (_.endsWith(a, '/') && _.startsWith(b, '/')) {
|
|
|
return a + b.substring(1, b.length);
|
|
|
} else if (!_.endsWith(a, '/') && !_.startsWith(b, '/')) {
|
|
|
return a + '/' + b;
|
|
|
} else {
|
|
|
return a + b;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
module.exports = (req, res, next) => {
|
|
|
let remoteIp = req.get('X-Forwarded-For') || '';
|
|
|
|
|
|
if (remoteIp.indexOf(',') > 0) {
|
|
|
let arr = remoteIp.split(',');
|
|
|
|
|
|
remoteIp = arr[0];
|
|
|
}
|
|
|
|
|
|
if (remoteIp && !_.get(req.app.locals, 'pc.sys.noLimiter')) { // 判断获取remoteIp成功,并且开关未关闭
|
|
|
if (remoteIp && !_.get(req.app.locals, 'pc.sys.noLimiter')) { // 判断获取remoteIp成功,并且开关未关闭
|
|
|
let key = `pc:limiter:${remoteIp}`;
|
|
|
|
|
|
logger.debug(`request limiter key=${key}`);
|
|
|
res.on('render', function() {
|
|
|
let route = req.route ? req.route.path : '';
|
|
|
let appPath = req.app.mountpath;
|
|
|
|
|
|
if (_.isArray(route) && route.length > 0) {
|
|
|
route = route[0];
|
|
|
}
|
|
|
|
|
|
cache.getAsync(key).then(result => {
|
|
|
let pageKey = urlJoin(appPath, route.toString()); // route may be a regexp
|
|
|
let pageIncr = pages[pageKey] || 0;
|
|
|
|
|
|
if (pageIncr > 0) {
|
|
|
cache.incrAsync(key, pageIncr);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
const limiterPage = () => {
|
|
|
let refer = req.method === 'GET' ? req.get('Referer') : '';
|
|
|
let limitAPI = helpers.urlFormat('/3party/check', {refer: refer});
|
|
|
let limitPage = helpers.urlFormat('/3party/check', {refer: req.protocol + '://' + req.get('host') + req.originalUrl});
|
|
|
|
|
|
if (_.indexOf(['/3party/check', '/passport/imagesNode', '/passport/cert/headerTip'], req.path) >= 0) {
|
|
|
return next();
|
|
|
}
|
|
|
|
|
|
if (req.xhr) {
|
|
|
return res.json({
|
|
|
code: 400,
|
|
|
data: {refer: limitAPI}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
return res.redirect(limitPage);
|
|
|
};
|
|
|
|
|
|
cache.getAsync(key).then((result) => {
|
|
|
if (result && _.isNumber(result)) {
|
|
|
if (result > 30) { // 判断 qps
|
|
|
res.status(403).end();
|
|
|
if (result > MAX_QPS) { // 判断 qps
|
|
|
cache.touch(key, ONE_DAY);
|
|
|
return limiterPage();
|
|
|
} else {
|
|
|
cache.incrAsync(key, 1); // qps + 1
|
|
|
next();
|
|
|
return next();
|
|
|
}
|
|
|
} else {
|
|
|
cache.setAsync(key, 1, 1); // 设置key,1s失效
|
|
|
next();
|
|
|
cache.setAsync(key, 1, 60); // 设置key,1m失效
|
|
|
return next();
|
|
|
}
|
|
|
}).catch(e => {
|
|
|
logger.error(`request limiter get key[${key}] from cache error.`, e);
|
|
|
next();
|
|
|
}).catch((err) => {
|
|
|
logger.error(`request limiter get key[${key}] from cache error.`, err);
|
|
|
return next();
|
|
|
});
|
|
|
} else {
|
|
|
next();
|
|
|
return next();
|
|
|
}
|
|
|
|
|
|
|
|
|
}; |
...
|
...
|
|