日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
SpringBoot中如何實現(xiàn)限流,這種方式才叫優(yōu)雅!

很早以前,我曾寫過兩篇介紹如何在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ù)。

  1. 使用Guava實現(xiàn)單機令牌桶限流
  2. 使用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)限流功能

  1. 創(chuàng)建限流接口

既然有兩種限流機制,按照套路肯定得先創(chuàng)建一個限流接口,就叫LimiterManager吧。

public interface LimiterManager {
boolean tryAccess(Limiter limiter);
}
  1. 分別實現(xiàn)Redis的限流功能和Guava的限流功能,這里只給出核心代碼。

Guava限流的核心實現(xiàn)GuavaLimiter

@Slf4j
public class GuavaLimiter implements LimiterManager{
private final Map limiterMap = 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" );
}

List keys = new ArrayList<>();
keys.add( key );

int seconds = limiter.getSeconds();
int limitCount = limiter.getLimitNum();

String luaScript = buildLuaScript();

RedisScript redisScript = 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)如下:

第五步,在項目中引用限流組件

  1. 引入依賴

com.jianzh5
cloud-limit-starter
  1. 在application.properties中設(shè)置加載的限流組件
limit.type = redis

如果不配置此屬性則不加載對應(yīng)限流功能。

  1. 在需要限流的接口上加上注解
@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