qps.js 1.96 KB
'use strict';

const logger = global.yoho.logger;
const cache = global.yoho.cache.master;
const config = global.yoho.config;
const Promise = require('bluebird');
const _ = require('lodash');

// 超出访问限制ip限制访问1小时
const limiterIpTime = 3600;

// 页面访问限制
const MAX_TIMES = config.REQUEST_LIMIT;

const limiterKey = 'limiter2';

module.exports = async({user}, next) => {
    if (!user.app || !user.ip) {
        return next();
    }

    // 存储规则的cache keys
    let ruleKeys = {};
    let getOp = {};

    _.forEach(MAX_TIMES, (val, key) => {
        ruleKeys[key] = `${user.app}:${limiterKey}:${key}:max:${user.ip}`;
        getOp[key] = cache.getAsync(ruleKeys[key]);
    });

    getOp.human = cache.getAsync(`${user.app}:${limiterKey}:ishuman:${user.ip}`);

    return Promise.props(getOp).then((results) => {
        if (results.human) { // 经过验证码之后1小时有效期内不再验证qps
            logger.warn('[qps] this user[%o] is being marked as human', user);
            return {};
        }

        // 遍历限制规则,若满足返回相应处理策略, 否则页面访问次数加1
        let operation = [];

        _.forEach(MAX_TIMES, (val, key) => {
            let cacheKey = ruleKeys[key];

            if (!results[key]) {
                operation.push(cache.setAsync(cacheKey, 1, +key));
            } else if (+results[key] > +val) {
                logger.warn('[qps] this user[%o] is being marked as rejected', user);

                // ip限制1小时
                operation.push(cache.setAsync(`${user.app}:${limiterKey}:${user.ip}`, 1, limiterIpTime));
            } else {
                operation.push(cache.incrAsync(cacheKey, 1));
            }
        });

        return Promise.all(operation);
    }).then((result) => {
        logger.debug('[qps] user[%o] result[%o]', user, result); // eslint-disable-line
    }).catch(err=>{
        logger.error(err);
    }).finally(() => {
        next();
    });
};