SpringBoot4.0新特性Resilience之并發(fā)限制詳解
文章末尾我們講了失敗重試的相關(guān)知識,下面我們來看另一個新特性 - @ConcurrencyLimit并發(fā)限制,簡單的說,它可以限制方法的并發(fā)訪問數(shù)。
使用入門
@ConcurrencyLimit 跟@Retryable類似,可以加在單個方法上,設(shè)置方法級別的并發(fā)限制,也可以加在類上,給類中所有的通過代理調(diào)用的方法統(tǒng)一設(shè)置并發(fā)限制,使用起來非常簡單,僅需2步:
- 在啟動類添加
@EnableResilientMethods
@EnableResilientMethods
@SpringBootApplication
public class Springboot4Application {}
- 在業(yè)務(wù)類或者方法上添加
@ConcurrencyLimit
@ConcurrencyLimit(limit = 1)
@GetMapping(path = "/demo")
public String demo(){
log.info("ConcurrencyLimitController demo");
return "demo";
}
- limit = 1:說明本方法同時只允許1個并發(fā)訪問,如果有多余的并發(fā)請求會一直阻塞等待。
- 加在controller的方法上等于實現(xiàn)了一個簡易版的限流功能,可以對接口做一定程度的保護(hù)。
測試一下效果:
public static void main(String[] args) throws Exception{
System.out.println("start at " + LocalDateTime.now());
int threadCount = 5;
CountDownLatch start = new CountDownLatch(1);
CountDownLatch stop = new CountDownLatch(threadCount);
for(int i=0; i<threadCount; i++){
new Thread(()->{
try {
start.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String res = new RestTemplate().getForObject("http://localhost:8080/api/concurrency-limit/demo", String.class);
System.out.println(res);
stop.countDown();
}).start();
}
start.countDown();
stop.await();
System.out.println("end at " + LocalDateTime.now());
}
我們使用5個線程并發(fā)訪問前面的接口,因為接口只允許1個并發(fā),因此總執(zhí)行時長會超過5秒鐘,日志如下:
2026-01-23T12:52:43 INFO 11868 --- [nio-exec-4] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo 2026-01-23T12:52:44 INFO 11868 --- [nio-exec-2] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo 2026-01-23T12:52:45 INFO 11868 --- [nio-exec-5] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo 2026-01-23T12:52:46 INFO 11868 --- [nio-exec-3] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo 2026-01-23T12:52:47 INFO 11868 --- [nio-exec-1] c.g.x.s.c.ConcurrencyLimitController : ConcurrencyLimitController demo
實現(xiàn)原理
@ConcurrencyLimit背后是ConcurrencyThrottleSupport, 因此我們也可以用編程的方式手動的來做并發(fā)控制,比如我們可以利用現(xiàn)成的SyncTaskExecutor來實現(xiàn)與前面相同的并發(fā)限制功能:
SyncTaskExecutor taskExecutor = new SyncTaskExecutor(){
{
// 設(shè)置并發(fā)數(shù)
this.setConcurrencyLimit(1);
}
};
@GetMapping(path = "/demo2")
public String demo2()throws Exception{
return taskExecutor.execute(()->{
log.info("ConcurrencyLimitController demo2");
Thread.sleep(1000);
return "demo";
});
}
更多SpringBoot4.0的知識請參考:http://m.fzitv.net/database/350507r03.htm
補充:SpringBoot4.0新特性-Resilience之失敗重試
從Spring7.0版本開始,Spring Framework核心模塊已經(jīng)集成了常用Resilience相關(guān)功能,主要包括了對方法調(diào)用的失敗重試支持以及并發(fā)控制支持。SpringBoot 4.0默認(rèn)的Spring的版本就是Spring 7.0, 因此SpringBoot 4.0自然也就具有了這些特性。本文先來學(xué)習(xí)下失敗重試,下一篇再來學(xué)習(xí)并發(fā)控制。
注解式失敗重試@Retryable
@Retryable 注解可以加在單個方法上,設(shè)置方法級別的重試,也可以加在類上,給類中所有的通過代理調(diào)用的方法統(tǒng)一指定重試特性,使用起來非常簡單,僅需2步:
- 在啟動類添加
@EnableResilientMethods
@EnableResilientMethods</code><code>@SpringBootApplication</code><code>public class Springboot4Application {}- 在業(yè)務(wù)類或者方法上添加
@Retryable
@Retryable</code><code>@GetMapping(path = "/demo")</code><code>public String demo(){</code><code> log.info("RetryController demo");</code><code> int a = 1/0;</code><code> return "demo";</code><code>}默認(rèn)情況下,當(dāng)方法調(diào)用拋出任何異常時都將觸發(fā)重試,初始失敗后最多進(jìn)行 3 次重試嘗試(maxRetries = 3),且每次重試之間間隔 1 秒延遲。因此,如果業(yè)務(wù)方法一直失敗的話,最多會被調(diào)用1(初始調(diào)用)+ 3(最多重試)=4次。
自定義重試次數(shù)和間隔
@Retryable包含以下屬性可以用來設(shè)置重試的次數(shù)和間隔:
- maxRetries:最大重試次數(shù),不包含初始調(diào)用
- maxDelay:這是任何重試嘗試的最大延遲時長,用于限制 jitter() 和 multiplier() 機制可能增加的延遲上限。
- delay:初次調(diào)用失敗后的基礎(chǔ)延遲時長。若指定了延遲倍數(shù),此值將作為初始延遲基數(shù)進(jìn)行倍增計算。
- multiplier:用于計算下一次重試延遲的倍數(shù),該倍數(shù)將應(yīng)用于前一次的延遲時間(起始值為 delay() 設(shè)置的基礎(chǔ)延遲)以及每次重試所適用的抖動時間。
- jitter:基礎(chǔ)重試嘗試的抖動值,會以隨機加減的方式作用于計算出的延遲時間,從而產(chǎn)生一個介于 delay - jitter 到 delay + jitter 之間的值,但該值永遠(yuǎn)不會低于基礎(chǔ) delay() 或高于 maxDelay()。如果指定了延遲倍數(shù),抖動值也將按此倍數(shù)進(jìn)行縮放。
舉個例子說明一下:
@Retryable(
maxRetries = 3,
delay = 1000,
maxDelay = 5000,
multiplier = 3,
jitter = 100
)日志如下:???????
2026-01-22T15:37:13 INFO -- [8080-exec-1] RetryController : RetryController demo 2026-01-22T15:37:14 INFO -- [8080-exec-1] RetryController : RetryController demo 2026-01-22T15:37:17 INFO -- [8080-exec-1] RetryController : RetryController demo 2026-01-22T15:37:22 INFO -- [8080-exec-1] RetryController : RetryController demo 2026-01-22T15:37:22 ERROR -- [8080-exec-1] [dispatcherServlet]: Servlet.service() for...
13秒初次調(diào)用,delay1秒以后,14秒發(fā)起第一次重試,重試失敗,發(fā)起第二次重試,需要延遲multiplier 3* delay1 = 3秒,此時的delay就變成了3秒,因此17秒發(fā)起第二次重試,還是失敗,需要延遲multiplier(3) * delay(3)= 9秒,但是maxDelay是5秒,因此實際重試發(fā)生在17+5=22秒。重試次數(shù)耗盡還是失敗,最后拋出異常。在此過程中,請求會一直阻塞,直到拋出異常,總耗時超過8秒。
自定義觸發(fā)重試的異常類型
默認(rèn)情況下,任何類型的異常都會觸發(fā)重試,可以通過以下屬性來限定需觸發(fā)重試的異常類型。
- includes:用于指定應(yīng)觸發(fā)重試的異常類型。
- excludes:用于指定不應(yīng)觸發(fā)重試的異常類型,優(yōu)先級更高。
- predicate:自定義
MethodRetryPredicate, 優(yōu)先級最低。
比如下面的案例將不會觸發(fā)重試:???????
@Retryable(
includes = RuntimeException.class,
excludes = ArithmeticException.class
)
@GetMapping(path = "/demo")
public String demo(){
log.info("RetryController demo");
int a = 1/0;
return "demo";
}編程式失敗重試RetryTemplate
與 @Retryable不同,RetryTemplate 提供了一組API,可用于對任意代碼塊進(jìn)行重試操作。當(dāng)然此時就不再需要@EnableResilientMethods注解了,比如:???????
@GetMapping(path = "/demo2")
public String demo2() throws Exception{
RetryTemplate retryTemplate = new RetryTemplate();
return retryTemplate.execute(()->{
log.info("RetryController demo2");
int a = 1/0;
return "demo2";
});
}RetryTemplate 會根據(jù)配置的 RetryPolicy 來執(zhí)行并可能重試一個可重試的操作,默認(rèn)的 RetryPolicy與@Retryable的行為一致,當(dāng)方法調(diào)用拋出任何異常時都將觸發(fā)重試,初始失敗后最多進(jìn)行 3 次重試嘗試(maxRetries = 3),且每次重試之間間隔 1 秒延遲。
自定義重試行為
可以自定義RetryPolicy對重試行為進(jìn)行自定義,比如:?????????????
@GetMapping(path = "/demo2")
public String demo2() throws Exception{
var retryPolicy = RetryPolicy.builder()
.maxRetries(3)
.delay(Duration.ofMillis(1000))
.maxDelay(Duration.ofMillis(5000))
.multiplier(3)
.jitter(Duration.ofMillis(100))
.includes(RuntimeException.class)
.excludes(ArithmeticException.class)
.build();
RetryTemplate retryTemplate = new RetryTemplate(retryPolicy);
return retryTemplate.execute(()->{
log.info("RetryController demo2");
int a = 1/0;
return "demo2";
});
}到此這篇關(guān)于SpringBoot4.0新特性Resilience之并發(fā)限制詳解的文章就介紹到這了,更多相關(guān)SpringBoot Resilience并發(fā)限制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot動態(tài)導(dǎo)出word文檔實整教程(復(fù)制即可使用)
在我們做項目的時候會需要把數(shù)據(jù)庫中的數(shù)據(jù)導(dǎo)出到word當(dāng)中,下面這篇文章主要給大家介紹了關(guān)于SpringBoot動態(tài)導(dǎo)出word文檔實整教程的相關(guān)資料,文中的代碼復(fù)制即可使用,需要的朋友可以參考下2023-06-06
SpringBoot使用?Sleuth?進(jìn)行分布式跟蹤的過程分析
Spring Boot Sleuth是一個分布式跟蹤解決方案,它可以幫助您在分布式系統(tǒng)中跟蹤請求并分析性能問題,Spring Boot Sleuth是Spring Cloud的一部分,它提供了分布式跟蹤的功能,本文將介紹如何在Spring Boot應(yīng)用程序中使用Sleuth進(jìn)行分布式跟蹤,感興趣的朋友一起看看吧2023-10-10
Java springboot里注解大全和使用指南(最新整理)
在Java Spring Boot中,注解是簡化開發(fā)、提高效率的關(guān)鍵工具,這篇文章給大家介紹Java springboot里注解大全和使用指南,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2026-03-03
Mybatis批量插入數(shù)據(jù)的兩種方式總結(jié)與對比
批量插入功能是我們?nèi)粘9ぷ髦斜容^常見的業(yè)務(wù)功能之一,下面這篇文章主要給大家介紹了關(guān)于Mybatis批量插入數(shù)據(jù)的兩種方式總結(jié)與對比的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01

