优雅解决分布式限流
Posted lywj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了优雅解决分布式限流相关的知识,希望对你有一定的参考价值。
SpringBoot
是为了简化 Spring
应用的创建、运行、调试、部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程
在前面的两篇文章中,介绍了一些限流的类型和策略,本篇从 Spring Boot
、Redis
应用层面来实现分布式的限流….
分布式限流
单机版中我们了解到 AtomicInteger
、RateLimiter
、Semaphore
这几种解决方案,但它们也仅仅是单机的解决手段,在集群环境下就透心凉了,后面又讲述了 nginx
的限流手段,可它又属于网关层面的策略之一,并不能解决所有问题。例如供短信接口,你无法保证消费方是否会做好限流控制,所以自己在应用层实现限流还是很有必要的。
本章目标
利用 自定义注解
、Spring Aop
、Redis Cache
实现分布式限流….
具体代码
很简单…
导入依赖
在 pom.xml
中添加上 starter-web
、starter-aop
、starter-data-redis
的依赖即可,习惯了使用 commons-lang3
和 guava
中的一些工具包…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
<dependencies>
|
属性配置
在 application.properites
资源文件中添加 redis
相关的配置项
1 2 3
|
spring.redis.host=localhost spring.redis.port=6379 spring.redis.password=battcn
|
Limit 注解
创建一个 Limit
注解,不多说注释都给各位写齐全了….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
package com.battcn.limiter.annotation;
import com.battcn.limiter.LimitType;
import java.lang.annotation.*;
|
RedisTemplate
默认情况下 spring-boot-data-redis
为我们提供了StringRedisTemplate
但是满足不了其它类型的转换,所以还是得自己去定义其它类型的模板….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
package com.battcn.limiter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
|
Limit 拦截器(AOP)
熟悉 Redis
的朋友都知道它是线程安全的,我们利用它的特性可以实现分布式锁、分布式限流等组件,在一起来学SpringBoot | 第二十三篇:轻松搞定重复提交(分布式锁)中讲述了分布式锁的实现,限流相比它稍微复杂一点,官方虽然没有提供相应的API,但却提供了支持 Lua 脚本的功能,我们可以通过编写 Lua 脚本实现自己的API,同时他是满足原子性的….
下面核心就是调用 execute
方法传入我们的 Lua 脚本内容,然后通过返回值判断是否超出我们预期的范围,超出则给出错误提示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
package com.battcn.limiter;
import com.battcn.limiter.annotation.Limit; import com.google.common.collect.ImmutableList; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest; import java.io.Serializable; import java.lang.reflect.Method;
|
控制层
在接口上添加 @Limit()
注解,如下代码会在 Redis 中生成过期时间为 100s 的 key = test 的记录,特意定义了一个 AtomicInteger
用作测试…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
package com.battcn.controller;
import com.battcn.limiter.annotation.Limit; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicInteger;
|
主函数
就一个普通的不能在普通的主函数类了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
package com.battcn;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
|
测试
完成准备事项后,启动 Chapter27Application
自行测试即可,测试手段相信大伙都不陌生了,如 浏览器
、postman
、junit
、swagger
,此处基于 postman
,如果你觉得自带的异常信息不够友好,那么配上一起来学SpringBoot | 第十八篇:轻松搞定全局异常 可以轻松搞定…
未达设定的阀值时
正确响应
达到设置的阀值时
错误响应
以上是关于优雅解决分布式限流的主要内容,如果未能解决你的问题,请参考以下文章
如何利用redis来进行分布式集群系统的限流设计
SpringBoot - 优雅的实现流控
SpringBoot中如何编写一个优雅的限流组件?
新来个技术总监,把限流实现的那叫一个优雅,佩服!
分布式解决方案之:限流
秒杀系统如何优雅稳定地处理大量请求?