importcom.alibaba.fastjson.JSONObject;importorg.apache.commons.lang.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;/***@authorhaosenwei[haosenwei@dubmic.com]
* @date 2019-06-14 14:59
*
Copyright 2008-2019 snsndk.com
*/@Componentpublic classFunnelRateLimiter {@Autowired
ICache iCache;/*** 漏斗容量*/
private static final int CAPACITY = 3;/*** 每单个单位时间允许的流量*/
private static final int ALLOWQUOTA = 3;/*** 单位时间(秒)*/
private static final int PERSECOND = 1;/*** 判断是否可以访问
*
*@paramdid 唯一标识
*@paramaction 操作
*@return是否运行访问*/
public booleanisActionAllowed(String did, String action) {return this.isActionAllowed(did, action, CAPACITY, ALLOWQUOTA, PERSECOND);
}/*** 根据给定的漏斗参数检查是否允许访问
*
*@paramdid 用户名
*@paramaction 操作
*@paramcapacity 漏斗容量
*@paramallowQuota 每单个单位时间允许的流量
*@paramperSecond 单位时间(秒)
*@return是否允许访问*/
public boolean isActionAllowed(String did, String action, int capacity, int allowQuota, intperSecond) {
String key= "funnel:" + action + ":" +did;
String s=iCache.get(key);
Funnel funnel= null;if(StringUtils.isBlank(s)) {
funnel= newFunnel(capacity, allowQuota, perSecond);this.iCache.set(key, JSONObject.toJSONString(funnel));
}else{
funnel= JSONObject.parseObject(s, Funnel.class);
}return funnel.watering(1);
}private static classFunnel {private intcapacity;private floatleakingRate;private intleftQuota;private longleakingTs;public Funnel(int capacity, int count, intperSecond) {this.capacity =capacity;//因为计算使用毫秒为单位的
perSecond *= 1000;this.leakingRate = (float) count /perSecond;
}/*** 根据上次水流动的时间,腾出已流出的空间*/
private voidmakeSpace() {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;
}/*** 漏斗漏水
*
*@paramquota 流量
*@return是否有足够的水可以流出(是否允许访问)*/
public boolean watering(intquota) {
makeSpace();int left = leftQuota -quota;if (left >= 0) {
leftQuota=left;return true;
}return false;
}
}
}