qps.js 2.44 KB
'use strict';

const logger = global.yoho.logger;
const cache = global.yoho.cache.master;
const Promise = require('bluebird');
const _ = require('lodash');
const {limitKey} = require('./vars');

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

// 页面访问限制
const REQUEST_LIMIT = {
    pc: {
        // 10s 最多访问5次
        10: 10,

        // 30s 最多访问10次
        30: 20,

        // 60s 最多访问15次
        60: 30,

        // 100s 最多访问100次
        600: 200
    },
    wap: {
        // 10s 最多访问20次
        10: 20,

        // 30s 最多访问40次
        30: 40,

        // 60s 最多访问60次
        60: 60,

        // 100s 最多访问200次
        600: 200
    }
};

module.exports = async({user}, next) => {
    const appType = user.appType;

    // 存储规则的cache keys
    let ruleKeys = {};
    let getOp = {};
    const MAX_TIMES = REQUEST_LIMIT[user.app];

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

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

    return Promise.props(getOp).then((results) => {
        logger.debug('[qps result] key=%j value=%j', ruleKeys, results);

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

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

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

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

                operation[`${appType}:${limitKey}:${user.ip}`] = cache.setAsync(`${appType}:${limitKey}:${user.ip}`, 1, limiterIpTime);
            } else {
                operation[cacheKey] = cache.incrAsync(cacheKey, 1);
            }
        });

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