前段时间,项目中需要对某些访问量较高的路径进行访问并发数控制,以及有些功能,比如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. kie-api介绍和使用

    参考:KIE kie在drools jbpm uberfire里广泛被使用,下面对kie-api中的几个重要组件做下简单介绍 maven依赖 <dependency> <groupI ...

  2. C# vb .net实现过度曝光效果滤镜

    在.net中,如何简单快捷地实现Photoshop滤镜组中的过度曝光效果呢?答案是调用SharpImage!专业图像特效滤镜和合成类库.下面开始演示关键代码,您也可以在文末下载全部源码: 设置授权 第 ...

  3. Linux Shell/Bash wildcard通配符、元字符、转义符使用

    说到shell通配符(wildcard),大家在使用时候会经常用到.下面是一个实例: 1   1 2 3 4 [chengmo@localhost ~/shell]$ ls a.txt  b.txt  ...

  4. 4.matplotlib绘制直方图

      # coding=utf-8 from matplotlib import pyplot as plt from matplotlib import font_manager a=[131, ...

  5. 【转载】C#中float.TryParse方法和float.Parse方法的异同之处

    在C#编程过程中,float.TryParse方法和float.Parse方法都可以将字符串string转换为单精度浮点类型float,但两者还是有区别,最重要的区别在于float.TryParse方 ...

  6. win10设置锁屏密码

    1.点击右下角窗口键 2.选择点击设置 3.点击账户 4.点击登录选项 5.点击密码,添加密码 6.设置密码 7.使用快捷键“窗口键+l”锁屏,就会提示你输入密码

  7. js计算hashcode

    String.prototype.hashCode = function(){ var hash = 0; for (var i = 0; i < this.length; i++) { var ...

  8. jstorm了解—应用场景

    JStorm处理数据的方式是基于消息的流水线处理, 因此特别适合无状态计算,也就是计算单元的依赖的数据全部在接受的消息中可以找到, 并且最好一个数据流不依赖另外一个数据流. 因此,常常用于: 日志分析 ...

  9. 【OGG】OGG的下载和安装篇

    [OGG]OGG的下载和安装篇 一.1  BLOG文档结构图 一.2  前言部分 一.2.1  导读 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不知道的知识,~O(∩_∩ ...

  10. oracle中的存储过程(实例一)

    引子 这是测试环境存在了很久的问题.由于基础配置信息(如:代理人信息)不像生产环境有专人维护,常常会有数据过期,导致无法使用的情况. 而很多配置数据是在外围系统维护(如代理人信息,在销管系统)以往的解 ...