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

    学过unittest的都知道里面用前置和后置setup和teardown非常好用,在每次用例开始前和结束后都去执行一次. 当然还有更高级一点的setupClass和teardownClass,需配合@ ...

  2. kibana内存设置

    kibana是一个基于NodeJS的单页web应用.而NodeJS则是基于Chrome V8引擎的.V8引擎对于内存的使用是有限制的,默认情况下,64位系统下约为1.4GB,32位系统下约为0.7GB ...

  3. Oracle数据库 常用的触发器命令

    创建自增序列,创建触发器(在触发时间中操纵序列,实现主键自增): Oracle数据库不支持自增方法 create sequence seq_userInfo_usid start with ;--创建 ...

  4. Spring-Cloud之Ribbon负载均衡-3

    一.负载均衡是指将负载分摊到多个执行单元上,常见的负载均衡有两种方式.一种是独立进程单元,通过负载均衡策略,将请求转发到不同的执行单元上,例如 Ngnix .另一种是将负载均衡逻辑以代码的形式封装到服 ...

  5. .Net Core WebApi(2)—Swagger

    上一个版本的入门Swagger提示不够完整,这章着重完善和优化 Swagger用于将我们编写的接口自动生成规范化的文档,便于进行测试和对接    一.创建Swagger 1.1  Nuget 安装   ...

  6. mysql-表关系介绍(应用较多)

    目录 表之间的关系(重点) foreign key (外键) 级联操作 (cascade) 两种级联操作 外键的使用 多对一(一对多) 多对多 一对一关系 表之间的关系(重点) foreign key ...

  7. selenium自动化测试框架之PO设计模式

    面向对象的特性:封装.继承.多态.在自动化中一样适用,Selenium自动化测试中有一个名字常常被提及PageObject(思想与面向对象的特性相同),通过PO模式可以大大提高测试用例的维护效率. 传 ...

  8. Grafana+prometheus+AlertManager+钉钉机器人

    一.Grafana (1)安装Grafana的Linux环境 在官网下载windows的Grafana的压缩包到指定目录,解压缩Grafana压缩文件到包含当前Grafana版本的文件夹.将该文件夹解 ...

  9. php通过curl发送XML数据,并获取XML数据

    php编程中经常会用到用xml格式传送数据,如调用微信等第三方接口经常用到,这里演示下php以curl形式发送xml,并通过服务器接收 一.发送xml数据 -- postXml.php <?ph ...

  10. MES选型很困惑?避开这三个禁忌!

    MES系统的选型除了要充分剖析自己企业,掌握自己企业的需要.信息化的目标.自身的特点外,还要完全了解MES系统供应商,对其实力.软件性能.服务.用户.软件实施速度.价格进行了解与分析,这也是MES系统 ...