版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
1、Google Guava提供了限流工具类RateLimiter,在Zuul下添加如下依赖:
<!--限流-->
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>1.5.0.RELEASE</version>
</dependency>
2、在Zuul服务下新加RateLimitZuulFilter类,继承ZuulFilter,源码如下:
@Component
public class RateLimitZuulFilter extends ZuulFilter {
private Map<String, RateLimiter> map = Maps.newConcurrentMap();
@Autowired
private ObjectMapper objectMapper;
@Autowired
private SystemPublicMetrics systemPublicMetrics;
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
// 这边的order一定要大于org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter的order
// 也就是要大于5
// 否则,RequestContext.getCurrentContext()里拿不到serviceId等数据。
return 5;
}
@Override
public boolean shouldFilter() {
// 这里可以考虑弄个限流开启的开关,开启限流返回true,关闭限流返回false,你懂的。
Collection<Metric<?>> metrics = systemPublicMetrics.metrics();
Optional<Metric<?>> freeMemoryMetric = metrics.stream()
.filter(t -> "mem.free".equals(t.getName()))
.findFirst();
// 如果不存在这个指标,稳妥起见,返回true,开启限流
if (!freeMemoryMetric.isPresent()) {
return true;
}
long freeMemory = freeMemoryMetric.get()
.getValue()
.longValue();
// 如果可用内存小于700*1024KB,开启流控
System.out.println("-----------------------------------freeMemory="+freeMemory);
return freeMemory < 700*1024L;
}
@Override
public Object run() {
try {
RequestContext context = RequestContext.getCurrentContext();
String key = null;
// 对于service格式的路由,走RibbonRoutingFilter
String serviceId = (String) context.get(SERVICE_ID_KEY);
if (serviceId != null) {
key = serviceId;
map.putIfAbsent(serviceId, RateLimiter.create(50));
}else {
// 对于URL格式的路由,走SimpleHostRoutingFilter
URL routeHost = context.getRouteHost();
if (routeHost != null) {
String url = routeHost.toString();
key = url;
map.putIfAbsent(url, RateLimiter.create(100));
}
}
RateLimiter rateLimiter = map.get(key);
if (!rateLimiter.tryAcquire()) {
accessTokenFail("系统繁忙,请稍后再试!");
}
} catch (Exception e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return null;
}
/**
* 请求失败
* @param message
*/
public void accessTokenFail(String message){
Result result = new Result();
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletResponse response = ctx.getResponse();
ctx.setSendZuulResponse(false);
result.setType(TypeEnum.FAIL.getCode());
result.setMessage(message);
result.setTrue(false);
result.setCode(TypeEnum.rateLimit.getCode());
response.setContentType("application/json;charset=UTF-8");
try {
response.getWriter().write(objectMapper.writeValueAsString(result));
}catch (Exception e){
e.printStackTrace();
}
}
}
3、为了方便测试,在Zuul配置中添加如下配置:
#目标主机最大连接数和每个主机的初始连接数。
zuul.host.max-total-connections=1000
zuul.host.max-per-route-connections=500
zuul.semaphore.max-semaphores=5000
#设置Hystrix隔离策略为线程池
zuul.ribbon-isolation-strategy=thread
#每个路由使用独立的线程池
zuul.thread-pool.use-separate-thread-pools=true
#修改线程池中的coreSize 默认10
hystrix.threadpool.default.coreSize=500
4、测试内容截图:
1、下载apache的jmeter进行测试
2、在jmeter里新加测试计划:5000个请求在10秒内完成(500/s),这是我在上面配置的线程数
3、启动测试(十秒内当系统freeMemory<700*1024L KB时,开启自动限流,返回‘系统繁忙’)