Springboot+Redis實(shí)現(xiàn)API接口限流的示例代碼
添加Redis的jar包.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在application.yml中配置redis
spring:
## Redis
redis:
database: 0
host: 127.0.0.1
port: 6379
password:
jedis:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
timeout: 2000ms
添加自定義注解
@Inherited
@Documented
@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
//指定second 時(shí)間內(nèi) API請(qǐng)求次數(shù)
int times() default 4;
// 請(qǐng)求次數(shù)的指定時(shí)間范圍 秒數(shù)(redis數(shù)據(jù)過期時(shí)間)
int second() default 10;
}
編寫攔截器
import com.ys.xlb.annotation.AccessLimit;
import com.ys.xlb.bean.Code;
import com.ys.xlb.exception.GlobalException;
import com.ys.xlb.utils.IpUtils;
import com.ys.xlb.utils.RequestUtils;
import com.ys.xlb.utils.ResultUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
/**
* @ClassName AccessLimitInterceptor
* @description: API請(qǐng)求限流攔截器
* @time 2019-04-20 11:08
**/
@Slf4j
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {
@Resource
private RedisTemplate<String, Integer> 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 times = accessLimit.times();//請(qǐng)求次數(shù)
int second = accessLimit.second();//請(qǐng)求時(shí)間范圍
//根據(jù) IP + API 限流
String key = IpUtils.getIpAddr(request) + request.getRequestURI();
//根據(jù)key獲取已請(qǐng)求次數(shù)
Integer maxTimes = redisTemplate.opsForValue().get(key);
if(maxTimes == null){
//set時(shí)一定要加過期時(shí)間
redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);
}else if(maxTimes < times){
redisTemplate.opsForValue().set(key, maxTimes+1, second, TimeUnit.SECONDS);
}else{
// 30405 API_REQUEST_TOO_MUCH 請(qǐng)求過于頻繁
RequestUtils.out(response, ResultUtils.error(Code.API_REQUEST_TOO_MUCH));
return false;
}
}
}catch (Exception e){
log.error("API請(qǐng)求限流攔截異常,請(qǐng)檢查Redis是否開啟!",e);
throw new GlobalException(Code.BAD_REQUEST,e.getMessage());
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
方法中的IP工具類方法
/**
* IpUtils工具類方法
* 獲取真實(shí)的ip地址
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if(org.apache.commons.lang.StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
//多次反向代理后會(huì)有多個(gè)ip值,第一個(gè)ip才是真實(shí)ip
int index = ip.indexOf(",");
if(index != -1){
return ip.substring(0,index);
}else{
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if(org.apache.commons.lang.StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){
return ip;
}
return request.getRemoteAddr();
}
RequestUtils.out()方法
/**
* @Title: out
* @Description: response輸出JSON數(shù)據(jù)
* @param response : 響應(yīng)請(qǐng)求
* @param object: object
* @return void
**/
public static void out(ServletResponse response, Object object){
PrintWriter out = null;
try {
response.setContentType("application/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
out = response.getWriter();
out.println(JSONObject.fromObject(resultMap).toString());
} catch (Exception e) {
log.error("輸出JSON報(bào)錯(cuò)!"+e);
}finally{
if(null != out){
out.flush();
out.close();
}
}
}
配置攔截器
@Configuration
public class ApplicationConfig implements WebMvcConfigurer {
//這里需要注入攔截器 否則無法獲取到攔截器注入的RedisTemplate<String, Integer> redisTemplate;
@Bean
public AccessLimitInterceptor accessLimitInterceptor(){
return new AccessLimitInterceptor();
}
/**
* 配置攔截器
* @author lance
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html","/user/login");
//API限流攔截
registry.addInterceptor(accessLimitInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html");
}
}
配置攔截器的類中必須先注入這個(gè)攔截器否則無法獲取到攔截器注入的RedisTemplate<String, Integer> redisTemplate
使用注解
/**
* @Title: selectAll
* @Description: 查詢文章信息
**/
@AccessLimit(times = 5)
@RequestMapping(value = "selectAll" , method = {RequestMethod.GET,RequestMethod.POST})
//GetMapping(value = "selectAll")
public ResultBody selectAll(Article article) {
return articleService.selectAll(article);
}
請(qǐng)求測(cè)試

時(shí)間間隔為默認(rèn)的10s, 10s內(nèi)請(qǐng)求第6次出現(xiàn)此返回值,完成.
參考博客:
https://blog.csdn.net/zrg523/article/details/82185088
到此這篇關(guān)于Springboot+Redis實(shí)現(xiàn)API接口限流的示例代碼的文章就介紹到這了,更多相關(guān)Springboot+Redis接口API限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot+redis 實(shí)現(xiàn)分布式限流令牌桶的示例代碼
- 使用SpringBoot?+?Redis?實(shí)現(xiàn)接口限流的方式
- SpringBoot整合Redis并且用Redis實(shí)現(xiàn)限流的方法 附Redis解壓包
- SpringBoot中使用Redis對(duì)接口進(jìn)行限流的實(shí)現(xiàn)
- 基于SpringBoot+Redis實(shí)現(xiàn)一個(gè)簡(jiǎn)單的限流器
- SpringBoot使用Redis對(duì)用戶IP進(jìn)行接口限流的示例詳解
- Springboot使用redis實(shí)現(xiàn)接口Api限流的實(shí)例
- SpringBoot使用Redis對(duì)用戶IP進(jìn)行接口限流的項(xiàng)目實(shí)踐
- Springboot使用redis實(shí)現(xiàn)接口Api限流的示例代碼
- SpringBoot使用Redis進(jìn)行限流功能實(shí)現(xiàn)
相關(guān)文章
SpringBoot中使用MyBatis-Plus實(shí)現(xiàn)分頁接口的詳細(xì)教程
MyBatis-Plus是一個(gè)MyBatis的增強(qiáng)工具,在MyBatis的基礎(chǔ)上只做增強(qiáng)不做改變,為簡(jiǎn)化開發(fā)、提高效率而生,在SpringBoot項(xiàng)目中使用MyBatis-Plus可以大大簡(jiǎn)化分頁邏輯的編寫,本文將介紹如何在 SpringBoot項(xiàng)目中使用MyBatis-Plus實(shí)現(xiàn)分頁接口2024-03-03
MyBatis中動(dòng)態(tài)SQL語句@Provider的用法
本文主要介紹了MyBatis中動(dòng)態(tài)SQL語句@Provider的用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java實(shí)現(xiàn)隊(duì)列的三種方法集合
這篇文章主要介紹了Java實(shí)現(xiàn)隊(duì)列的三種方法集合,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
Java中的RabbitMQ使用場(chǎng)景和實(shí)踐完全指南
本文涵蓋了RabbitMQ在Java中的主要使用場(chǎng)景和實(shí)踐方法,在實(shí)際應(yīng)用中,還需要根據(jù)具體的業(yè)務(wù)需求和系統(tǒng)架構(gòu)進(jìn)行適當(dāng)?shù)恼{(diào)整和優(yōu)化,本文結(jié)合代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2025-08-08
Invalid?bound?statement?(not?found)出現(xiàn)原因以及解決辦法
這篇文章主要給大家介紹了關(guān)于Invalid?bound?statement?(not?found)出現(xiàn)原因以及解決辦法的相關(guān)資料,文中給出了詳細(xì)的解決方法,需要的朋友可以參考下2023-07-07
Java多線程之synchronized關(guān)鍵字的使用
這篇文章主要介紹了Java多線程之synchronized關(guān)鍵字的使用,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04

