SpringBoot项目的限流
开发访问量比较大的系统是,爬虫的目的就是解决访问量大的问题;缓存穿透是为了保护后端数据库查询服务;计数服务解决了接近真实访问量以及数据库服务的压力。
架构图

限流
就拿十万博客来说,如果存在热点文章,可能会有数十万级别的并发用户参与阅读。如果想让这些用户正常访问,无非就是加机器横向扩展各种服务,但凡事都有一个利益平衡点,有时候只需要少量的机器保证大部分用户在大部分时间可以正常访问即可。
亦或是,如果存在大量爬虫或者恶意攻击,我们必须采取一定的措施来保证服务的正常运行。这时候我们就要考虑限流来保证服务的可用性,以防止非预期的请求对系统压力过大而引起的系统瘫痪。通常的策略就是拒绝多余的访问,或者让多余的访问排队等待服务。
限流算法
任何限流都不是漫无目的的,也不是一个开关就可以解决的问题,常用的限流算法有:令牌桶,漏桶。
令牌桶
令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据的数目,并允许突发数据的发送(百科)。

用户的请求速率是不固定的,这里我们假定为10r/s,令牌按照5个每秒的速率放入令牌桶,桶中最多存放20个令牌。仔细想想,是不是总有那么一部分请求被丢弃。
漏桶
漏桶算法的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量(百科)。

令牌桶是无论你流入速率多大,我都按照既定的速率去处理,如果桶满则拒绝服务。
应用限流
Tomcat
在Tomcat容器中,我们可以通过自定义线程池,配置最大连接数,请求处理队列等参数来达到限流的目的。

Tomcat默认使用自带的连接池,这里我们也可以自定义实现,打开/conf/server.xml文件,在Connector之前配置一个线程池:

name:共享线程池的名字。这是Connector为了共享线程池要引用的名字,该名字必须唯一。默认值:None;
namePrefix:在JVM上,每个运行线程都可以有一个name 字符串。这一属性为线程池中每个线程的name字符串设置了一个前缀,Tomcat将把线程号追加到这一前缀的后面。默认值:tomcat-exec-;
maxThreads:该线程池可以容纳的最大线程数。默认值:200;
maxIdleTime:在Tomcat关闭一个空闲线程之前,允许空闲线程持续的时间(以毫秒为单位)。只有当前活跃的线程数大于minSpareThread的值,才会关闭空闲线程。默认值:60000(一分钟)。
minSpareThreads:Tomcat应该始终打开的最小不活跃线程数。默认值:25。
配置Connector

executor:表示使用该参数值对应的线程池;
minProcessors:服务器启动时创建的处理请求的线程数;
maxProcessors:最大可以创建的处理请求的线程数;
acceptCount:指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。
API限流
这里我们采用开源工具包guava提供的限流工具类RateLimiter进行API限流,该类基于"令牌桶算法",开箱即用。
自定义定义注解

自定义切面

业务实现:

分布式限流
Nginx
如何使用Nginx实现基本的限流,比如单个IP限制每秒访问50次。通过Nginx限流模块,我们可以设置一旦并发连接数超过我们的设置,将返回503错误给客户端。
配置nginx.conf

配置说明
imitconnzone
是针对每个IP定义一个存储session状态的容器。这个示例中定义了一个100m的容器,按照32bytes/session,可以处理3200000个session。
limit_rate 300k;
对每个连接限速300k. 注意,这里是对连接限速,而不是对IP限速。如果一个IP允许两个并发连接,那么这个IP就是限速limit_rate×2。
burst=5;
这相当于桶的大小,如果某个请求超过了系统处理速度,会被放入桶中,等待被处理。如果桶满了,那么抱歉,请求直接返回503,客户端得到一个服务器忙的响应。如果系统处理请求的速度比较慢,桶里的请求也不能一直待在里面,如果超过一定时间,也是会被直接退回,返回服务器忙的响应。
OpenResty
这里我们使用 OpenResty 开源的限流方案,测试案例使用OpenResty1.15.8.1最新版本,自带lua-resty-limit-traffic模块以及案例 ,实现起来更为方便。
限制接口总并发数/请求数
热点博文,由于突发流量暴增,有可能会影响整个系统的稳定性从而造成崩溃,这时候我们就要限制热点博文的总并发数/请求数。
这里我们采用 lua-resty-limit-traffic中的resty.limit.count模块实现:

限制接口时间窗请求数
现在网络爬虫泛滥,有时候并不是人为的去点击,亦或是存在恶意攻击的情况。此时我们就要对客户端单位时间内的请求数进行限制,以至于黑客不是那么猖獗。当然了道高一尺魔高一丈,攻击者总是会有办法绕开你的防线,从另一方面讲也促进了技术的进步。
这里我们采用 lua-resty-limit-traffic中的resty.limit.conn模块实现:

平滑限制接口请求数
之前的限流方式允许突发流量,也就是说瞬时流量都会被允许。突然流量如果不加以限制会影响整个系统的稳定性,因此在秒杀场景中需要对请求整形为平均速率处理,即20r/s。
这里我们采用 lua-resty-limit-traffic 中的resty.limit.req 模块实现漏桶限流和令牌桶限流。
其实漏桶和令牌桶根本的区别就是,如何处理超过请求速率的请求。漏桶会把请求放入队列中去等待均速处理,队列满则拒绝服务;令牌桶在桶容量允许的情况下直接处理这些突发请求。
漏桶
桶容量大于零,并且是延迟模式。如果桶没满,则进入请求队列以固定速率等待处理,否则请求被拒绝。
令牌桶
桶容量大于零,并且是非延迟模式。如果桶中存在令牌,则允许突发流量,否则请求被拒绝。
压测
为了测试以上配置效果,我们采用AB压测,Linux下执行以下命令即可:

测试命令:

测试结果:

总结
以上限流方案,只是针对此次大访问量项目做一个简单的小结,大家也不要刻意区分那种方案的好坏,只要适合业务场景就是最好的。
SpringBoot项目的限流的更多相关文章
- SpringBoot 如何进行限流?老鸟们都这么玩的!
大家好,我是飘渺.SpringBoot老鸟系列的文章已经写了四篇,每篇的阅读反响都还不错,那今天继续给大家带来老鸟系列的第五篇,来聊聊在SpringBoot项目中如何对接口进行限流,有哪些常见的限流算 ...
- springboot + aop + Lua分布式限流的最佳实践
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 一.什么是限流?为什么要限流? 不知道大家有没有做过帝都的地铁, ...
- 【Distributed】限流技巧
一.概述 1.1 高并发服务限流特技 1.2 为什么要互联网项目要限流 1.3 高并发限流解决方案 二.限流算法 2.1 计数器 2.2 滑动窗口计数 2.3 令牌桶算法 使用RateLimiter实 ...
- SpringBoot 整合 RabbitMQ(包含三种消息确认机制以及消费端限流)
目录 说明 生产端 消费端 说明 本文 SpringBoot 与 RabbitMQ 进行整合的时候,包含了三种消息的确认模式,如果查询详细的确认模式设置,请阅读:RabbitMQ的三种消息确认模式 同 ...
- SpringBoot 2.0 + 阿里巴巴 Sentinel 动态限流实战
前言 在从0到1构建分布式秒杀系统和打造十万博文系统中,限流是不可缺少的一个环节,在系统能承受的范围内既能减少资源开销又能防御恶意攻击. 在前面的文章中,我们使用了开源工具包 Guava 提供的限流工 ...
- SpringCloud(六)之 网关概念、Zuul项目搭建-(利用Zuul 实现鉴权和限流实战)
一.网关概念 1.什么是路由网关 网关是系统的唯一对外的入口,介于客户端和服务器端之间的中间层,处理非业务功能 提供路由请求.鉴权.监控.缓存.限流等功能.它将"1对N"问题转换成 ...
- SpringBoot进阶教程(六十八)Sentinel实现限流降级
前面两篇文章nginx限流配置和SpringBoot进阶教程(六十七)RateLimiter限流,我们介绍了如何使用nginx和RateLimiter限流,这篇文章介绍另外一种限流方式---Senti ...
- springBoot整合Sentinel实现降级限流熔断
由于hystrix的停止更新,以及阿里Sentinel在历年双十一的贡献.项目中使用了Sentinel,今天我们来讲讲Sentinel的入门教程,本文使用1.6.3版本进行讲解 本文通过Sentine ...
- WCF 项目应用连载[8] - 绑定、服务、行为 大数据传输与限流 - 下 (ServiceThrottlingAttribute)
因为ORM的原因,对Attribute编程有一种情节..所以这节的出现,完全是因为在WCF对自定义Attribute的一种应用. WCF 项目应用连载[7] - 绑定.服务.行为 大数据传输与限流 - ...
随机推荐
- kotlin异常类
kotlin中所有的错误异常类都是throwable的自雷,没各一次都能带有一个错误消息,调用堆栈,以及可选的错误原因,要抛出异常,可以使用throw表达式 throw myException(&qu ...
- Qt编写自定义控件33-图片切换动画
一.前言 在很多看图软件中,切换图片的时候可以带上动画过渡或者切换效果,显得更人性化,其实主要还是炫一些,比如百叶窗.透明度变化.左下角飞入等,无论多少种效果,核心都是围绕QPainter来进行,将各 ...
- windows 下OPENSSL 生成秘钥和公钥的方法
1. 生成原始 RSA私钥文件 private_key.pem openssl genrsa -out private_key.pem 1024 2. 将原始 RSA私钥转换为 pkcs8格式 ope ...
- es6 map()和filter()详解【转】
原文地址:http://www.zhangxinxu.com/wordpress/2013/04/es5%e6%96%b0%e5%a2%9e%e6%95%b0%e7%bb%84%e6%96%b9%e6 ...
- save()和savaorupdate的区别
hibernate的保存 hibernate对于对象的保存提供了太多的方法,他们之间有很多不同,在这里细说一下,以便区别: 一.预备知识: 在所有之前,说明一下,对于hibernate,它的对 ...
- Go语言中defer语句使用小结
defer是Go语言中的延迟执行语句,用来添加函数结束时执行的代码,常用于释放某些已分配的资源.关闭数据库连接.断开socket连接.解锁一个加锁的资源.Go语言机制担保一定会执行defer语句中的代 ...
- prometheus 监控 redis + rabbitmq
1.安装部署 1.1 wget https://github.com/oliver006/redis_exporter/releases/download/v0.15.0/redis_exporter ...
- js函数(5)
函数属性方法和构造函数 length属性 函数体中,arguments.length表示传入函数的实参个数. prototype属性 指向一个对象的引用 call方法和apply方法 看作是某个对象的 ...
- poj1410(判断线段和矩形是否相交)
题目链接:https://vjudge.net/problem/POJ-1410 题意:判断线段和矩形是否相交. 思路:注意这里的相交包括线段在矩形内,因此先判断线段与矩形的边是否相交,再判断线段的两 ...
- kafka 名词解释及原理解析过程(三)
为什么要了解这些名词的解释呢?因为在学一个新的知识或者领域的时候,我们需要知道它所定义的概念和名词意思,因为只有这样我们才能理解和掌握这个新的知识点,才能更加系统的掌握这个技术. 一.名词解释 1.b ...