close
關注我,回復關鍵字「spring」,
免費領取Spring學習資料。

前言

在開發分布式高並發系統時有三把利器用來保護系統:緩存、降級、限流。

緩存

緩存的目的是提升系統訪問速度和增大系統處理容量

降級

降級是當服務出現問題或者影響到核心流程時,需要暫時屏蔽掉,待高峰或者問題解決後再打開

限流

限流的目的是通過對並發訪問/請求進行限速,或者對一個時間窗口內的請求進行限速來保護系統,一旦達到限制速率則可以拒絕服務、排隊或等待、降級等處理

本文主要講的是api接口限流相關內容,雖然不是論述高並發概念中的限流, 不過道理都差不多。通過限流可以讓系統維持在一個相對穩定的狀態,為更多的客戶提供服務。

api接口的限流主要應用場景有:

電商系統(特別是6.18、雙11等)中的秒殺活動,使用限流防止使用軟件惡意刷單;
各種基礎api接口限流:例如天氣信息獲取,IP對應城市接口,百度、騰訊等對外提供的基礎接口,都是通過限流來實現免費與付費直接的轉換。
被各種系統廣泛調用的api接口,嚴重消耗網絡、內存等資源,需要合理限流。
api限流實戰一、SpringBoot中集成Redis

SpringBoot中集成Redis相對比較簡單,步驟如下:

1.1 引入Redis依賴

<!--springbootredis依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

1.2 在application.yml中配置Redis

spring:redis:database:3#Redis數據庫索引(默認為0)host:127.0.0.1#Redis服務器地址port:6379#Redis服務器連接端口password:123456#Redis服務器連接密碼(默認為空)timeout:2000#連接超時時間(毫秒)jedis:pool:max-active:200#連接池最大連接數(使用負值表示沒有限制)max-idle:20#連接池中的最大空閒連接min-idle:0#連接池中的最小空閒連接max-wait:-1#連接池最大阻塞等待時間(使用負值表示沒有限制)

1.3 配置RedisTemplate

/***@Description:redis配置類*@Authoroyc*/@Configuration@EnableCachingpublicclassRedisConfigextendsCachingConfigurerSupport{/***RedisTemplate相關配置*使redis支持插入對象**@paramfactory*@return方法緩存Methodsthecache*/@BeanpublicRedisTemplate<String,Object>redisTemplate(RedisConnectionFactoryfactory){RedisTemplate<String,Object>template=newRedisTemplate<>();//配置連接工廠template.setConnectionFactory(factory);//設置key的序列化器template.setKeySerializer(newStringRedisSerializer());//設置value的序列化器//使用Jackson2,將對象序列化為JSONJackson2JsonRedisSerializerjackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class);//json轉對象類,不設置默認的會將json轉成hashmapObjectMapperom=newObjectMapper();om.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setValueSerializer(jackson2JsonRedisSerializer);returntemplate;}}

以上,已經完成Redis的集成,後續使用可以直接注入RedisTemplate,如下所示:

@AutowiredprivateRedisTemplate<String,Object>redisTemplate;二、實現限流

2.1 添加自定義AccessLimit註解

使用註解方式實現接口的限流操作,方便而優雅。

/***@Description:*@Authoroyc*/@Inherited@Documented@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public@interfaceAccessLimit{/***指定second時間內API請求次數*/intmaxCount()default5;/***請求次數的指定時間範圍秒數(redis數據過期時間)*/intsecond()default60;}

2.2 編寫攔截器

限流的思路

通過路徑:ip的作為key,訪問次數為value的方式對某一用戶的某一請求進行唯一標識
每次訪問的時候判斷key是否存在,是否count超過了限制的訪問次數
若訪問超出限制,則應response返回msg:請求過於頻繁給前端予以展示
/***@Description:訪問攔截器*@Authoroyc*/@ComponentpublicclassAccessLimitInterceptorimplementsHandlerInterceptor{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());@AutowiredprivateRedisTemplate<String,Object>redisTemplate;@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{try{//Handler是否為HandlerMethod實例if(handlerinstanceofHandlerMethod){//強轉HandlerMethodhandlerMethod=(HandlerMethod)handler;//獲取方法Methodmethod=handlerMethod.getMethod();//是否有AccessLimit註解if(!method.isAnnotationPresent(AccessLimit.class)){returntrue;}//獲取註解內容信息AccessLimitaccessLimit=method.getAnnotation(AccessLimit.class);if(accessLimit==null){returntrue;}intseconds=accessLimit.second();intmaxCount=accessLimit.maxCount();//存儲keyStringkey=request.getRemoteAddr()+":"+request.getContextPath()+":"+request.getServletPath();//已經訪問的次數Integercount=(Integer)redisTemplate.opsForValue().get(key);System.out.println("已經訪問的次數:"+count);if(null==count||-1==count){redisTemplate.opsForValue().set(key,1,seconds,TimeUnit.SECONDS);returntrue;}if(count<maxCount){redisTemplate.opsForValue().increment(key);returntrue;}if(count>=maxCount){logger.warn("請求過於頻繁請稍後再試");returnfalse;}}returntrue;}catch(Exceptione){logger.warn("請求過於頻繁請稍後再試");e.printStackTrace();}returntrue;}}

2.3 註冊攔截器並配置攔截路徑和不攔截路徑

/***@Description:訪問攔截器配置*@Authoroyc*@Date2020/10/2211:34下午*/@ConfigurationpublicclassIntercepterConfigimplementsWebMvcConfigurer{@AutowiredprivateAccessLimitInterceptoraccessLimitInterceptor;@OverridepublicvoidaddInterceptors(InterceptorRegistryregistry){registry.addInterceptor(accessLimitInterceptor).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html","/user/login");}}

2.4 使用AccessLimit

/***@Description:*@Authoroyc*/@RestController@RequestMapping("access")publicclassAccessLimitController{privatefinalLoggerlogger=LoggerFactory.getLogger(this.getClass());/***限流測試*/@GetMapping@AccessLimit(maxCount=3,second=60)publicStringlimit(HttpServletRequestrequest){logger.error("AccessLimitTest");return"限流測試";}}

2.5 測試

源碼傳送門:

https://github.com/oycyqr/springboot-learning-demo/tree/master/springboot-validated

來源:blog.csdn.net/u014553029/article/details/109232225



END


Spring Boot 關於日期時間格式化處理方式總結
Spring Boot 最流行的 16 條實踐,Java 開發變得更加簡單!
如何自己實現一個簡單的 Spring Bean 容器
為什麼Spring Boot構造的jar包能直接運行?

關注後端面試那些事,回復【2022面經】

獲取最新大廠Java面經


最後重要提示:高質量的技術交流群,限時免費開放,今年抱團最重要。想進群的,關注SpringForAll社區,回復關鍵詞:加群,拉你進群。




點擊這裡領取2022大廠面經
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()