自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

SpringBoot中如何實(shí)現(xiàn)限流,這種方式才叫優(yōu)雅!

開發(fā) 前端
在實(shí)際應(yīng)用中,只需要根據(jù)場景需求選擇對應(yīng)的限流機(jī)制,即可非常方便的進(jìn)行限流操作。這種靈活性和便捷性,也是SpringBoot中定義Starter的一般套路。

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

  1. 使用Guava實(shí)現(xiàn)單機(jī)令牌桶限流
  2. 使用Redis實(shí)現(xiàn)分布式限流

現(xiàn)在,一個(gè)問題擺在我們面前:如何將這兩種限流機(jī)制整合到同一個(gè)組件中,以便用戶隨時(shí)切換呢?

顯然,我們需要定義一個(gè)通用的限流組件,將其引入到業(yè)務(wù)中,并支持通過配置文件自由切換不同的限流機(jī)制。舉例而言,當(dāng)使用limit.type=redis時(shí),啟用Redis分布式限流組件,當(dāng)使用limit.type=local時(shí),啟用Guava限流組件。這種自由切換機(jī)制能夠?yàn)橛脩籼峁└蟮撵`活性和可維護(hù)性。

接下來,讓我們開始動(dòng)手實(shí)現(xiàn)吧!

第一步,創(chuàng)建通用模塊cloud-limiter-starter

首先在父項(xiàng)目下創(chuàng)建一個(gè)模塊

圖片

然后在pom文件中引入相關(guān)依賴

<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!--SpringFramework-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<scope>provided</scope>
</dependency>

</dependencies>

小提示:通用模塊命名最好遵照規(guī)則以starter命名結(jié)束,同時(shí)通用模塊引入的依賴最好設(shè)置<scope>provided</scope>屬性。

第二步,實(shí)現(xiàn)限流功能

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

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

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

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

@Slf4j
public class GuavaLimiter implements LimiterManager{
private final Map<String, RateLimiter> 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限流的核心實(shí)現(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<String> keys = new ArrayList<>();
keys.add( key );

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

String luaScript = buildLuaScript();

RedisScript<Long> 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ù)配置文件注入限流實(shí)現(xiàn)類,當(dāng)配置文件中屬性 limit.type=local 時(shí)啟用Guava限流機(jī)制,當(dāng)limit.type=redis 時(shí)啟用Redis限流機(jī)制。

@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,所以很顯然還得需要?jiǎng)?chuàng)建一個(gè)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屬性時(shí)才會加載這個(gè)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)如下:

圖片

第五步,在項(xiàng)目中引用限流組件

  1. 引入依賴
<dependency>
<groupId>com.jianzh5</groupId>
<artifactId>cloud-limit-starter</artifactId>
</dependency>
  1. 在application.properties中設(shè)置加載的限流組件
limit.type = redis

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

  1. 在需要限流的接口上加上注解
@Limit(key = "Limiter:test",limitNum = 3,seconds = 1)

小結(jié)

通過上述步驟,我們已經(jīng)成功實(shí)現(xiàn)了一個(gè)通用限流組件。在實(shí)際應(yīng)用中,只需要根據(jù)場景需求選擇對應(yīng)的限流機(jī)制,即可非常方便的進(jìn)行限流操作。這種靈活性和便捷性,也是SpringBoot中定義Starter的一般套路。

如果你想詳細(xì)了解這兩種限流機(jī)制的原理,可以參考之前的文章中所介紹的內(nèi)容。

責(zé)任編輯:武曉燕 來源: JAVA日知錄
相關(guān)推薦

2022-02-15 17:56:19

SpringBoot日志

2022-09-01 13:12:53

LinuxTC網(wǎng)絡(luò)限流

2023-10-27 08:20:12

springboot微服務(wù)

2024-09-09 11:35:35

2020-03-25 17:55:30

SpringBoot攔截器Java

2021-03-30 10:46:42

SpringBoot計(jì)數(shù)器漏桶算法

2021-11-10 10:03:18

SpringBootJava代碼

2020-10-25 19:58:04

Pythonic代碼語言

2023-06-28 08:25:14

事務(wù)SQL語句

2023-12-20 13:50:00

SpringBootJSON序列化

2021-11-05 21:33:28

Redis數(shù)據(jù)高并發(fā)

2023-08-01 08:54:02

接口冪等網(wǎng)絡(luò)

2024-12-18 12:10:00

2025-03-17 00:00:00

2024-03-18 14:06:00

停機(jī)Spring服務(wù)器

2023-08-08 08:01:22

微服務(wù)架構(gòu)服務(wù)

2024-11-05 15:02:41

2023-08-15 08:01:12

2024-02-26 14:07:18

2023-05-10 13:58:13

服務(wù)限流系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號