Springboot+Redis實(shí)現(xiàn)API接口防刷限流的項(xiàng)目實(shí)踐
前言
在開發(fā)分布式高并發(fā)系統(tǒng)時有三把利器用來保護(hù)系統(tǒng):緩存、降級、限流。
緩存:緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)處理容量
降級:降級是當(dāng)服務(wù)出現(xiàn)問題或者影響到核心流程時,需要暫時屏蔽掉,待高峰或者問題解決后再打開
限流:限流的目的是通過對并發(fā)訪問/請求進(jìn)行限速,或者對一個時間窗口內(nèi)的請求進(jìn)行限速來保護(hù)系統(tǒng),一旦達(dá)到限制速率則可以拒絕服務(wù)、排隊(duì)或等待、降級等處理
本文主要講的是api接口限流相關(guān)內(nèi)容,雖然不是論述高并發(fā)概念中的限流, 不過道理都差不多。通過限流可以讓系統(tǒng)維持在一個相對穩(wěn)定的狀態(tài),為更多的客戶提供服務(wù)。
api接口的限流主要應(yīng)用場景有:
- 電商系統(tǒng)(特別是6.18、雙11等)中的秒殺活動,使用限流防止使用軟件惡意刷 單;
- 各種基礎(chǔ)api接口限流:例如天氣信息獲取,IP對應(yīng)城市接口,百度、騰訊等對外提供的基礎(chǔ)接口,都是通過限流來實(shí)現(xiàn)免費(fèi)與付費(fèi)直接的轉(zhuǎn)換。
- 被各種系統(tǒng)廣泛調(diào)用的api接口,嚴(yán)重消耗網(wǎng)絡(luò)、內(nèi)存等資源,需要合理限流。
api限流實(shí)戰(zhàn)
一、SpringBoot中集成Redis
SpringBoot中集成Redis相對比較簡單,步驟如下:
1.1 引入Redis依賴
<!--springboot redis依賴--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
1.2 在application.yml中配置Redis
spring: redis: database: 3 # Redis數(shù)據(jù)庫索引(默認(rèn)為0) host: 127.0.0.1 # Redis服務(wù)器地址 port: 6379 # Redis服務(wù)器連接端口 password: 123456 # Redis服務(wù)器連接密碼(默認(rèn)為空) timeout: 2000 # 連接超時時間(毫秒) jedis: pool: max-active: 200 # 連接池最大連接數(shù)(使用負(fù)值表示沒有限制) max-idle: 20 # 連接池中的最大空閑連接 min-idle: 0 # 連接池中的最小空閑連接 max-wait: -1 # 連接池最大阻塞等待時間(使用負(fù)值表示沒有限制)
1.3 配置RedisTemplate
/** * @Description: redis配置類 * @Author oyc * @Date 2020/4/22 11:50 下午 */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { /** * RedisTemplate相關(guān)配置 * 使redis支持插入對象 * * @param factory * @return 方法緩存 Methods the cache */ @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); // 配置連接工廠 template.setConnectionFactory(factory); // 設(shè)置key的序列化器 template.setKeySerializer(new StringRedisSerializer()); // 設(shè)置value的序列化器 //使用Jackson 2,將對象序列化為JSON Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //json轉(zhuǎn)對象類,不設(shè)置默認(rèn)的會將json轉(zhuǎn)成hashmap ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } }
以上,已經(jīng)完成Redis的集成,后續(xù)使用可以直接注入RedisTemplate(具體可參考另一篇博客:https://blog.csdn.net/u014553029/article/details/106087846),如下所示:
@Autowired private RedisTemplate<String, Object> redisTemplate;
二、實(shí)現(xiàn)限流
2.1 添加自定義AccessLimit注解
使用注解方式實(shí)現(xiàn)接口的限流操作,方便而優(yōu)雅。
/** * @Description: * @Author oyc * @Date 2020/10/22 11:16 下午 */ @Inherited @Documented @Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface AccessLimit { /** * 指定second 時間內(nèi) API請求次數(shù) */ int maxCount() default 5; /** * 請求次數(shù)的指定時間范圍 秒數(shù)(redis數(shù)據(jù)過期時間) */ int second() default 60; }
2.2 編寫攔截器
限流的思路
- 通過路徑:ip的作為key,訪問次數(shù)為value的方式對某一用戶的某一請求進(jìn)行唯一標(biāo)識
- 每次訪問的時候判斷
key
是否存在,是否count
超過了限制的訪問次數(shù) - 若訪問超出限制,則應(yīng)
response
返回msg:請求過于頻繁
給前端予以展示
/** * @Description: 訪問攔截器 * @Author oyc * @Date 2020/10/22 11:20 下午 */ @Component public class AccessLimitInterceptor implements HandlerInterceptor { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { try {// Handler 是否為 HandlerMethod 實(shí)例 if (handler instanceof HandlerMethod) { // 強(qiáng)轉(zhuǎn) HandlerMethod handlerMethod = (HandlerMethod) handler; // 獲取方法 Method method = handlerMethod.getMethod(); // 是否有AccessLimit注解 if (!method.isAnnotationPresent(AccessLimit.class)) { return true; } // 獲取注解內(nèi)容信息 AccessLimit accessLimit = method.getAnnotation(AccessLimit.class); if (accessLimit == null) { return true; } int seconds = accessLimit.second(); int maxCount = accessLimit.maxCount(); // 存儲key String key = request.getRemoteAddr() + ":" + request.getContextPath() + ":" + request.getServletPath(); // 已經(jīng)訪問的次數(shù) Integer count = (Integer) redisTemplate.opsForValue().get(key); System.out.println("已經(jīng)訪問的次數(shù):" + count); if (null == count || -1 == count) { redisTemplate.opsForValue().set(key, 1, seconds, TimeUnit.SECONDS); return true; } if (count < maxCount) { redisTemplate.opsForValue().increment(key); return true; } if (count >= maxCount) { logger.warn("請求過于頻繁請稍后再試"); return false; } } return true; } catch (Exception e) { logger.warn("請求過于頻繁請稍后再試"); e.printStackTrace(); } return true; } }
2.3 注冊攔截器并配置攔截路徑和不攔截路徑
/** * @Description: 訪問攔截器配置 * @Author oyc * @Date 2020/10/22 11:34 下午 */ @Configuration public class IntercepterConfig implements WebMvcConfigurer { @Autowired private AccessLimitInterceptor accessLimitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(accessLimitInterceptor) .addPathPatterns("/**").excludePathPatterns("/static/**","/login.html","/user/login"); } }
2.4 使用AccessLimit
/** * @Description: * @Author oyc * @Date 2020/10/22 11:36 下午 */ @RestController @RequestMapping("access") public class AccessLimitController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 限流測試 */ @GetMapping @AccessLimit(maxCount = 3,second = 60) public String limit(HttpServletRequest request) { logger.error("Access Limit Test"); return "限流測試"; } }
2.4 測試
源碼傳送門:https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-validated
到此這篇關(guān)于Springboot+Redis實(shí)現(xiàn)API接口防刷限流的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)Springboot Redis 接口防刷限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot整合Redis并且用Redis實(shí)現(xiàn)限流的方法 附Redis解壓包
- 基于SpringBoot+Redis實(shí)現(xiàn)一個簡單的限流器
- SpringBoot使用Redis對用戶IP進(jìn)行接口限流的項(xiàng)目實(shí)踐
- SpringBoot使用Redis對用戶IP進(jìn)行接口限流的示例詳解
- SpringBoot Redis用注釋實(shí)現(xiàn)接口限流詳解
- 使用SpringBoot?+?Redis?實(shí)現(xiàn)接口限流的方式
- SpringBoot中使用Redis對接口進(jìn)行限流的實(shí)現(xiàn)
- springboot+redis 實(shí)現(xiàn)分布式限流令牌桶的示例代碼
- SpringBoot整合redis實(shí)現(xiàn)計(jì)數(shù)器限流的示例
相關(guān)文章
Spring?Boot整合持久層之JdbcTemplate多數(shù)據(jù)源
持久層是JavaEE中訪問數(shù)據(jù)庫的核心操作,SpringBoot中對常見的持久層框架都提供了自動化配置,例如JdbcTemplate、JPA 等,MyBatis 的自動化配置則是MyBatis官方提供的。接下來分別向讀者介紹Spring Boot整合這持久層技術(shù)中的整合JdbcTemplate2022-08-08Java HttpURLConnection超時和IO異常處理
這篇文章主要介紹了Java HttpURLConnection超時和IO異常處理的相關(guān)資料,需要的朋友可以參考下2016-09-09Fluent Mybatis實(shí)現(xiàn)環(huán)境隔離和租戶隔離
我們在實(shí)際的業(yè)務(wù)開發(fā)中,經(jīng)常會碰到環(huán)境邏輯隔離和租戶數(shù)據(jù)邏輯隔離的問題。本文就詳細(xì)的來介紹一下,感興趣的小伙伴們可以參考一下2021-08-08springboot2 生產(chǎn)部署注意事項(xiàng)及示例代碼
這篇文章主要介紹了springboot2 生產(chǎn)部署注意事項(xiàng)及示例代碼,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04鄰接表無向圖的Java語言實(shí)現(xiàn)完整源碼
這篇文章主要介紹了鄰接表無向圖的Java語言實(shí)現(xiàn)完整源碼,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12Spring Boot2中如何優(yōu)雅地個性化定制Jackson實(shí)現(xiàn)示例
這篇文章主要為大家介紹了Spring Boot2中如何優(yōu)雅地個性化定制Jackson實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05