在高并发应用场景中,经常会出现访问突然增大的情况,例如:
某天A君突然发现自己的接口请求量突然涨到之前的10倍,没多久该接口几乎不可使用,并引发连锁反应导致整个系统崩溃。如何应对这种情况呢?生活给了我们答案:比如老式电闸都安装了保险丝,一旦有人使用超大功率的设备,保险丝就会烧断以保护各个电器不被强电流给烧坏。同理我们的接口也需要安装上“保险丝”,以防止非预期的请求对系统压力过大而引起的系统瘫痪,当流量过大时,可以采取拒绝或者引流等机制。
常见的限流算法,有以下两种
漏桶算法
漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率
令牌桶算法
对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。如图2所示,令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。
SpringCloud微服务中,我们可以使用Zuul来实现对于接口的限流,我们只需要编写一个过滤器就可以,例如以下例子:
我们可以编写一个pre类型的Zuul过滤器,然后使用Google Guava 为我们提供了限流工具类RateLimiter ,来完成限流的功能(该类基于令牌桶算法来完成限流,非常易于使用)
单节点限流案例:
@Component
public class RateLimitZuulFilter extends ZuulFilter {
private final RateLimiter rateLimiter = RateLimiter.create(1000.0);
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public boolean shouldFilter() {
// 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false
return true;
}
@Override
public Object run() {
try {
RequestContext currentContext = RequestContext.getCurrentContext();
HttpServletResponse response = currentContext.getResponse();
//判断如果获取令牌失败,则抛出异常
if (!rateLimiter.tryAcquire()) {
HttpStatus httpStatus = HttpStatus.TOO_MANY_REQUESTS;
response.setContentType(MediaType.TEXT_PLAIN_VALUE);
response.setStatus(httpStatus.value());
response.getWriter().append(httpStatus.getReasonPhrase());
currentContext.setSendZuulResponse(false);
throw new ZuulException(
httpStatus.getReasonPhrase(),
httpStatus.value(),
httpStatus.getReasonPhrase()
);
}
} catch (Exception e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return null;
}
}
分布式场景限流案例:
网友评论