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

快速掌握Spring異步請求接口,輕松解決并發(fā)問題

開發(fā) 前端
異步請求接口是解決并發(fā)問題的有效方法之一,特別是在高并發(fā)、耗時操作、實時數(shù)據(jù)處理等場景中具有顯著優(yōu)勢。通過異步請求,系統(tǒng)能夠避免阻塞線程,提高系統(tǒng)的響應性能和資源利用率。

環(huán)境:SpringBoot2.7.12


1. 概述

      在現(xiàn)代的互聯(lián)網(wǎng)應用中,隨著用戶數(shù)量的不斷增加和業(yè)務復雜性的提升,并發(fā)問題成為了開發(fā)中面臨的重大挑戰(zhàn)。傳統(tǒng)的同步請求接口往往無法滿足高并發(fā)場景的需要,不僅會阻塞調(diào)用線程,影響系統(tǒng)的響應性能,而且還可能導致線程資源的浪費。為了解決這些問題,異步請求接口逐漸成為了開發(fā)者的首選。

在SpringBoot框架中,異步請求接口的創(chuàng)建和使用非常方便,能夠讓你輕松解決并發(fā)問題,提高系統(tǒng)的可維護性和響應性能。本文將介紹如何快速掌握SpringBoot異步請求接口,以輕松解決并發(fā)問題。

2. 異步請求接口優(yōu)勢

異步請求接口相比于傳統(tǒng)同步請求具有以下優(yōu)勢:

  • 非阻塞性:異步請求不會阻塞調(diào)用線程,而是在請求處理過程中繼續(xù)執(zhí)行其他任務或操作,從而提高了系統(tǒng)的并發(fā)性能和響應速度。
  • 可用資源:異步請求可以釋放占用的線程等資源,避免阻塞,等到結(jié)果產(chǎn)生再重新獲取線程處理。這樣能夠節(jié)省資源,提高系統(tǒng)的資源利用率。
  • 解耦:異步請求可以減少系統(tǒng)間的耦合度,使得不同的系統(tǒng)或服務之間可以更好地協(xié)同工作。因為請求處理過程中不會阻塞其他操作,可以更容易地實現(xiàn)分布式系統(tǒng)的調(diào)用。
  • 異常處理:異步請求可以更容易地捕獲、處理異常,因為請求處理的結(jié)果是在另一個線程中返回的,不會影響到主線程的執(zhí)行流程。
  • 控制流程:異步請求可以通過回調(diào)函數(shù)或者Promise等方式來控制流程,使得流程更加靈活和可擴展。
  • 總之,異步請求接口具有上述優(yōu)勢,尤其是在高并發(fā)場景下,能夠提高系統(tǒng)的性能和可用性,是解決并發(fā)問題的有效方法之一。

3. 應用場景

異步請求接口可以應用于以下場景:

  • 高并發(fā)場景:在面對大量用戶請求時,異步請求接口能夠避免線程阻塞,提高系統(tǒng)的并發(fā)處理能力,減少等待時間,提升用戶體驗。
  • 耗時操作處理:當需要進行一些耗時的操作,如網(wǎng)絡請求、IO磁盤等操作時,采用異步請求接口可以避免阻塞主線程,提高系統(tǒng)的響應性能。
  • 實時數(shù)據(jù)處理:對于實時性要求較高的應用,如實時數(shù)據(jù)流處理、實時股票交易等場景,采用異步請求接口能夠快速處理數(shù)據(jù),并實時反饋結(jié)果,提高系統(tǒng)的實時性。
  • 異步業(yè)務流程:在某些業(yè)務流程中,涉及到多個異步任務,這些任務之間沒有依賴關(guān)系,可以采用異步請求接口進行實現(xiàn),提高系統(tǒng)的并發(fā)性和響應性能。
  • API接口的調(diào)用:當需要對其他API接口進行調(diào)用時,特別是對于一些耗時較長的接口,采用異步請求接口可以提高系統(tǒng)的響應性能和資源利用率。

總之,異步請求接口適用于那些需要避免阻塞、提高系統(tǒng)響應性能、處理耗時操作和實時數(shù)據(jù)處理等場景中,能夠提高系統(tǒng)的并發(fā)性能和資源利用率,減少系統(tǒng)瓶頸的出現(xiàn)。

接下來我們進入正文,在Spring環(huán)境下如何將我們的接口異步化。

4. 實戰(zhàn)異步接口

Spring MVC 廣泛集成了 Servlet 3.0 異步請求處理功能:

  • Controller方法中的 DeferredResult 和 Callable 返回值為單個異步返回值提供了基本支持。
  • Controller可以流式傳輸多個值,包括 SSE 和原始數(shù)據(jù)。
  • Controller可以使用反應式客戶端和返回反應式類型來處理響應。

4.1 DeferredResult

一旦在 Servlet 容器中啟用異步請求處理功能,控制器方法就可以用 DeferredResult 封裝任何受支持的控制器方法返回值,如下例所示:

