前段时间,项目中需要对某些访问量较高的路径进行访问并发数控制,以及有些功能,比如Excel导出下载功能,数据量很大的情况下,用户不断的点击下载按钮,重复请求数据库,导致线上数据库挂掉。于是在这样的情况下,这个限流组件应运而生,也许有人会提及SpringCloud zuul,其实它的现也是借助了RateLimiter。由于项目使用的是SpringBoot,也就没往外思考。反正最后功能实现了就行,毕竟殊途同归啊。本文只是用代码来快速的帮你理清整个限流的流程,至于RateLimiter中具体限流算法以及Semaphore信号量的具体实现还是得自己去深挖了,这里就不再展开了。正片时间到:

0、由于需要对限流的路径进行后台管理,那限流实体肯定是需要的

public class RateLimit {
private String rateLimitId;
/**
* 限流路径,支持通配符,示例 /user/**
*/
private String limitPath;
/**
* 每秒限流频率
*/
private Integer permitsPerSecond;
/**
* 限流等待超时时间,单位s
*/
private Integer permitsTimeOut;
/**
* 排序
*/private Integer orderNo;
/**
* 最大线程数
*/
private Integer maxThread;
/**
* 创建时间
*/
private Date gmtCreate;
//get、set略
}

1、因为要借助RateLimiter类,所以再封装一个限流信息类

/**
* @描述: 限流信息
*/
public class RateLimitInfo { private RateLimiter rateLimiter; private RateLimitVo rateLimitVo; private long lastUpdateTime; public RateLimitInfo(RateLimiter rateLimiter, RateLimitVo rateLimitVo, long lastUpdateTime) {
this.rateLimiter = rateLimiter;
this.rateLimitVo = rateLimitVo;
this.lastUpdateTime = lastUpdateTime;
}
//get、set略
}

2、定义限流策略RateLimitStrategist

/**
* @描述: 限流策略
*/
public class RateLimitStrategist { private PathMatcher pathMatcher = new AntPathMatcher(); private final Map<String, RateLimitInfo> limiterMap = new LinkedHashMap<>(); private final Map<String, Semaphore> threadMap = new LinkedHashMap<>(); /**
* 更新频率,意为后台配置路径后5分钟生效
*/
private static final long UPDATE_RATE = 1000*60*5; private long lastUpdateTime = 0; @Autowired
private RateLimitManager rateLimitManager; public void init() {
limiterMap.clear();
threadMap.clear();
List<RateLimitVo> rateLimitVos = rateLimitManager.findListForPriority(); //查询数据库中配置的路径信息,需要自己实现
if(CollectionUtils.isNotEmpty(rateLimitVos)) {
return;
}
for (RateLimitVo rateLimitVo : rateLimitVos) {
RateLimiter rateLimiter = RateLimiter.create(rateLimitVo.getPermitsPerSecond());
limiterMap.put(rateLimitVo.getLimitPath(), new RateLimitInfo(rateLimiter, rateLimitVo, System.currentTimeMillis()));
threadMap.put(rateLimitVo.getLimitPath(), new Semaphore(rateLimitVo.getMaxThread(), true));
}
lastUpdateTime = System.currentTimeMillis();
} public boolean tryAcquire(String requestUri) {
//目前设置5分钟更新一次
if(System.currentTimeMillis() - lastUpdateTime > UPDATE_RATE) {
synchronized (this) {
if(System.currentTimeMillis() - lastUpdateTime > UPDATE_RATE) {
init();
}
}
} for (Map.Entry<String, RateLimitInfo> entry : limiterMap.entrySet()) {
if(!pathMatcher.match(entry.getKey(), requestUri)) {
continue;
}
RateLimitInfo rateLimitInfo = entry.getValue();
RateLimitVo rateLimitVo = rateLimitInfo.getRateLimitVo();
RateLimiter rateLimiter = rateLimitInfo.getRateLimiter();
boolean concurrentFlag = rateLimiter.tryAcquire(1, rateLimitVo.getPermitsTimeOut(), TimeUnit.SECONDS);
if(!concurrentFlag) { //验证失败,直接返回
return concurrentFlag;
} else {
if(threadMap.get(requestUri).availablePermits() != 0) { //当前路径对应剩余可执行线程数不为0
try {
//申请可执行线程
threadMap.get(requestUri).acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
} else {
return false;
}
}
}
return true;
} public void setLastUpdateTime(long lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
} /**
* 释放路径对应的线程数
* @param requestURI
*/
public void releaseSemaphore(String requestURI) {
if(null != threadMap.get(requestURI)) {
threadMap.get(requestURI).release();
}
}
}

3、定义拦截器RateLimitFilter,在拦截器中调用限流策略

/**
* @描述: 限流过滤器,配置后生效
*/
public class RateLimitFilter implements Filter { private RateLimitStrategist rateLimitStrategist; private static final Logger LOGGER = LoggerFactory.getLogger(RateLimitFilter.class); @Override
public void init(FilterConfig filterConfig) throws ServletException { } @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if(rateLimitStrategist == null) {
rateLimitStrategist = InstanceFactory.getInstance(RateLimitStrategist.class);
}
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String requestURI = req.getRequestURI();
String contextPath = req.getContextPath();
if(StringUtils.isNotBlank(contextPath)) {
requestURI = StringUtils.substring(requestURI, contextPath.length());
}
if(!rateLimitStrategist.tryAcquire(requestURI)) {
res.setContentType("text/html;charset=UTF-8");
res.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getWriter().write("当前服务器繁忙,请稍后再试!");
LOGGER.info(requestURI + "路径请求服务器繁忙,请稍后再试");
} else {
try {
chain.doFilter(request, response);
} catch (Exception e) {
e.printStackTrace();
} finally {
rateLimitStrategist.releaseSemaphore(requestURI);
}
} } @Override
public void destroy() { }
}

