应用场景
疲劳度模型在很多场景下都是必不可少的,比如限制用户在一定时间内发送短信的次数、限制用户在一定时间内请求接口的次数等等。通过限制用户的操作次数,可以避免用户过度使用某个功能或接口,从而保护系统的稳定性和安全性。
设计与功能
这个疲劳度模型包含了两个维度的疲劳度配置:全平台维度和userId维度。全平台维度的疲劳度配置是所有用户共享的,而userId维度的疲劳度配置是每个用户单独拥有的。模型中的consume方法用于消耗一个疲劳度,每次调用会检查全平台维度和userId维度的疲劳度是否已经用完。如果还有剩余,则消耗一个疲劳度并返回true;否则返回false。模型中还包含了设置全平台维度和userId维度疲劳度配置的方法。
用法
使用这个疲劳度模型非常简单。首先,需要创建一个FatigueModel对象。可以使用默认的配置,也可以自定义配置。然后,在需要限制操作次数的地方调用consume方法,传入userId参数即可。如果consume方法返回true,则说明还有剩余的疲劳度,可以进行操作;如果返回false,则说明疲劳度已经用完,需要等待疲劳度周期结束后才能进行操作。
编码实现
用户维度疲劳度模型默认一天20次
全平台维度的疲劳度默认一天10000次
package com.eden.core.model;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 疲劳度模型
*
* @author Eden
*/
@Slf4j
@NoArgsConstructor
public class FatigueModel {
/**
* 全平台维度的疲劳度配置-默认一天10000次
*/
private int globalLimit = 10000;
/**
* 全平台维度的疲劳度配置-疲劳度周期 默认一天
*/
private long globalPeriod = 24 * 60 * 60 * 1000;
/**
* 全平台维度的疲劳度配置-当前疲劳度次数
*/
private int currentGlobalLimit = globalLimit;
/**
* 全平台疲劳度周期的开始时间
*/
private long globalStartTime = System.currentTimeMillis();
/**
* userId维度的疲劳配置
*/
private final Map<String, Integer> userLimits = new HashMap<>();
private final Map<String, Long> userStartTimes = new HashMap<>();
/**
* userId维度的疲劳配置-默认一天20次
*/
private int userIdLimit = 20;
/**
* userId维度的疲劳配置-疲劳度周期为一天
*/
private long userIdPeriod = 24 * 60 * 60 * 1000;
public FatigueModel(int globalLimit, int userIdLimit) {
this.globalLimit = globalLimit;
this.currentGlobalLimit = globalLimit;
this.userIdLimit = userIdLimit;
}
public FatigueModel(int globalLimit, long globalPeriod, int userIdLimit, long userIdPeriod) {
this.globalLimit = globalLimit;
this.currentGlobalLimit = globalLimit;
this.globalPeriod = globalPeriod;
this.userIdLimit = userIdLimit;
this.userIdPeriod = userIdPeriod;
}
/**
* 用户调用一次,消耗一个疲劳度
*/
public synchronized boolean consume(String userId) {
// 获取当前时间
long now = System.currentTimeMillis();
// 更新全平台维度的疲劳度周期
if (now - globalStartTime >= globalPeriod) {
// 重置全平台维度的疲劳度
currentGlobalLimit = globalLimit;
// 更新全平台疲劳度周期的开始时间
globalStartTime = now;
}
// 判断全平台维度的疲劳度是否已经用完
if (currentGlobalLimit <= 0) {
log.info(String.format("consume,currentGlobalLimit <= 0,userId=%s,now=%s,currentGlobalLimit=%s,globalStartTime=%s",
userId, now, currentGlobalLimit, globalStartTime));
return false;
}
// 判断userId维度的疲劳度是否已经用完
int limit = fetchUserLimit(userId);
if (limit <= 0) {
return false;
}
// 更新全平台维度的疲劳度和userId维度的疲劳度
currentGlobalLimit--;
userLimits.put(userId, limit - 1);
return true;
}
/**
* 设置全平台维度的疲劳度配置
*/
public synchronized void setGlobalLimit(int limit, long period) {
globalLimit = limit;
currentGlobalLimit = limit;
globalPeriod = period;
globalStartTime = System.currentTimeMillis();
}
/**
* 设置userId维度的疲劳度配置
*/
public synchronized void setUserLimit(String userId, int limit, long period) {
userLimits.put(userId, limit);
userStartTimes.put(userId, System.currentTimeMillis() - period);
}
/**
* 充值userId维度的疲劳度
*/
public synchronized void addUserLimit(String userId, int limit) {
Integer userLimit = userLimits.getOrDefault(userId, this.userIdLimit);
userLimits.put(userId, userLimit + limit);
}
/**
* 获取用户当前剩余疲劳度
*/
public int fetchUserLimit(String userId) {
long now = System.currentTimeMillis();
// 获取用户的疲劳度周期和周期内可用次数
int limit = userLimits.getOrDefault(userId, userIdLimit);
Long userStartTime = userStartTimes.get(userId);
// 判断是否在疲劳度周期内,如果不在则重置周期内可用次数
if (Objects.isNull(userStartTime) || (now - userStartTime >= userIdPeriod)) {
userStartTimes.put(userId, now);
limit = userIdLimit;
userLimits.put(userId, limit);
}
log.info(String.format("fetchUserLimit,userId=%s,now=%s,limit=%s,userStartTime=%s",
userId, now, limit, userStartTime));
return limit;
}
/**
* 校验用户疲劳度
*/
public boolean checkUserLimit(String userId) {
return fetchUserLimit(userId) > 0;
}
}
网友评论