Springboot+Redis實現(xiàn)API接口防刷限流的項目實踐
前言
在開發(fā)分布式高并發(fā)系統(tǒng)時有三把利器用來保護系統(tǒng):緩存、降級、限流。
緩存:緩存的目的是提升系統(tǒng)訪問速度和增大系統(tǒng)處理容量
降級:降級是當(dāng)服務(wù)出現(xiàn)問題或者影響到核心流程時,需要暫時屏蔽掉,待高峰或者問題解決后再打開
限流:限流的目的是通過對并發(fā)訪問/請求進行限速,或者對一個時間窗口內(nèi)的請求進行限速來保護系統(tǒng),一旦達到限制速率則可以拒絕服務(wù)、排隊或等待、降級等處理
本文主要講的是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ǔ)接口,都是通過限流來實現(xiàn)免費與付費直接的轉(zhuǎn)換。
- 被各種系統(tǒng)廣泛調(diào)用的api接口,嚴(yán)重消耗網(wǎng)絡(luò)、內(nèi)存等資源,需要合理限流。
api限流實戰(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ù)(使用負值表示沒有限制)
max-idle: 20 # 連接池中的最大空閑連接
min-idle: 0 # 連接池中的最小空閑連接
max-wait: -1 # 連接池最大阻塞等待時間(使用負值表示沒有限制)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;
二、實現(xiàn)限流
2.1 添加自定義AccessLimit注解
使用注解方式實現(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的方式對某一用戶的某一請求進行唯一標(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 實例
if (handler instanceof HandlerMethod) {
// 強轉(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實現(xiàn)API接口防刷限流的項目實踐的文章就介紹到這了,更多相關(guān)Springboot Redis 接口防刷限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot整合Redis并且用Redis實現(xiàn)限流的方法 附Redis解壓包
- 基于SpringBoot+Redis實現(xiàn)一個簡單的限流器
- SpringBoot使用Redis對用戶IP進行接口限流的項目實踐
- SpringBoot使用Redis對用戶IP進行接口限流的示例詳解
- SpringBoot Redis用注釋實現(xiàn)接口限流詳解
- 使用SpringBoot?+?Redis?實現(xiàn)接口限流的方式
- SpringBoot中使用Redis對接口進行限流的實現(xiàn)
- springboot+redis 實現(xiàn)分布式限流令牌桶的示例代碼
- SpringBoot整合redis實現(xiàn)計數(shù)器限流的示例
相關(guān)文章
Spring?Boot整合持久層之JdbcTemplate多數(shù)據(jù)源
持久層是JavaEE中訪問數(shù)據(jù)庫的核心操作,SpringBoot中對常見的持久層框架都提供了自動化配置,例如JdbcTemplate、JPA 等,MyBatis 的自動化配置則是MyBatis官方提供的。接下來分別向讀者介紹Spring Boot整合這持久層技術(shù)中的整合JdbcTemplate2022-08-08
Java HttpURLConnection超時和IO異常處理
這篇文章主要介紹了Java HttpURLConnection超時和IO異常處理的相關(guān)資料,需要的朋友可以參考下2016-09-09
Fluent Mybatis實現(xiàn)環(huán)境隔離和租戶隔離
我們在實際的業(yè)務(wù)開發(fā)中,經(jīng)常會碰到環(huán)境邏輯隔離和租戶數(shù)據(jù)邏輯隔離的問題。本文就詳細的來介紹一下,感興趣的小伙伴們可以參考一下2021-08-08
springboot2 生產(chǎn)部署注意事項及示例代碼
這篇文章主要介紹了springboot2 生產(chǎn)部署注意事項及示例代碼,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04
Spring Boot2中如何優(yōu)雅地個性化定制Jackson實現(xiàn)示例
這篇文章主要為大家介紹了Spring Boot2中如何優(yōu)雅地個性化定制Jackson實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05