4、需要的配置(采用注解也可以)

先在web.xml中引入过滤器(开始处)

<filter>
<filter-name>rateLimiter</filter-name>
<filter-class>com.limit.filter.RateLimitFilter</filter-class>
</filter> <filter-mapping>
<filter-name>rateLimiter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

然后在context.xml中注入RateLimitStrategist

<bean id="rateLimitStrategist" class="com.limit.factory.RateLimitStrategist" />

5、代码tag-0.0.1是项目单节点部署可用,版本tag-0.0.2为适应多节点部署改为redis来实现对处理线程数的控制

**需要源码留邮箱**

限流神器之-Guava RateLimiter 实战的更多相关文章

  1. 常用限流算法与Guava RateLimiter源码解析

    在分布式系统中,应对高并发访问时,缓存.限流.降级是保护系统正常运行的常用方法.当请求量突发暴涨时,如果不加以限制访问,则可能导致整个系统崩溃,服务不可用.同时有一些业务场景,比如短信验证码,或者其它 ...

  2. 服务限流 -- 自定义注解基于RateLimiter实现接口限流

    1. 令牌桶限流算法 令牌桶会以一个恒定的速率向固定容量大小桶中放入令牌,当有浏览来时取走一个或者多个令牌,当发生高并发情况下拿到令牌的执行业务逻辑,没有获取到令牌的就会丢弃获取服务降级处理,提示一个 ...

  3. 限流神器Sentinel,不了解一下吗?

    概述 书接上回:你来说说什么是限流? ,限流的整体概述中,描述了 限流是什么,限流方式和限流的实现.在文章尾部的 分布式限流,没有做过多的介绍,选择了放到这篇文章中.给大伙细细讲解一下 Sentine ...

  4. 阿里限流神器Sentinel夺命连环 17 问?

    1.前言 这是<spring Cloud 进阶>专栏的第五篇文章,这篇文章介绍一下阿里开源的流量防卫兵Sentinel,一款非常优秀的开源项目,经过近10年的双十一的考验,非常成熟的一款产 ...

  5. 一个轻量级的基于RateLimiter的分布式限流实现

    上篇文章(限流算法与Guava RateLimiter解析)对常用的限流算法及Google Guava基于令牌桶算法的实现RateLimiter进行了介绍.RateLimiter通过线程锁控制同步,只 ...

  6. 超详细的Guava RateLimiter限流原理解析

    超详细的Guava RateLimiter限流原理解析  mp.weixin.qq.com 点击上方“方志朋”,选择“置顶或者星标” 你的关注意义重大! 限流是保护高并发系统的三把利器之一,另外两个是 ...

  7. 【Guava】使用Guava的RateLimiter做限流

    一.常见的限流算法 目前常用的限流算法有两个:漏桶算法和令牌桶算法. 1.漏桶算法 漏桶算法的原理比较简单,请求进入到漏桶中,漏桶以一定的速率漏水.当请求过多时,水直接溢出.可以看出,漏桶算法可以强制 ...

  8. coding++:RateLimiter 限流算法之漏桶算法、令牌桶算法--简介

    RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类 <dependency> <groupId>com.google.guava</g ...

  9. ☕【Java技术指南】「并发编程专题」针对于Guava RateLimiter限流器的入门到精通(含实战开发技巧)

    并发编程的三剑客 在开发高并发系统时有三剑客:缓存.降级和限流. 缓存 缓存的目的是提升系统访问速度和增大系统处理容量. 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题 ...