@GetMapping("/deferred")
@ResponseBody
public DeferredResult<Map<String, Object>> deferred(){
  long start = System.currentTimeMillis() ;
  System.out.printf("%s - 開始時間:%d%n", Thread.currentThread().getName(), start) ;
  DeferredResult<Map<String, Object>> deferredResult = new DeferredResult<>();
  // 為了演示方便直觀,這里直接創(chuàng)建線程
  new Thread(() -> {
    try {
      // 這里模擬耗時操作
      TimeUnit.SECONDS.sleep(3) ;
      // 將執(zhí)行結(jié)果保存
      Map<String, Object> result = new HashMap<>() ;
      result.put("code", 1) ;
      result.put("data", "你的業(yè)務數(shù)據(jù)") ;
      deferredResult.setResult(result) ;
    } catch (InterruptedException e) {}
  }).start() ;
  long end = System.currentTimeMillis() ;
  System.out.printf("%s - 結(jié)束時間:%d%n", Thread.currentThread().getName(), end) ;
  System.out.printf("總耗時:%d毫秒%n", (end - start)) ;
  return deferredResult ;
}

控制臺輸出結(jié)果:

2023-10-19 14:25:30.321  INFO 3884 --- [nio-8808-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
http-nio-8808-exec-1 - 開始時間:1697696730335
http-nio-8808-exec-1 - 結(jié)束時間:1697696730335
總耗時:0毫秒

從結(jié)果看出,處理請求的tomcat線程幾乎沒有占用時間,線程被快速的釋放,這樣就可以去處理其它的連接請求,整個系統(tǒng)的吞吐量就的到了明顯的提升。

Controller可以從不同的線程異步生成返回值。

4.2 Callable

Controller可以用 java.util.concurrent.Callable 封裝任何受支持的返回值,如下例所示:

@GetMapping("/callable")
public Callable<Map<String, Object>> callable() {
  long start = System.currentTimeMillis() ;
  System.out.printf("%s - 開始時間:%d%n", Thread.currentThread().getName(), start) ;
  Callable<Map<String, Object>> callable  = new Callable<Map<String, Object>>() {
    public Map<String, Object> call() throws Exception {
      Map<String, Object> result = new HashMap<>() ;
      try {
        // 這里模擬耗時操作
        TimeUnit.SECONDS.sleep(3) ;
        // 將執(zhí)行結(jié)果保存
        result.put("code", 1) ;
        result.put("data", "你的業(yè)務數(shù)據(jù)") ;
      } catch (InterruptedException e) {}
      return result ;
    }
  } ;
  long end = System.currentTimeMillis() ;
  System.out.printf("%s - 結(jié)束時間:%d%n", Thread.currentThread().getName(), end) ;
  System.out.printf("總耗時:%d毫秒%n", (end - start)) ;
  return callable ;
}

控制臺輸出結(jié)果:

http-nio-8808-exec-2 - 開始時間:1697697345385
http-nio-8808-exec-2 - 結(jié)束時間:1697697345386
總耗時:1毫秒

執(zhí)行結(jié)果與上面一樣。
注意:這里Callable中的代碼執(zhí)行是在系統(tǒng)默認的一個TaskExecutor線程池中運行,我們可以通過配置自己的TaskExecutor來執(zhí)行。如下:

@Bean
public ThreadPoolTaskExecutor myAsyncTaskExecutor() {
  // 配置ThreadPoolTaskExecutor相關(guān)參數(shù),比如核心線程數(shù)等
  return ... ;
}

4.3 ResponseBodyEmitter

你可以將 DeferredResult 和 Callable 用于單個異步返回值。如果要生成多個異步值并將其寫入響應,該怎么辦?本節(jié)將介紹如何做到這一點。

ResponseBodyEmitter 的返回值生成一個對象流,其中每個對象都會被 HttpMessageConverter 序列化并寫入響應,如下例所示:

// 這里應該保存到一個集合中
private ResponseBodyEmitter emitter ;
@GetMapping("/emitter")
public ResponseBodyEmitter emitter() throws Exception {
  ResponseBodyEmitter bodyEmitter = new ResponseBodyEmitter(-1L);
  this.emitter = bodyEmitter ;
  return bodyEmitter;
}
// 可以不斷調(diào)用該接口進行消息的發(fā)送
@GetMapping("/sender")
public void sender() throws Exception {
  this.emitter.send(System.currentTimeMillis()) ;
}
// 調(diào)用該j接口后請求結(jié)束
@GetMapping("/complete")
public void complete() throws Exception {
  this.emitter.complete() ;
}

當訪問/emitter接口時,瀏覽器會一直轉(zhuǎn)圈,一直等待。只有調(diào)用了/complete接口后請求內(nèi)容才會被發(fā)送到客戶端并結(jié)束請求。

4.4 StreamingResponseBody

有時,繞過消息轉(zhuǎn)換并直接流式傳輸?shù)巾憫?OutputStream(例如,文件下載)非常有用。為此,可以使用 StreamingResponseBody 返回值類型,如下例所示:

