SpringBoot集成Redisson实现自定义注解限流
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。
其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。
Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。
引入坐标文件
SpringBoot版本
<parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.6.9</version> <relativePath/> </parent>
|
redisson版本
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.17.4</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
|
具体代码实现
application.yml,redisson配置文件
redisson: threads: 2 nettyThreads: 4 singleServerConfig: clientName: xxxxxx connectionMinimumIdleSize: 8 connectionPoolSize: 32 idleConnectionTimeout: 10000 timeout: 3000 subscriptionConnectionPoolSize: 50 cacheGroup: - groupId: springCacheTest ttl: 30 maxIdleTime: 30 maxSize: 500
|
RedissonProperties.java,用于获取application.yml中的配置文件
@Data @Component @ConfigurationProperties(prefix = "redisson") public class RedissonProperties {
private int threads;
private int nettyThreads;
private SingleServerConfig singleServerConfig;
private ClusterServersConfig clusterServersConfig;
private List<CacheGroup> cacheGroup;
@Data @NoArgsConstructor public static class SingleServerConfig {
private String clientName;
private int connectionMinimumIdleSize;
private int connectionPoolSize;
private int idleConnectionTimeout;
private int timeout;
private int subscriptionConnectionPoolSize;
}
@Data @NoArgsConstructor public static class ClusterServersConfig {
private String clientName;
private int masterConnectionMinimumIdleSize;
private int masterConnectionPoolSize;
private int slaveConnectionMinimumIdleSize;
private int slaveConnectionPoolSize;
private int idleConnectionTimeout;
private int timeout;
private int subscriptionConnectionPoolSize;
private ReadMode readMode;
private SubscriptionMode subscriptionMode;
}
@Data @NoArgsConstructor public static class CacheGroup {
private String groupId;
private long ttl;
private long maxIdleTime;
private int maxSize; } }
|
RedissonConfig.java,Redisson的配置文件
@RequiredArgsConstructor @EnableCaching @Configuration public class RedissonConfig extends CachingConfigurerSupport{
private final RedissonProperties redissonProperties; private final ObjectMapper objectMapper;
@Bean public RedissonAutoConfigurationCustomizer redisCustomizer() { return config -> { config.setThreads(redissonProperties.getThreads()) .setNettyThreads(redissonProperties.getNettyThreads()) .setCodec(new JsonJacksonCodec(objectMapper)); RedissonProperties.SingleServerConfig singleServerConfig = redissonProperties.getSingleServerConfig(); if (ObjectUtil.isNotNull(singleServerConfig)) { config.useSingleServer() .setTimeout(singleServerConfig.getTimeout()) .setClientName(singleServerConfig.getClientName()) .setIdleConnectionTimeout(singleServerConfig.getIdleConnectionTimeout()) .setSubscriptionConnectionPoolSize(singleServerConfig.getSubscriptionConnectionPoolSize()) .setConnectionMinimumIdleSize(singleServerConfig.getConnectionMinimumIdleSize()) .setConnectionPoolSize(singleServerConfig.getConnectionPoolSize()); } }; } }
|
MyRateLimit.java,自定义限流注解,加载需要限流的方法上
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface MyRateLimit {
int count() default 50;
int time() default 10; }
|
MyRateLimitAspect.java,aop切面处理器
@Component @Aspect
@Order(1) @Slf4j public class MyRateLimitAspect {
@Before("@annotation(myRateLimit)") public void doBefore(JoinPoint point, MyRateLimit myRateLimit) { int count = myRateLimit.count(); int time = myRateLimit.time(); log.info("次数:" + count + ",间隔:" + time); String key = getCombineKey(point); try { long number = RedisUtil.rateLimiter(key, RateType.OVERALL, count, time); if (number == -1) { throw new RateLimitException("请求速度过快,请稍后重试"); } log.info("限制令牌 => {}, 剩余令牌 => {}, 缓存key => '{}'", count, number, key); } catch (RateLimitException e1) { throw new RateLimitException("请求速度过快,请稍后重试"); } catch (Exception e) { throw new RuntimeException("服务器限流异常,请稍候再试"); } }
private String getCombineKey(JoinPoint point) { StringBuilder stringBuilder = new StringBuilder(ServletUtils.getClientIp()) .append("-");
MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); Class<?> targetClass = method.getDeclaringClass(); stringBuilder.append(targetClass.getName()) .append("-") .append(method.getName()); return stringBuilder.toString(); } }
|
将MyRateLimit注解加在需要限流的方法上,示例:
@MyRateLimit(time = 10,count = 10) @GetMapping("/toRateLimit") public R<Void> toRateLimit() { return R.success(); }
|
注:SpringBoot集成Redisson,使用Redis替换SpringCache的底层实现,见:下篇文章