新聞中心
很早以前,我曾寫過兩篇介紹如何在SpringBoot中使用Guava和Redis實現(xiàn)接口限流的文章。具體包括:

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供重慶網(wǎng)站建設(shè)、重慶做網(wǎng)站、重慶網(wǎng)站設(shè)計、重慶網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、重慶企業(yè)網(wǎng)站模板建站服務(wù),10余年重慶做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
- 使用Guava實現(xiàn)單機令牌桶限流
- 使用Redis實現(xiàn)分布式限流
現(xiàn)在,一個問題擺在我們面前:如何將這兩種限流機制整合到同一個組件中,以便用戶隨時切換呢?
顯然,我們需要定義一個通用的限流組件,將其引入到業(yè)務(wù)中,并支持通過配置文件自由切換不同的限流機制。舉例而言,當(dāng)使用limit.type=redis時,啟用Redis分布式限流組件,當(dāng)使用limit.type=local時,啟用Guava限流組件。這種自由切換機制能夠為用戶提供更大的靈活性和可維護性。
接下來,讓我們開始動手實現(xiàn)吧!
第一步,創(chuàng)建通用模塊cloud-limiter-starter
首先在父項目下創(chuàng)建一個模塊
然后在pom文件中引入相關(guān)依賴
com.google.guava
guava
org.springframework.boot
spring-boot-starter-web
provided
org.springframework.boot
spring-boot-starter-data-redis
provided
org.projectlombok
lombok
provided
org.springframework
spring-aspects
provided
小提示:通用模塊命名最好遵照規(guī)則以starter命名結(jié)束,同時通用模塊引入的依賴最好設(shè)置
provided 屬性。
第二步,實現(xiàn)限流功能
- 創(chuàng)建限流接口
既然有兩種限流機制,按照套路肯定得先創(chuàng)建一個限流接口,就叫LimiterManager吧。
public interface LimiterManager {
boolean tryAccess(Limiter limiter);
}- 分別實現(xiàn)Redis的限流功能和Guava的限流功能,這里只給出核心代碼。
Guava限流的核心實現(xiàn)GuavaLimiter
@Slf4j
public class GuavaLimiter implements LimiterManager{
private final MaplimiterMap = Maps.newConcurrentMap();
@Override
public boolean tryAccess(Limiter limiter) {
RateLimiter rateLimiter = getRateLimiter(limiter);
if (rateLimiter == null) {
return false;
}
boolean access = rateLimiter.tryAcquire(1,100, TimeUnit.MILLISECONDS);
log.info("{} access :{}",limiter.getKey() , access);
return access;
}
}
Redis限流的核心實現(xiàn)RedisLimiter
@Slf4j
public class RedisLimiter implements LimiterManager{
private final StringRedisTemplate stringRedisTemplate;
public RedisLimiter(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean tryAccess(Limiter limiter) {
String key = limiter.getKey();
if (StringUtils.isEmpty(key)) {
throw new LimiterException( "redis limiter key cannot be null" );
}
Listkeys = new ArrayList<>();
keys.add( key );
int seconds = limiter.getSeconds();
int limitCount = limiter.getLimitNum();
String luaScript = buildLuaScript();
RedisScriptredisScript = new DefaultRedisScript<>(luaScript, Long.class);
Long count = stringRedisTemplate.execute( redisScript, keys, "" + limitCount, "" + seconds );
log.info( "Access try count is {} for key={}", count, key );
return count != null && count != 0;
}
}
第三步,創(chuàng)建配置類
編寫配置類根據(jù)配置文件注入限流實現(xiàn)類,當(dāng)配置文件中屬性 limit.type=local 時啟用Guava限流機制,當(dāng)limit.type=redis 時啟用Redis限流機制。
@Configuration
public class LimiterConfigure {
@Bean
@ConditionalOnProperty(name = "limit.type",havingValue = "local")
public LimiterManager guavaLimiter(){
return new GuavaLimiter();
}
@Bean
@ConditionalOnProperty(name = "limit.type",havingValue = "redis")
public LimiterManager redisLimiter(StringRedisTemplate stringRedisTemplate){
return new RedisLimiter(stringRedisTemplate);
}
}
第四步,創(chuàng)建AOP
根據(jù)前面的兩篇文章可知,避免限流功能污染業(yè)務(wù)邏輯的最好方式是借助Spring AOP,所以很顯然還得需要創(chuàng)建一個AOP。
@Aspect
@EnableAspectJAutoProxy(proxyTargetClass = true) //使用CGLIB代理
@Conditional(LimitAspectCondition.class)
public class LimitAspect {
@Setter(onMethod_ = @Autowired)
private LimiterManager limiterManager;
@Pointcut("@annotation(com.jianzh5.limit.aop.Limit)")
private void check() {
}
@Before("check()")
public void before(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Limit limit = method.getAnnotation(Limit.class);
if(limit != null){
Limiter limiter = Limiter.builder().limitNum(limit.limitNum())
.seconds(limit.seconds())
.key(limit.key()).build();
if(!limiterManager.tryAccess(limiter)){
throw new LimiterException( "There are currently many people , please try again later!" );
}
}
}
}
注意到類上我加了一行@Conditional(LimitAspectCondition.class),使用了自定義條件選擇器,意思是只有當(dāng)配置類中出現(xiàn)了limit.type屬性時才會加載這個AOP。
public class LimitAspectCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//檢查配置文件是否包含limit.type屬性
return conditionContext.getEnvironment().containsProperty(ConfigConstant.LIMIT_TYPE);
}
}第四步,創(chuàng)建spring.factories文件,引導(dǎo)SpringBoot加載配置類
## AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoCnotallow=\
com.jianzh5.limit.config.LimiterConfigure,\
com.jianzh5.limit.aop.LimitAspect
完整目錄結(jié)構(gòu)如下:
第五步,在項目中引用限流組件
- 引入依賴
com.jianzh5
cloud-limit-starter
- 在application.properties中設(shè)置加載的限流組件
limit.type = redis
如果不配置此屬性則不加載對應(yīng)限流功能。
- 在需要限流的接口上加上注解
@Limit(key = "Limiter:test",limitNum = 3,seconds = 1)
小結(jié)
通過上述步驟,我們已經(jīng)成功實現(xiàn)了一個通用限流組件。在實際應(yīng)用中,只需要根據(jù)場景需求選擇對應(yīng)的限流機制,即可非常方便的進行限流操作。這種靈活性和便捷性,也是SpringBoot中定義Starter的一般套路。
如果你想詳細了解這兩種限流機制的原理,可以參考之前的文章中所介紹的內(nèi)容。
新聞名稱:SpringBoot中如何實現(xiàn)限流,這種方式才叫優(yōu)雅!
網(wǎng)站路徑:http://www.dlmjj.cn/article/cdccpcs.html


咨詢
建站咨詢
