redis+lua實(shí)現(xiàn)限流的項(xiàng)目實(shí)踐
1、需要引入Redis的maven坐標(biāo)
<!--redis和 springboot集成的包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.3.0.RELEASE</version> </dependency>
2、redis配置
spring: # Redis數(shù)據(jù)庫(kù)索引 redis: database: 0 # Redis服務(wù)器地址 host: 127.0.0.1 # Redis服務(wù)器連接端口 port: 6379 # Redis服務(wù)器連接密碼(默認(rèn)為空) password: # 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制) jedis: pool: max-active: 8 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制) max-wait: -1 # 連接池中的最大空閑連接 max-idle: 8 # 連接池中的最小空閑連接 min-idle: 0 # 連接超時(shí)時(shí)間(毫秒) timeout: 10000
3、新建腳本放在該項(xiàng)目的 resources 目錄下,新建 limit.lua
local key = KEYS[1] --限流KEY local limit = tonumber(ARGV[1]) --限流大小 local current = tonumber(redis.call('get', key) or "0") if current + 1 > limit then return 0 else redis.call("INCRBY", key,"1") redis.call("expire", key,"2") return current + 1 end
4、自定義限流注解
import java.lang.annotation.*; @Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RedisRateLimiter { //往令牌桶放入令牌的速率 double value() default Double.MAX_VALUE; //獲取令牌的超時(shí)時(shí)間 double limit() default Double.MAX_VALUE; }
5、自定義切面類(lèi) RedisLimiterAspect 類(lèi) ,修改掃描自己controller類(lèi)
import com.imooc.annotation.RedisRateLimiter; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.assertj.core.util.Lists; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.scripting.support.ResourceScriptSource; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.List; @Aspect @Component public class RedisLimiterAspect { @Autowired private HttpServletResponse response; /** * 注入redis操作類(lèi) */ @Autowired private StringRedisTemplate stringRedisTemplate; private DefaultRedisScript<List> redisScript; /** * 初始化 redisScript 類(lèi) * 返回值為 List */ @PostConstruct public void init(){ redisScript = new DefaultRedisScript<List>(); redisScript.setResultType(List.class); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua"))); } public final static Logger log = LoggerFactory.getLogger(RedisLimiterAspect.class); @Pointcut("execution( public * com.zz.controller.*.*(..))") public void pointcut(){ } @Around("pointcut()") public Object process(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { MethodSignature signature = (MethodSignature)proceedingJoinPoint.getSignature(); //使用Java 反射技術(shù)獲取方法上是否有@RedisRateLimiter 注解類(lèi) RedisRateLimiter redisRateLimiter = signature.getMethod().getDeclaredAnnotation(RedisRateLimiter.class); if(redisRateLimiter == null){ //正常執(zhí)行方法,執(zhí)行正常業(yè)務(wù)邏輯 return proceedingJoinPoint.proceed(); } //獲取注解上的參數(shù),獲取配置的速率 double value = redisRateLimiter.value(); double time = redisRateLimiter.limit(); //list設(shè)置lua的keys[1] //取當(dāng)前時(shí)間戳到單位秒 String key = "ip:"+ System.currentTimeMillis() / 1000; List<String> keyList = Lists.newArrayList(key); //用戶Mpa設(shè)置Lua 的ARGV[1] //List<String> argList = Lists.newArrayList(String.valueOf(value)); //調(diào)用腳本并執(zhí)行 List result = stringRedisTemplate.execute(redisScript, keyList, String.valueOf(value),String.valueOf(time)); log.info("限流時(shí)間段內(nèi)訪問(wèn)第:{} 次", result.toString()); //lua 腳本返回 "0" 表示超出流量大小,返回1表示沒(méi)有超出流量大小 if(StringUtils.equals(result.get(0).toString(),"0")){ //服務(wù)降級(jí) fullback(); return null; } // 沒(méi)有限流,直接放行 return proceedingJoinPoint.proceed(); } /** * 服務(wù)降級(jí)方法 */ private void fullback(){ response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); PrintWriter writer = null; try { writer= response.getWriter(); JSONObject o = new JSONObject(); o.put("status",500); o.put("msg","Redis限流:請(qǐng)求太頻繁,請(qǐng)稍后重試!"); o.put("data",null); writer.printf(o.toString() ); }catch (Exception e){ e.printStackTrace(); }finally { if(writer != null){ writer.close(); } } } }
6、在需要限流的類(lèi)添加注解
import com.imooc.annotation.RedisRateLimiter; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.concurrent.TimeUnit; @RestController @Api(value = "限流", tags = {"限流測(cè)試接口"}) @RequestMapping("limiter") public class LimiterController { @ApiOperation(value = "Redis限流注解測(cè)試接口",notes = "Redis限流注解測(cè)試接口", httpMethod = "GET") @RedisRateLimiter(value = 10, limit = 1) @GetMapping("/redislimit") public IMOOCJSONResult redislimit(){ System.out.println("Redis限流注解測(cè)試接口"); return IMOOCJSONResult.ok(); } }
到此這篇關(guān)于redis+lua實(shí)現(xiàn)限流的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)redis lua限流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis實(shí)現(xiàn)主從復(fù)制方式(Master&Slave)
這篇文章主要介紹了Redis實(shí)現(xiàn)主從復(fù)制方式(Master&Slave),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06Redis做預(yù)定庫(kù)存緩存功能設(shè)計(jì)使用
這篇文章主要為大家介紹了Redis做預(yù)定庫(kù)存緩存功能設(shè)計(jì)使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04spring?boot集成redis基礎(chǔ)入門(mén)實(shí)例詳解
redis在spring?boot項(xiàng)目開(kāi)發(fā)中是常用的緩存套件,常見(jiàn)使用的是spring-boot-starter-data-redis,這篇文章主要介紹了spring?boot集成redis基礎(chǔ)入門(mén),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10淺談Redis?中的過(guò)期刪除策略和內(nèi)存淘汰機(jī)制
本文主要介紹了Redis?中的過(guò)期刪除策略和內(nèi)存淘汰機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04Redis簡(jiǎn)單動(dòng)態(tài)字符串SDS的實(shí)現(xiàn)示例
Redis沒(méi)有直接復(fù)用C語(yǔ)言的字符串,而是新建了SDS,本文主要介紹了Redis簡(jiǎn)單動(dòng)態(tài)字符串SDS的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-08-08Redis中Bloom filter布隆過(guò)濾器的學(xué)習(xí)
布隆過(guò)濾器是一個(gè)非常長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)哈希函數(shù)的組合,可用于檢索一個(gè)元素是否存在,本文就詳細(xì)的介紹一下Bloom filter布隆過(guò)濾器,具有一定的參考價(jià)值,感興趣的可以了解一下2022-12-12