-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
539 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>framework</artifactId> | ||
<groupId>com.cloudshare</groupId> | ||
<version>1.0-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>org.novo</groupId> | ||
<artifactId>limit-spring-boot-starter</artifactId> | ||
|
||
<properties> | ||
<maven.compiler.source>17</maven.compiler.source> | ||
<maven.compiler.target>17</maven.compiler.target> | ||
</properties> | ||
|
||
<dependencies> | ||
<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.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-redis</artifactId> | ||
</dependency> | ||
</dependencies> | ||
</project> |
42 changes: 42 additions & 0 deletions
42
framework/limit-spring-boot-starter/src/main/java/org/novo/limit/annotation/RateLimit.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package org.novo.limit.annotation; | ||
|
||
import org.novo.limit.enums.LimitType; | ||
|
||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* @author novo | ||
* @since 2023/11/14 | ||
*/ | ||
@Target(ElementType.METHOD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@Documented | ||
public @interface RateLimit { | ||
|
||
String key() default "rate_limit:"; | ||
|
||
/** | ||
* 限流时间窗 | ||
* | ||
* @return | ||
*/ | ||
int time() default 60; | ||
|
||
/** | ||
* 在时间窗内的限流次数 | ||
* | ||
* @return | ||
*/ | ||
int count() default 100; | ||
|
||
/** | ||
* 限流类型 | ||
* | ||
* @return | ||
*/ | ||
LimitType limitType() default LimitType.DEFAULT; | ||
} |
82 changes: 82 additions & 0 deletions
82
...work/limit-spring-boot-starter/src/main/java/org/novo/limit/aspect/RateLimiterAspect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package org.novo.limit.aspect; | ||
|
||
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.novo.limit.annotation.RateLimit; | ||
import org.novo.limit.enums.LimitType; | ||
import org.novo.limit.exception.RateLimitException; | ||
import org.novo.limit.util.IpUtil; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.data.redis.core.StringRedisTemplate; | ||
import org.springframework.data.redis.core.script.RedisScript; | ||
import org.springframework.web.context.request.RequestContextHolder; | ||
import org.springframework.web.context.request.ServletRequestAttributes; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.Collections; | ||
|
||
/** | ||
* @author novo | ||
* @since 2023/11/14 | ||
*/ | ||
@Aspect | ||
public class RateLimiterAspect { | ||
|
||
private static final Logger logger = LoggerFactory.getLogger(RateLimiterAspect.class); | ||
|
||
private final RedisTemplate<String, Long> redisTemplate; | ||
|
||
private final RedisScript<Long> redisScript; | ||
|
||
public RateLimiterAspect(RedisTemplate<String, Long> redisTemplate, RedisScript<Long> redisScript) { | ||
this.redisTemplate = redisTemplate; | ||
this.redisScript = redisScript; | ||
} | ||
|
||
@Before("@annotation(rateLimit)") | ||
public void doBefore(JoinPoint joinPoint, RateLimit rateLimit) { | ||
int time = rateLimit.time(); | ||
int count = rateLimit.count(); | ||
String combineKey = getCombineKey(rateLimit, joinPoint); | ||
Long current = redisTemplate.execute(redisScript, Collections.singletonList(combineKey), time, count); | ||
if (current == null || current.intValue() > count) { | ||
// 超过限流 | ||
logger.info("当前接口已达到最大限流次数"); | ||
throw new RateLimitException("访问过于频繁,请稍后访问"); | ||
} | ||
logger.info("一个时间窗内请求次数:{},当前请求次数:{},缓存的 key 为{}", count, current, combineKey); | ||
} | ||
|
||
/** | ||
* 组合key | ||
* redis上的key | ||
* 基于IP: | ||
* 前缀:IP-方法的唯一标识 | ||
* rate_limit:10.10.1.1-com.wyu.controller.HelloController-hello | ||
* <p> | ||
* 默认限流: | ||
* rate_limit:com.wyu.controller.HelloController-hello | ||
* | ||
* @param rateLimit | ||
* @param joinPoint | ||
* @return | ||
*/ | ||
private String getCombineKey(RateLimit rateLimit, JoinPoint joinPoint) { | ||
StringBuilder key = new StringBuilder(rateLimit.key()); | ||
if (rateLimit.limitType() == LimitType.IP) { | ||
key.append(IpUtil.getIpAddr(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest())) | ||
.append("-"); | ||
} | ||
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); | ||
Method method = signature.getMethod(); | ||
key.append(method.getDeclaringClass().getName()) | ||
.append("-") // 类名 | ||
.append(method.getName()); // 方法名 | ||
logger.info(key.toString()); | ||
return key.toString(); | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...t-spring-boot-starter/src/main/java/org/novo/limit/config/RateLimitAutoConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package org.novo.limit.config; | ||
|
||
import org.novo.limit.aspect.RateLimiterAspect; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.core.io.ClassPathResource; | ||
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.core.script.RedisScript; | ||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; | ||
import org.springframework.data.redis.serializer.StringRedisSerializer; | ||
import org.springframework.scripting.support.ResourceScriptSource; | ||
|
||
/** | ||
* @author novo | ||
* @since 2023/11/14 | ||
*/ | ||
public class RateLimitAutoConfiguration { | ||
|
||
@Bean("limitScript") | ||
public DefaultRedisScript<Long> limitScript() { | ||
DefaultRedisScript<Long> limitScript = new DefaultRedisScript<>(); | ||
// 设置脚本返回的类型 | ||
limitScript.setResultType(Long.class); | ||
// 设置脚本位置 根目录从resource开始 | ||
limitScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit.lua"))); | ||
return limitScript; | ||
} | ||
|
||
|
||
@Bean("limitRedisTemplate") | ||
public RedisTemplate<String, Long> redisTemplate(RedisConnectionFactory connectionFactory) { | ||
RedisTemplate<String, Long> redisTemplate = new RedisTemplate<>(); | ||
Jackson2JsonRedisSerializer<Long> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Long.class); | ||
//设置string key和value序列器 | ||
redisTemplate.setKeySerializer(new StringRedisSerializer()); | ||
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); | ||
redisTemplate.setConnectionFactory(connectionFactory); | ||
return redisTemplate; | ||
} | ||
|
||
@Bean | ||
public RateLimiterAspect rateLimiterAspect(@Qualifier("limitRedisTemplate") RedisTemplate<String, Long> redisTemplate, | ||
@Qualifier("limitScript") RedisScript<Long> redisScript) { | ||
return new RateLimiterAspect(redisTemplate, redisScript); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
framework/limit-spring-boot-starter/src/main/java/org/novo/limit/enums/LimitType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.novo.limit.enums; | ||
|
||
/** | ||
* @author novo | ||
* @since 2023/11/14 | ||
*/ | ||
public enum LimitType { | ||
|
||
/** | ||
* 默认限流策略 针对某一个接口 | ||
*/ | ||
DEFAULT, | ||
|
||
/** | ||
* 针对某一个ip | ||
*/ | ||
IP, | ||
|
||
; | ||
} |
17 changes: 17 additions & 0 deletions
17
.../limit-spring-boot-starter/src/main/java/org/novo/limit/exception/RateLimitException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.novo.limit.exception; | ||
|
||
import java.io.Serial; | ||
|
||
/** | ||
* @author novo | ||
* @since 2023/11/14 | ||
*/ | ||
public class RateLimitException extends RuntimeException { | ||
|
||
@Serial | ||
private static final long serialVersionUID = 7364848679991344873L; | ||
|
||
public RateLimitException(String message) { | ||
super(message); | ||
} | ||
} |
Oops, something went wrong.