随机推荐

  1. Jenkins+Gitlab配置Webhook实现提交自动部署

    一.概述 在上一篇文章,链接如下: https://www.cnblogs.com/xiao987334176/p/11434849.html 已经实现了 Jenkins+harbor+gitlab+ ...

  2. pytest_skip跳过用例

    前言 pytest.mark.skip可以标记无法在某些平台上运行的测试功能,或者您希望失败的测试功能 skip意味着只有在满足某些条件时才希望测试通过,否则pytest应该跳过运行测试. 常见示例是 ...

  3. Golang资料集

    <Platform-native GUI library for Go> 介绍:跨平台的golang GUI库,支持Windows(xp以上),Unix,Mac OS X(Mac OS X ...

  4. WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里?

    原文:WPF 程序鼠标在窗口之外的时候,控件拿到的鼠标位置在哪里? 在 WPF 程序中,我们有 Mouse.GetPosition(IInputElement relativeTo) 方法可以拿到鼠标 ...

  5. Source roots (or source folders) Test source roots (or test source folders; shown as rootTest)Resource rootsTest resource roots

    idea中Mark Directory As里的Sources Root.ReSources Root等的区别 1.Source roots (or source folders) 通过这个类指定一个 ...

  6. [HNOI2012]矿场搭建 (点双连通)

    题目 [HNOI2012]矿场搭建 解析 这个题做的我十分自闭.. 没看出这个是个点双,然后一晚上+半上午.. 一看肯定和割点有关,我们找到所有的点双,会发现有这么几种情况 连通块中一个割点也没有,这 ...

  7. 解决centos7下 selenium报错--unknown error: DevToolsActivePort file doesn't exist

    解决centos7下 selenium报错--unknown error: DevToolsActivePort file doesn't exist 早上在linux下用selenium启动Chro ...

  8. 【开发工具】- Windows下多个jdk版本切换

    一.直接安装jdk,如图我安装了JDK6.JDK7和JDK8三个版本: 二.在安装JDK8后需要在 C:\Windows\System32 该目录下删除 java.exe 和 javaw.exe两个文 ...

  9. PHP防止SQL注入攻击和XSS攻击

    代码如下: /** * 防SQL注入和XSS攻击 * @param $arr */ function SafeFilter (&$arr) { $ra=Array('/([\x00-\x08, ...

  10. 嵌入式 vlc从接收到数据流到播放视频的过程分析(经典)

    个人整理: Vlc流播放流程 vlc源码目录树: 目录名称 说明 bindings Java, CIL 和Python绑定 doc 帮助文档 (不是更新的) extras 另叙. include VL ...