@GetMapping("/stream")
public ResponseEntity<StreamingResponseBody> stream() {
  long start = System.currentTimeMillis() ;
  System.out.printf("%s - 開始時間:%d%n", Thread.currentThread().getName(), start) ;
  // 內(nèi)部執(zhí)行還是用的系統(tǒng)內(nèi)部的線程池
  StreamingResponseBody stream = new StreamingResponseBody() {
    @Override
    public void writeTo(OutputStream outputStream) throws IOException {
      outputStream.write(String.valueOf("當前時間: " + System.currentTimeMillis() + "<br/>").getBytes()) ;
      try {
        TimeUnit.SECONDS.sleep(1) ;
      } catch (InterruptedException e) {}
      outputStream.write(String.valueOf("當前時間: " + System.currentTimeMillis() + "<br/>").getBytes()) ;
      try {
        TimeUnit.SECONDS.sleep(1) ;
      } catch (InterruptedException e) {}
      outputStream.write(String.valueOf("當前時間: " + System.currentTimeMillis() + "<br/>").getBytes()) ;
    }
  };
  MultiValueMap<String, String> headers = new HttpHeaders() ;
  headers.add("Content-Type", "text/html;charset=UTF-8") ;
  ResponseEntity<StreamingResponseBody> response = new ResponseEntity<StreamingResponseBody>(stream, headers , HttpStatus.OK) ;


  long end = System.currentTimeMillis() ;
  System.out.printf("%s - 結(jié)束時間:%d%n", Thread.currentThread().getName(), end) ;
  System.out.printf("總耗時:%d毫秒%n", (end - start)) ;
  return response ;
}

控制臺輸出:

http-nio-8808-exec-1 - 開始時間:1697700256912
http-nio-8808-exec-1 - 結(jié)束時間:1697700256915
總耗時:3毫秒

tomcat線程非常短的時間內(nèi)釋放,這樣就可以處理更多的請求,提升系統(tǒng)整體的吞吐量。這種最適合文件下載。

瀏覽器輸出:

圖片

4.5 SSE

該方式請閱讀《實時數(shù)據(jù)推送并非只有WebSocket一種選擇》詳細介紹了如何使用。

4.6 基于反應式

Spring MVC 支持在控制器中使用反應式客戶端庫。這包括 spring-webflux 中的 WebClient 以及 Spring Data 反應式數(shù)據(jù)存儲庫等其他庫。在這種情況下,從控制器方法中返回反應類型是很方便的。如下例所示:

@GetMapping("/mono")
public Mono<Map<String, Object>> mono() {
  long start = System.currentTimeMillis() ;
  System.out.printf("%s - 開始時間:%d%n", Thread.currentThread().getName(), start) ;
  Mono<Map<String, Object>> mono = Mono.defer(() -> {
    try {
      TimeUnit.SECONDS.sleep(3) ;
    } catch (InterruptedException e) {}
    Map<String, Object> result = new HashMap<>() ;
    result.put("code", 1) ;
    result.put("data", "你的業(yè)務數(shù)據(jù)") ;
    return Mono.just(result) ;
  }) ;
  long end = System.currentTimeMillis() ;
  System.out.printf("%s - 結(jié)束時間:%d%n", Thread.currentThread().getName(), end) ;
  System.out.printf("總耗時:%d毫秒%n", (end - start)) ;
  return mono ;
}

控制臺輸出:

http-nio-8808-exec-2 - 開始時間:1697700686250
http-nio-8808-exec-2 - 結(jié)束時間:1697700686251
總耗時:1毫秒


以上就是Spring中異步請求接口的實現(xiàn)方式。

異步請求接口是解決并發(fā)問題的有效方法之一,特別是在高并發(fā)、耗時操作、實時數(shù)據(jù)處理等場景中具有顯著優(yōu)勢。通過異步請求,系統(tǒng)能夠避免阻塞線程,提高系統(tǒng)的響應性能和資源利用率。

責任編輯:武曉燕 來源: Spring全家桶實戰(zhàn)案例源碼
相關(guān)推薦

2023-10-23 08:12:34

并發(fā)問題有鎖和無鎖

2012-07-17 10:54:49

AJAX

2009-11-25 13:33:39

并發(fā)

2022-09-13 13:49:05

數(shù)據(jù)庫隔離

2012-02-02 15:57:09

HibernateJava

2024-01-31 08:50:41

Guava并發(fā)工具

2022-12-27 11:06:35

海量接口并發(fā)

2009-06-29 09:38:50

JSF標簽JSF

2020-07-07 07:47:07

Java無鎖技術(shù)

2011-08-03 10:39:06

IOS程序 HTTP 請求

2024-08-13 17:35:27

2021-08-10 07:00:01

Redis單線程并發(fā)

2024-04-16 00:00:00

Spring微服務架構(gòu)

2024-06-12 00:00:01

Java函數(shù)式接口

2022-04-02 20:27:30

ETS操作系統(tǒng)鴻蒙

2024-03-28 08:41:10

高并發(fā).NET異步編程

2021-06-03 14:08:03

開發(fā)技能代碼

2021-02-26 13:50:37

Java并發(fā)代碼

2021-06-04 14:28:07

協(xié)程線程Android開發(fā)

2024-06-03 00:00:01

點贊
收藏

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