Commit 8d151d8e authored by liaozan's avatar liaozan 🏀

Remove RateLimiter

parent 139a0c15
...@@ -26,14 +26,6 @@ ...@@ -26,14 +26,6 @@
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
</dependency> </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>
</dependency>
</dependencies> </dependencies>
</project> </project>
package com.schbrain.framework.autoconfigure.cache; package com.schbrain.framework.autoconfigure.cache;
import com.schbrain.common.util.JacksonUtils;
import com.schbrain.framework.autoconfigure.cache.concurrent.RateLimitAspect;
import com.schbrain.framework.autoconfigure.cache.properties.CacheProperties; import com.schbrain.framework.autoconfigure.cache.properties.CacheProperties;
import com.schbrain.framework.autoconfigure.cache.provider.CacheProvider; import com.schbrain.framework.autoconfigure.cache.provider.CacheProvider;
import com.schbrain.framework.autoconfigure.cache.provider.CacheProviderDelegate; import com.schbrain.framework.autoconfigure.cache.provider.CacheProviderDelegate;
import com.schbrain.framework.autoconfigure.cache.provider.redis.RedisCacheConfiguration; import com.schbrain.framework.autoconfigure.cache.provider.redis.RedisCacheConfiguration;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -37,17 +30,4 @@ public class CacheAutoConfiguration { ...@@ -37,17 +30,4 @@ public class CacheAutoConfiguration {
return provider; return provider;
} }
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(RedissonClient.class)
public RateLimitAspect rateLimitAspect(ConfigurableListableBeanFactory beanFactory, RedissonClient redissonClient) {
return new RateLimitAspect(beanFactory, redissonClient);
}
@Bean
@ConditionalOnBean(RedissonClient.class)
public RedissonAutoConfigurationCustomizer redissonConfigurationCodecCustomizer() {
return config -> config.setCodec(new JsonJacksonCodec(JacksonUtils.getObjectMapper()));
}
} }
package com.schbrain.framework.autoconfigure.cache.concurrent;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author liaozan
* @since 2023-07-15
*/
public class ExpressionParser {
private final Map<String, Expression> expressionCache = new ConcurrentHashMap<>(256);
private final SpelExpressionParser parser = new SpelExpressionParser();
private final ParserContext parserContext = new TemplateParserContext();
private final ConfigurableListableBeanFactory beanFactory;
public ExpressionParser(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String parse(String value, Map<String, Object> variables) {
String resolved = beanFactory.resolveEmbeddedValue(value);
Expression expression = expressionCache.computeIfAbsent(resolved, this::parseExpression);
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(beanFactory));
context.setVariables(variables);
return expression.getValue(context, String.class);
}
private Expression parseExpression(String value) {
return parser.parseExpression(value, parserContext);
}
}
package com.schbrain.framework.autoconfigure.cache.concurrent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.util.Collections;
import java.util.Map;
/**
* @author liaozan
* @since 2022/5/5
*/
public class NoOpRateLimitCacheKeyVariablesContributor implements RateLimitCacheKeyVariablesContributor {
@Override
public Map<String, Object> contribute(RateLimiter rateLimiter, JoinPoint joinPoint, MethodSignature signature) {
return Collections.emptyMap();
}
}
package com.schbrain.framework.autoconfigure.cache.concurrent;
import com.schbrain.common.exception.BaseException;
import com.schbrain.common.util.ApplicationName;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
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.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static com.schbrain.common.util.ParameterDiscoverUtils.getMethodArgsMap;
/**
* @author liaozan
* @see RateLimiter
* @since 2022/5/5
*/
@Slf4j
@Aspect
public class RateLimitAspect {
private final Map<String, RRateLimiter> rateLimiterMap = new ConcurrentHashMap<>();
private final Map<Class<?>, RateLimitCacheKeyVariablesContributor> contributorMap = new ConcurrentHashMap<>();
private final String keyPrefix;
private final RedissonClient redissonClient;
private final ExpressionParser expressionParser;
public RateLimitAspect(ConfigurableListableBeanFactory beanFactory, RedissonClient redissonClient) {
this.keyPrefix = ApplicationName.get();
this.redissonClient = redissonClient;
this.expressionParser = new ExpressionParser(beanFactory);
}
@Before("@annotation(annotation)")
public void beforeExecute(JoinPoint joinPoint, RateLimiter annotation) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
doRateLimit(annotation, joinPoint, methodSignature);
}
protected void doRateLimit(RateLimiter annotation, JoinPoint joinPoint, MethodSignature signature) {
Map<String, Object> variables = prepareVariables(annotation, joinPoint, signature);
String cacheKey = expressionParser.parse(annotation.cacheKey(), variables);
if (cacheKey == null) {
throw new BaseException("cacheKey should not be null");
}
String formattedCacheKey = formatCacheKey(cacheKey);
RRateLimiter rateLimiter = rateLimiterMap.computeIfAbsent(formattedCacheKey, key -> createRateLimiter(key, annotation));
if (rateLimiter.tryAcquire()) {
return;
}
throw new BaseException("The access frequency is too fast, please try again later");
}
protected String formatCacheKey(String cacheKey) {
return "rateLimit:" + keyPrefix + ":" + cacheKey;
}
protected RRateLimiter createRateLimiter(String cacheKey, RateLimiter annotation) {
RRateLimiter rateLimiter = redissonClient.getRateLimiter(cacheKey);
rateLimiter.setRate(RateType.PER_CLIENT, annotation.permits(), annotation.expireTime(), RateIntervalUnit.valueOf(annotation.unit().name()));
return rateLimiter;
}
private Map<String, Object> prepareVariables(RateLimiter annotation, JoinPoint joinPoint, MethodSignature signature) {
Map<String, Object> variables = getMethodArgsMap(signature.getMethod(), joinPoint.getArgs());
Class<? extends RateLimitCacheKeyVariablesContributor> contributorClass = annotation.contributor();
if (contributorClass == null || contributorClass == NoOpRateLimitCacheKeyVariablesContributor.class) {
return variables;
}
RateLimitCacheKeyVariablesContributor contributor = contributorMap.get(contributorClass);
if (contributor == null) {
contributor = BeanUtils.instantiateClass(contributorClass);
contributorMap.put(contributorClass, contributor);
}
Map<String, Object> contributeVariables = contributor.contribute(annotation, joinPoint, signature);
if (MapUtils.isNotEmpty(contributeVariables)) {
variables.putAll(contributeVariables);
}
return variables;
}
}
package com.schbrain.framework.autoconfigure.cache.concurrent;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.util.Map;
/**
* @author liaozan
* @since 2022/5/5
*/
public interface RateLimitCacheKeyVariablesContributor {
Map<String, Object> contribute(RateLimiter rateLimiter, JoinPoint joinPoint, MethodSignature signature);
}
package com.schbrain.framework.autoconfigure.cache.concurrent;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @author liaozan
* @since 2022/5/5
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
/**
* 过期时间
*/
long expireTime() default 10;
/**
* 过期时间单位
*/
TimeUnit unit() default TimeUnit.SECONDS;
/**
* 过期时间内允许的许可数
*/
int permits() default 3;
/**
* 缓存的 key,使用 spring el 进行解析
*/
String cacheKey();
/**
* evaluation variables contributor
*/
Class<? extends RateLimitCacheKeyVariablesContributor> contributor() default NoOpRateLimitCacheKeyVariablesContributor.class;
}
...@@ -22,6 +22,10 @@ ...@@ -22,6 +22,10 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment