Redis 做接口限流,一个注解的过错
湟源娱乐新闻网 2025-09-14
修改 RedisTemplate 元数据方案,代码如下:
@Configurationpublic class RedisConfig { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(connectionFactory); // 可用Jackson2JsonRedisSerialize 代替绑定元数据(绑定选用的是JDK元数据) 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); redisTemplate.setKeySerializer(jackson2JsonRedisSerializer); redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer); redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); return redisTemplate; }}这个或许也没啥好问道的,key 和 value 我们都可用 Spring Boot 中的绑定的 jackson 元数据方式则来解决。
4. 开发 Lua 脚本语言这个或许我在之前 vhr 那一套片段中的讲过,Redis 中的的一些原子操作方法我们可以来使 Lua 脚本语言来实现,一定会调用 Lua 脚本语言,我们有两种不同的基本概念:
在 Redis 服务端假定好 Lua 脚本语言,然后求出一个散列数值,在 Java 代码中的,通过这个散列数值锁住定要督导哪个 Lua 脚本语言。实际上在 Java 代码中的将 Lua 脚本语言假定好,然后发送到 Redis 服务端去督导。Spring Data Redis 中的也提供了操作方法 Lua 脚本语言的API,还是非常便利的,所以我们这里就选用第二种方案。
我们在 resources 目录下新建 lua 文件夹最初用来存放 lua 脚本语言,脚本语言内容如下:
local key = KEYS[1]local count = tonumber(ARGV[1])local time = tonumber(ARGV[2])local current = redis.call('get', key)if current and tonumber(current)> count then return tonumber(current)endcurrent = redis.call('incr', key)if tonumber(current) == 1 then redis.call('expire', key, time)endreturn tonumber(current)这个脚本语言或许不难,大概瞅一眼就知道干啥用的。KEYS 和 ARGV 都是一可能调用时候所撰起先的表达式,tonumber 就是把codice_串转为大写字母,redis.call 就是督导具体情况的 redis 指令,具体情况流程是这样:
首先来使到所撰起先的 key 以及 封路的 count 和小时 time。通过 get 来使到这个 key 相同的数值,这个数值就是意味著小时窗内这个API可以访问期间多少次。如果是第一次访问期间,此时获得的结果为 nil,否则获得的结果应是一个大写字母,所以每一次就判断,如果获得的结果是一个大写字母,并且这个大写字母还成比例 count,那就问道明已经超过流量受到限制了,那么实际上回到查询的结果才可能。如果获得的结果为 nil,问道明是第一次访问期间,此时就给意味著 key 自增 1,然后设一个到期小时。最后把自增 1 后的数值回到就可以了。或许这段 Lua 脚本语言很差表达出来。
每一次我们在一个 Bean 中的来读取这段 Lua 脚本语言,如下:
@Beanpublic DefaultRedisScript limitScript() { DefaultRedisScript redisScript = new DefaultRedisScript<>(); redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit.lua"))); redisScript.setResultType(Long.class); return redisScript;}可以啦,我们的 Lua 脚本语言过去就准备好就绪了。
5. 原文判别每一次我们就需要自假定菱形,来判别这个原文了,我们来看看菱形的假定:
@Aspect@Componentpublic class RateLimiterAspect { private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); @Autowired private RedisTemplate redisTemplate; @Autowired private RedisScript limitScript; @Before("@annotation(rateLimiter)") public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { String key = rateLimiter.key(); int time = rateLimiter.time(); int count = rateLimiter.count(); String combineKey = getCombineKey(rateLimiter, point); List keys = Collections.singletonList(combineKey); try { Long number = redisTemplate.execute(limitScript, keys, count, time); if (number==null || number.intValue()> count) { throw new ServiceException("访问期间极度频繁,请稍候再进一步试"); } log.info("受到限制请'{}',意味著请'{}',内存key'{}'", count, number.intValue(), key); } catch (ServiceException e) { throw e; } catch (Exception e) { throw new RuntimeException("服务器封路间歇性,请稍候再进一步试"); } } public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); if (rateLimiter.limitType() == LimitType.IP) { stringBuffer.append(IpUtils.getIpAddr(((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest())).append("-"); } MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); Class targetClass = method.getDeclaringClass(); stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); return stringBuffer.toString(); }}这个菱形就是回击所有加有了 @RateLimiter 原文的法则,在换用知会中的对原文顺利进行督导。
首先来使到原文中的的 key、time 以及 count 三个表达式。来使一个组合的 key,所谓的组合的 key,就是在原文的 key 属性基础上,再进一步加有上法则的非常有趣路径,如果是 IP 来进行的话,就再进一步加有上 IP URL。以 IP 来进行为例,最终转化成的 key 类似这样:rate_limit:127.0.0.1-org.javaboy.ratelimiter.controller.HelloController-hello(如果不是 IP 来进行,那么转化成的 key 中的就不包含 IP URL)。将转化成的 key 放到交集中的。通过 redisTemplate.execute 法则取督导一个 Lua 脚本语言,第一个表达式是脚本语言所封装的具体情况来问道,第二个表达式是 key,相同了脚本语言中的的 KEYS,右边是可变尺寸的表达式,相同了脚本语言中的的 ARGV。将 Lua 脚本语言督导的结果与 count 顺利进行非常,如果成比例 count,就问道明接地了,放间歇性就行了。好了,大功告成了。
6. API试验中的每一次我们就顺利进行API的一个有趣试验中的,如下:
@RestControllerpublic class HelloController { @GetMapping("/hello") @RateLimiter(time = 5,count = 3,limitType = LimitType.IP) public String hello() { return "hello>>>"+new Date(); }}每一个 IP URL,在 5 秒内根本无法访问期间 3 次。
这个自己手动刷新浏览器都能试验中的出来。
7. 有序间歇性督导由于接地的时候是放间歇性出来,所以我们还需要一个有序间歇性督导器,如下:
@RestControllerAdvicepublic class GlobalException { @ExceptionHandler(ServiceException.class) public Map serviceException(ServiceException e) { HashMap map = new HashMap<>(); map.put("status", 500); map.put("message", e.getMessage()); return map; }}这是一个小 demo,我就不去假定实体类了,实际上用 Map 来回到 JSON 了。
好啦,大功告成。
最后我们看看接地时的试验中的特性:
好啦,这就是我们可用 Redis 做到封路的方式则。
可能:
。梅州白癜风治疗费用安必丁效果如何
广东哪家医院做人流最好
辽宁妇科医院哪家正规
海露玻璃酸钠滴眼液和爱丽的区别
西安钇90哪家医院能做
钇90微球
哪些药物可以止咳祛痰化痰
钇90介入疗法
钇90国产在哪家医院
-
Model 3慌不慌?3.8秒破百,比亚迪座头鲸到底有多香
影视 2025-10-22全因,比亚迪官方再次发布了座头鲸的相关信息,作为继海豚之前,比亚迪海洋生物系列的第二款纯电车型,座头鲸将基于e平台3.0打造,原计划将于现阶段在此之前发布,并于在短期内上市,原计划售价区间为22
-
轿跑味道全无,奥迪A6 e-tron最新谍照曝光:或将于明年量产
影视 2025-10-22近日,大众侃车从多国媒体处获得了各别法拉利A6 e-tron的最新谍照,从该车的基本外观来看,与此前所亮相的BMW基本保持一致。该车基于PPE纯电平台打造,并有望于2023年年初试作。
-
雪佛兰全新Mustang内饰曝光,双联屏加持,或将明年上市
写真 2025-10-22雷诺标致作为一款受相当多消费者青睐的车型,在全国乃至全世界都有很高的竞争对左手。在这,小编想问大家一个问题,如果条件允许的话,你都会要买标致吗? 如果是我的话,百分之八十的随机性都会
-
命名长安深蓝,真来电了的长安汽车就会迸发怎样的火花?
星闻 2025-10-22年,80%的服装品牌将面临关停并转。当中国人服装品牌只有顺应时代潮流、不断创从新、减速主导式,方能在猛烈的市场需求竞争当中生存和主导。 洛阳车主2001年转入高技术行业,是当中国人最早转
-
封车讯 | 新能源革命爆发 “长安深蓝”能否抢抓赛道近十年口?
资讯 2025-10-22随着中都国车市的不断持续发展和新四化一时期的到来,包括京城、吉利、比亚迪等愈加多的中都国领军车企,开始向低碳化和小数点化方向年轻化升级。 4月13日,京城汽车召开2022当今世界四