河北网站推广,网站查外链,网络推广公司名字大全,网络销售的方法和技巧前言
业务中需要对一些接口进行限流处理#xff0c;防止机器人调用或者保证服务质量#xff1b;
实现方式
基于redis的lua脚本
引入依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis防止机器人调用或者保证服务质量
实现方式
基于redis的lua脚本
引入依赖
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependencyredis配置
package com.qiangesoft.wechat.config;import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redis配置** author qiangesoft* date 2024-03-19*/
Configuration
EnableCaching
public class RedisConfig extends CachingConfigurerSupport {BeanSuppressWarnings(value {unchecked, rawtypes})public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplateObject, Object template new RedisTemplate();template.setConnectionFactory(connectionFactory);FastJson2JsonRedisSerializer serializer new FastJson2JsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}Beanpublic DefaultRedisScriptLong limitScript() {DefaultRedisScriptLong redisScript new DefaultRedisScript();redisScript.setScriptText(limitScriptText());redisScript.setResultType(Long.class);return redisScript;}/*** 限流脚本*/private String limitScriptText() {return local key KEYS[1]\n local count tonumber(ARGV[1])\n local time tonumber(ARGV[2])\n local current redis.call(get, key);\n if current and tonumber(current) count then\n return tonumber(current);\n end\n current redis.call(incr, key)\n if tonumber(current) 1 then\n redis.call(expire, key, time)\n end\n return tonumber(current);;}
}
限流注解
package com.qiangesoft.wechat.config;import java.lang.annotation.*;/*** 限流注解** author qiangesoft* date 2024-03-19*/
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
Documented
public interface RateLimiter {/*** 限流类型*/LimitType limitType() default LimitType.IP;/*** 限流时间,单位秒*/int time() default 60;/*** 限流次数*/int count() default 10;}
限流实现
package com.qiangesoft.wechat.config;import com.qiangesoft.wechat.utils.IpUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;/*** 限流处理** author qiangesoft* date 2024-03-19*/
RequiredArgsConstructor
Slf4j
Aspect
Component
public class RateLimiterAspect {private final RedisTemplateObject, Object redisTemplate;private final RedisScriptLong limitScript;Before(annotation(rateLimiter))public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {int time rateLimiter.time();int count rateLimiter.count();String key this.buildKey(rateLimiter, point);ListObject keys Collections.singletonList(key);try {Long number redisTemplate.execute(limitScript, keys, count, time);if (number null || number.intValue() count) {throw new RuntimeException(访问过于频繁请稍候再试);}} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException(服务器限流异常请稍候再试);}}/*** 缓存key** param rateLimiter* param point* return*/public String buildKey(RateLimiter rateLimiter, JoinPoint point) {String limitId ;if (rateLimiter.limitType() LimitType.IP) {limitId IpUtil.getIpAddr();}MethodSignature signature (MethodSignature) point.getSignature();Method method signature.getMethod();Class? targetClass method.getDeclaringClass();String key rate_limit: limitId - targetClass.getName() - method.getName();return key;}
}
测试代码
package com.qiangesoft.wechat.controller;import com.qiangesoft.wechat.config.RateLimiter;
import com.qiangesoft.wechat.utils.ResultVO;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;/*** 测试接口** author qiangesoft* date 2024-03-19*/
RestController
public class TestController {RateLimiterGetMapping(/test)public ResultVO test() {return ResultVO.ok(调用成功);}} 继续频繁点击