Springboot使用redis實(shí)現(xiàn)接口Api限流的實(shí)例

該篇介紹的內(nèi)容如題,就是利用redis實(shí)現(xiàn)接口的限流( 某時(shí)間范圍內(nèi) 最大的訪問(wèn)次數(shù) ) 。
正文慣例,先看下我們的實(shí)戰(zhàn)目錄結(jié)構(gòu):

首先是pom.xml 核心依賴:
<!--用于redis數(shù)據(jù)庫(kù)連接--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--用于redis lettuce 連接池pool使用--><dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>
然后是application.yml里面的redis接入配置:
spring: redis: lettuce: pool:#連接池最大連接數(shù) 使用負(fù)值代表無(wú)限制 默認(rèn)為8max-active: 10#最大空閑連接 默認(rèn)8max-idle: 10#最小空閑連接 默認(rèn)0min-idle: 1 host: 127.0.0.1 password: 123456 port: 6379 database: 0 timeout: 2000msserver: port: 8710
redis的配置類, RedisConfig.java:
ps:可以看到日期是18年的,因?yàn)檫@些redis的整合教程,在這個(gè)系列里面一共有快10篇,不了解的看客如果感興趣可以去看一看。
import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheConfiguration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.RedisSerializationContext;import org.springframework.data.redis.serializer.StringRedisSerializer; import static org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig; /** * @Author: JCccc * @CreateTime: 2018-09-11 * @Description: */@Configuration@EnableCachingpublic class RedisConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {RedisCacheConfiguration cacheConfiguration =defaultCacheConfig().disableCachingNullValues().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer(Object.class)));return RedisCacheManager.builder(connectionFactory).cacheDefaults(cacheConfiguration).build(); } @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);//序列化設(shè)置 ,這樣為了存儲(chǔ)操作對(duì)象時(shí)正常顯示的數(shù)據(jù),也能正常存儲(chǔ)和獲取redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);return redisTemplate; } @Bean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();stringRedisTemplate.setConnectionFactory(factory);return stringRedisTemplate; }}
自定義注解:
import java.lang.annotation.*; /** * @Author JCccc * @Description * @Date 2021/7/23 11:46 */@Inherited@Documented@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface RequestLimit { /** * 時(shí)間內(nèi) 秒為單位 */ int second() default 10; /** * 允許訪問(wèn)次數(shù) */ int maxCount() default 5; //默認(rèn)效果 : 10秒內(nèi) 對(duì)于使用該注解的接口,只能總請(qǐng)求訪問(wèn)數(shù) 不能大于 5次 }
接下來(lái)是攔截器 RequestLimitInterceptor.java:
攔截接口的方式 是通過(guò) ip地址+接口url ,做時(shí)間內(nèi)的訪問(wèn)計(jì)數(shù)
import com.elegant.testdemo.annotation.RequestLimit;import com.elegant.testdemo.utils.IpUtil;import com.fasterxml.jackson.databind.ObjectMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;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 javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.concurrent.TimeUnit; /** * @Author JCccc * @Description * @Date 2021/7/23 11:49 */ @Componentpublic class RequestLimitInterceptor implements HandlerInterceptor { private final Logger log = LoggerFactory.getLogger(this.getClass()); @Autowired private RedisTemplate<String, Object> redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {try { if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;// 獲取RequestLimit注解RequestLimit requestLimit = handlerMethod.getMethodAnnotation(RequestLimit.class);if (null==requestLimit) { return true;}//限制的時(shí)間范圍int seconds = requestLimit.second();//時(shí)間內(nèi)的 最大次數(shù)int maxCount = requestLimit.maxCount();String ipAddr = IpUtil.getIpAddr(request);// 存儲(chǔ)keyString key = ipAddr+':'+request.getContextPath() + ':' + request.getServletPath();// 已經(jīng)訪問(wèn)的次數(shù)Integer count = (Integer) redisTemplate.opsForValue().get(key);log.info('檢測(cè)到目前ip對(duì)接口={}已經(jīng)訪問(wèn)的次數(shù)', request.getServletPath() , 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;}log.warn('請(qǐng)求過(guò)于頻繁請(qǐng)稍后再試');returnData(response);return false; } return true;} catch (Exception e) { log.warn('請(qǐng)求過(guò)于頻繁請(qǐng)稍后再試'); e.printStackTrace();}return true; } public void returnData(HttpServletResponse response) throws IOException {response.setCharacterEncoding('UTF-8');response.setContentType('application/json; charset=utf-8');ObjectMapper objectMapper = new ObjectMapper();//這里傳提示語(yǔ)可以改成自己項(xiàng)目的返回?cái)?shù)據(jù)封裝的類response.getWriter().println(objectMapper.writeValueAsString('請(qǐng)求過(guò)于頻繁請(qǐng)稍后再試'));return; } }
接下來(lái)是 攔截器的配置 WebConfig.java:
import com.elegant.testdemo.interceptor.RequestLimitInterceptor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @Author JCccc * @Description * @Date 2021/7/23 11:52 */ @Configurationpublic class WebConfig implements WebMvcConfigurer { @Autowired private RequestLimitInterceptor requestLimitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(requestLimitInterceptor) //攔截所有請(qǐng)求路徑.addPathPatterns('/**')//再設(shè)置 放開(kāi)哪些路徑.excludePathPatterns('/static/**','/auth/login'); } }
最后還有兩個(gè)工具類
IpUtil:
https://www.jb51.net/article/218249.htm
RedisUtil :
https://www.jb51.net/article/218246.htm
最后寫個(gè)測(cè)試接口
TestController.java
import com.elegant.testdemo.annotation.RequestLimit;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController; /** * @Author JCccc * @Description * @Date 2021/7/23 11:55 */@RestControllerpublic class TestController { @GetMapping('/test') @RequestLimit(maxCount = 3,second = 60) public String test() {return '你好,如果對(duì)你有幫助,請(qǐng)點(diǎn)贊加關(guān)注。'; } }
這個(gè)/test接口的注解,我們?cè)O(shè)置的是 60秒內(nèi) 最大訪問(wèn)次數(shù)為 3次 (實(shí)際應(yīng)用應(yīng)該是根據(jù)具體接口做相關(guān)的次數(shù)限制。)
然后使用postman測(cè)試一下接口:
前面三次都是請(qǐng)求通過(guò)的:

第四次:

到此這篇關(guān)于Springboot使用redis實(shí)現(xiàn)接口Api限流的實(shí)例的文章就介紹到這了,更多相關(guān)Springboot redis接口Api限流內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. asp文件用什么軟件編輯2. PHP基礎(chǔ)之生成器4——比較生成器和迭代器對(duì)象3. CentOS郵箱服務(wù)器搭建系列——SMTP服務(wù)器的構(gòu)建( Postfix )4. Docker 啟動(dòng)Redis 并設(shè)置密碼的操作5. ASP新手必備的基礎(chǔ)知識(shí)6. Vue axios獲取token臨時(shí)令牌封裝案例7. vue+element開(kāi)發(fā)一個(gè)谷歌插件的全過(guò)程8. Spring如何替換掉默認(rèn)common-logging.jar9. 利用CSS制作3D動(dòng)畫10. JAVA 實(shí)現(xiàn)延遲隊(duì)列的方法

網(wǎng)公網(wǎng)安備