一、限流场景
限流场景一般基于硬件资源的使用负载,包括CPU,内存,IO。例如某个报表服务需要消耗大量内存,如果并发数增加就会拖慢整个应用,甚至内存溢出导致应用挂掉。
限流适用于会动态增加的资源,已经池化的资源不一定需要限流,例如数据库连接池,它是已经确定的资源,池的大小固定(即使可以动态伸缩池大小),这种场景下并不需要通过限流来实现,只要能做到如果池内链接已经使用完,则无法再获取新的连接则可。
因此,使用限流的前提是:
1.防止资源使用过载产生不良影响。
2.使用的资源会动态增加,例如一个站点的请求。
二、Spring中实现限流
I、限流需求
1.只针对Controller限流
2.根据url请求路径限流
3.可根据正则表达式匹配url来限流 4.可定义多个限流规则,每个规则的最大流量不同
II、相关类结构
1.CurrentLimiteAspect是一个拦截器,在controller执行前后执行后拦截
2.CurrentLimiter是限流器,可以添加限流规则,根据限流规则获取流量通行证,释放流量通行证;如果获取通行证失败则抛出异常。
3.LimiteRule是限流规则,限流规则可设置匹配url的正则表达式和最大流量值,同时获取该规则的流量通信证和释放流量通信证。
4.AcquireResult是获取流量通信证的结果,结果有3种:获取成功,获取失败,不需要获取。
5.Application是Spring的启动类,简单起见,在启动类种添加限流规则。III、Show me code
1.AcquireResult.java
public class AcquireResult {
/** 获取通行证成功 */
public static final int ACQUIRE_SUCCESS = 0;
/** 获取通行证失败 */
public static final int ACQUIRE_FAILED = 1;
/** 不需要获取通行证 */
public static final int ACQUIRE_NONEED = 2;
/** 获取通行证结果 */
private int result;
/** 可用通行证数量 */
private int availablePermits;
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
public int getAvailablePermits() {
return availablePermits;
}
public void setAvailablePermits(int availablePermits) {
this.availablePermits = availablePermits;
}
}
2.LimiteRule.java
/**
* @ClassName LimiteRule
* @Description TODO
* @Author 铿然一叶
* @Date 2019/10/4 20:18
* @Version 1.0
* javashizhan.com
**/
public class LimiteRule {
/** 信号量 */
private final Semaphore sema;
/** 请求URL匹配规则 */
private final String pattern;
/** 最大并发数 */
private final int maxConcurrent;
public LimiteRule(String pattern, int maxConcurrent) {
this.sema = new Semaphore(maxConcurrent);
this.pattern = pattern;
this.maxConcurrent = maxConcurrent;
}
/**
* 获取通行证。这里加同步是为了打印可用通行证数量时看起来逐个减少或者逐个增加,无此打印需求可不加synchronized关键字
* @param urlPath 请求Url
* @return 0-获取成功,1-没有获取到通行证,2-不需要获取通行证
*/
public synchronized AcquireResult tryAcquire(String urlPath) {
AcquireResult acquireResult = new AcquireResult();
acquireResult.setAvailablePermits(this.sema.availablePermits());
try {
//Url请求匹配规则则获取通行证
if (Pattern.matches(pattern, urlPath)) {
boolean acquire =