如何在API控制器层面基于条件启用Resilience4j限流?
实现Resilience4j限流的条件控制
下面是几种通过标志位控制@RateLimiter注解是否生效的可行方案:
方案一:配置开关+自定义RateLimiter实例
先在配置文件中新增限流开关,支持通过环境变量动态控制:
resilience4j.ratelimiter: instances: basic: limitForPeriod: ${RATE_LIMIT_PERMISIBLE_HITS:1000} limitRefreshPeriod: ${RATE_LIMIT_REFRESH_RATE:1h} timeoutDuration: ${RATE_LIMIT_TIMEOUT:20s} registerHealthIndicator: ${RATE_LIMIT_HEALTH_INDICATOR:true} eventConsumerBufferSize: ${RATE_LIMIT_EVENT_BUFFER:100} # 新增限流开关 rate-limit: enabled: ${RATE_LIMIT_ENABLED:true}
然后自定义RateLimiterRegistry Bean,根据开关状态决定使用正常限流配置还是无限制配置:
import io.github.resilience4j.ratelimiter.RateLimiterConfig import io.github.resilience4j.ratelimiter.RateLimiterRegistry import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration @Configuration class CustomRateLimiterConfig { @Value("\${rate-limit.enabled:true}") private val rateLimitEnabled: Boolean = true @Bean fun rateLimiterRegistry(): RateLimiterRegistry { val registry = RateLimiterRegistry.ofDefaults() if (!rateLimitEnabled) { // 创建无限制的限流实例,覆盖原有basic实例 val unlimitedConfig = RateLimiterConfig.custom() .limitForPeriod(Int.MAX_VALUE) .limitRefreshPeriod(java.time.Duration.ofMillis(1)) .timeoutDuration(java.time.Duration.ofMillis(0)) .build() registry.rateLimiter("basic", unlimitedConfig) } return registry } }
当开关关闭时,basic限流实例会被替换为无限制版本,相当于限流逻辑不生效。
方案二:AOP环绕通知跳过限流
通过AOP拦截所有带有@RateLimiter注解的方法,根据开关状态决定是否直接执行方法:
确保项目已引入AOP依赖后,编写切面类:
import io.github.resilience4j.ratelimiter.annotation.RateLimiter import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.reflect.MethodSignature import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component @Aspect @Component class RateLimiterControlAspect { @Value("\${rate-limit.enabled:true}") private val rateLimitEnabled: Boolean = true @Around("@annotation(io.github.resilience4j.ratelimiter.annotation.RateLimiter)") suspend fun aroundRateLimiterMethod(joinPoint: ProceedingJoinPoint): Any? { if (!rateLimitEnabled) { // 开关关闭,直接执行方法,跳过限流逻辑 return joinPoint.proceed() } // 开关开启,执行原有限流逻辑 return joinPoint.proceed() } }
这个切面会拦截所有带@RateLimiter的方法,开关关闭时直接放行,等同于注解不生效。
方案三:自定义条件注解
封装一个带开关控制的自定义注解,替换原有的@RateLimiter:
首先定义自定义注解:
import org.springframework.core.annotation.AliasFor import io.github.resilience4j.ratelimiter.annotation.RateLimiter @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) @Retention(AnnotationRetention.RUNTIME) @RateLimiter annotation class ConditionalRateLimiter( @get:AliasFor(annotation = RateLimiter::class) val name: String = "", @get:AliasFor(annotation = RateLimiter::class) val fallbackMethod: String = "", // 指定读取的开关配置项 val enabledProperty: String = "rate-limit.enabled" )
然后编写切面处理该注解:
import org.aspectj.lang.ProceedingJoinPoint import org.aspectj.lang.annotation.Around import org.aspectj.lang.annotation.Aspect import org.aspectj.lang.reflect.MethodSignature import org.springframework.core.env.Environment import org.springframework.stereotype.Component @Aspect @Component class ConditionalRateLimiterAspect(private val environment: Environment) { @Around("@annotation(your.package.ConditionalRateLimiter)") suspend fun aroundConditionalRateLimiter(joinPoint: ProceedingJoinPoint): Any? { val signature = joinPoint.signature as MethodSignature val annotation = signature.method.getAnnotation(ConditionalRateLimiter::class.java) val enabled = environment.getProperty(annotation.enabledProperty, Boolean::class.java, true) if (!enabled) { return joinPoint.proceed() } return joinPoint.proceed() } }
最后在控制器方法上替换注解:
@PostMapping @ConditionalRateLimiter(name = "basic", fallbackMethod = "rateLimiterFallback") suspend fun getTradeEngineResponse(@Valid @RequestBody tradeEngineRequest: TradeEngineRequest): ResponseMessage { return Response<ResponseMessage>().ok( ResponseMessage(id = tradeEngineService.getTradeEngineResponse(tradeEngineRequest), message = SaasConstants.SUCCESS_MESSAGE) ) }
内容的提问来源于stack exchange,提问作者Pranav Gautam
相关产品推荐
相关产品推荐

