springboot并发量_「聊聊高并发 一」限流篇

「聊聊高并发 一」限流篇

b6ff404e5a29f70f22768070e380f494.png

高并发系统保护的三把利器 : 缓存、降级和限流

66f20e32e2a349ca30ec191de6ee4e00.png
  • 缓存 缓存的目的是提升系统访问速度和增大系统处理容量
  • 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开
  • 限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理

今天呢,咱们主要来聊一聊限流

对于比较关键又高并发的服务,比如商城秒杀,就不能通过缓存和降级方式解决,至少不够解决太大并发量,这时候就需要防刷限流了。

一、限流目的

先有缓存这个银弹,后有限流来应对618、双十一高并发流量,在处理高并发问题上可以说是如虎添翼,不用担心瞬间流量导致系统挂掉或雪崩,最终做到有损服务而不是不服务;限流需要评估好,不可乱用,否则正常流量出现一些奇怪的问题而导致用户抱怨。

限流的目的是通过对并发访问进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货),在压测的时候我们可以找出每个系统的处理峰值,然后通过设定峰值阈值来防止当系统过载的时候,通过拒绝处理过载的请求来保障系统的可用性。

二、限流方式

  • 限制总并发数(比如数据库连接池、线程池)
  • 限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)
  • 限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
  • 限制远程接口调用速率
  • 限制MQ的消费速率。
  • 可以根据网络连接数、网络流量、CPU或内存负载等来限流

三、限流算法

常见的限流算法有:漏桶(Leaky Bucket)算法、令牌桶算法(Token Bucket),计数器也可以进行粗暴限流实现。

  • 漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。
019d560d2ac9410db9b9b56a30b4da6a.png
  • 对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶(Token Bucket)算法更为适合。
dfb46c76c76a54f791d98cdb0c5c9c03.png
  • 令牌桶vs漏桶

令牌桶限制的是平均流入速率,允许突发请求,并允许一定程度突发流量

漏桶限制的是常量流出速率,从而平滑突发流入速率

四、限流实现

ad9954a8e60646e7f2dd3f811a3ae7cc.png

Google Guava框架提供了,Guava RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现,不过相对不够灵活,下面简单介绍下个人自己实现的限流工具:

源码地址

https://github.com/lyplyp/ratelimiter

1.设计初衷

一般系统发展迭代过程都是先以功能开发为主,后期考虑的才是性能,因此如何在原有系统上无代码侵入的情况下实现接口限流,是我本次设计实现限流工具的主要目标。基于目前主流的Java Web开发都在使用Spring全家桶,很容易想到利用SpringAOP的方式来达到想要的效果。

2.实现介绍

利用面向切面编程的思想,通过自定义注解的方式在无代码侵入的情况下实现系统方法级别的限流,目前主要实现了一下几种方式:

  • 基于内存实现计数器限流 CounterRateLimiter
  • 基于Guava SmoothBursty 的封装GuavaRateLimiter
  • 基于Redis实现计数器限流 RedisCounterRateLimiter
  • 基于Redis实现令牌桶限流 RedisTokenBucketRateLimiter
  • 未完待续...

限流的类型主要支持:

全部限制ALL, 基于IP限制, 基于登录用户名限制和用户自定义限制(如:用户组,用户id)

限流接口定义public interface RateLimiter { public void rateLimit(String key, long limit, long refreshInterval,  long tokenBucketTimeInterval, long tokenNumber); public String algorithm();}
开启限流功能注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Import(EnableRateLimiterConfiguration.class)@Documented@Inheritedpublic @interface EnableRateLimiter {}
方法限流注解@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documented@Inheritedpublic @interface MethodRateLimiter { /** * * @return RateLimitType 限流类型。默认值:ALL。可选值:ALL,IP,USER,CUSTOM */ public RateLimitType type() default RateLimitType.ALL; public RateLimitAlgorithm algorithm() default RateLimitAlgorithm.REDIS_COUNTER; /** * * @return 限流次数。默认值10 */ public long limit() default 10; /** * * @return 限流时间间隔,以秒为单位。默认值60 */ public long refreshInterval() default 60; /** * 向令牌桶中添加数据的时间间隔,以秒为单位。默认值10秒 */ public long tokenBucketTimeInterval() default 10; /** * 每次为令牌桶中添加的令牌数量。默认值5个 */ public long tokenNumber() default 5;}
MethodRateLimiter注解切面类@Component@Aspectpublic class MethodRateLimiterAspect { @Pointcut("@annotation(methodRateLimiter)") public void annotationPointcut(MethodRateLimiter methodRateLimiter) { } @Before("@annotation(methodRateLimiter)") public void doBefore(JoinPoint joinPoint, MethodRateLimiter methodRateLimiter) {String key = RateLimiterUtil.getKey(methodRateLimiter.algorithm.getName(),joinPoint,methodRateLimiter.type());RateLimiterFactory.getRateLimiter(methodRateLimiter.algorithm().getName()).rateLimit( key, methodRateLimiter.limit(), methodRateLimiter.refreshInterval(), methodRateLimiter.tokenBucketTimeInterval(),  methodRateLimiter.tokenNumber()); }}

3.使用方式

springboot项目yml配置redis信息

server: port: 8888spring: redis: host: 192.168.4.131 port: 6379 password: WzQ9oWgx timeout: 2000 jedis: pool: max-active: 8 max-wait: 1 max-idle: 8 min-idle: 0
开启限流功能@SpringBootApplication@EnableRateLimiterpublic class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); }}
测试方法限流@RestController@RequestMapping("/test")public class TestController { @RequestMapping("/testMethodRedisCounter") @MethodRateLimiter(refreshInterval = 30, limit = 3, algorithm = RateLimitAlgorithm.REDIS_COUNTER) public String testMethodRedisCounter(){ return "testMethodRedisCounter"; } @RequestMapping("/testMethodCounter") @MethodRateLimiter(refreshInterval = 30, limit = 3, algorithm = RateLimitAlgorithm.COUNTER) public String testMethodCounter(){ return "testMethodCounter"; } @RequestMapping("/testIpMethodCounter") @MethodRateLimiter(type = RateLimitType.IP, refreshInterval = 30, limit = 3, algorithm = RateLimitAlgorithm.COUNTER) public String testIpMethodCounter(){ return " testIpMethodCounter"; } @RequestMapping("/testMethodRedisTokenBucket") @MethodRateLimiter(tokenBucketTimeInterval = 10, tokenNumber = 3, algorithm = RateLimitAlgorithm.REDIS_TOKEN_BUCKET) public String testMethodRedisTokenBucket(){ return "testMethodRedisTokenBucket"; } @RequestMapping("/testMethodGuava") @MethodRateLimiter(algorithm = RateLimitAlgorithm.GUAVA, tokenNumber = 3) public String testMethodGuava(){ return "testMethodGuava"; }}

源码地址

https://github.com/lyplyp/ratelimiter

上述为个人实现和自测仅供参考,有问题欢迎交流,后续也会继续完善!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值