漏斗算法 java

import com.alibaba.fastjson.JSONObject;import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author haosenwei[haosenwei@dubmic.com]
 * @date 2019-06-14 14:59
 * <p>Copyright 2008-2019 snsndk.com</p>
 */
@Component
public class FunnelRateLimiter {

    @Autowired
    ICache iCache;

    /**
     * 漏斗容量
     */
    private static final int CAPACITY = 3;
    /**
     * 每单个单位时间允许的流量
     */
    private static final int ALLOWQUOTA = 3;

    /**
     * 单位时间(秒)
     */
    private static final int PERSECOND = 1;

    /**
     * 判断是否可以访问
     *
     * @param did    唯一标识
     * @param action 操作
     * @return 是否运行访问
     */
    public boolean isActionAllowed(String did, String action) {
        return this.isActionAllowed(did, action, CAPACITY, ALLOWQUOTA, PERSECOND);
    }

    /**
     * 根据给定的漏斗参数检查是否允许访问
     *
     * @param did        用户名
     * @param action     操作
     * @param capacity   漏斗容量
     * @param allowQuota 每单个单位时间允许的流量
     * @param perSecond  单位时间(秒)
     * @return 是否允许访问
     */
    public boolean isActionAllowed(String did, String action, int capacity, int allowQuota, int perSecond) {
        String key = "funnel:" + action + ":" + did;
        String s = iCache.get(key);
        Funnel funnel = null;
        if (StringUtils.isBlank(s)) {
            funnel = new Funnel(capacity, allowQuota, perSecond);
            this.iCache.set(key, JSONObject.toJSONString(funnel));
        } else {
            funnel = JSONObject.parseObject(s, Funnel.class);
        }
        return funnel.watering(1);
    }

    private static class Funnel {
        private int capacity;
        private float leakingRate;
        private int leftQuota;
        private long leakingTs;

        public Funnel(int capacity, int count, int perSecond) {
            this.capacity = capacity;
            // 因为计算使用毫秒为单位的
            perSecond *= 1000;
            this.leakingRate = (float) count / perSecond;
        }

        /**
         * 根据上次水流动的时间,腾出已流出的空间
         */
        private void makeSpace() {
            long now = System.currentTimeMillis();
            long time = now - leakingTs;
            int leaked = (int) (time * leakingRate);
            if (leaked < 1) {
                return;
            }
            leftQuota += leaked;
            // 如果剩余大于容量,则剩余等于容量
            if (leftQuota > capacity) {
                leftQuota = capacity;
            }
            leakingTs = now;
        }

        /**
         * 漏斗漏水
         *
         * @param quota 流量
         * @return 是否有足够的水可以流出(是否允许访问)
         */
        public boolean watering(int quota) {
            makeSpace();
            int left = leftQuota - quota;
            if (left >= 0) {
                leftQuota = left;
                return true;
            }
            return false;
        }
    }
}
原文地址:https://www.cnblogs.com/go4mi/p/11023411.html