SpringBoot配置異步任務
有些業務是不需要你同步去操作的, 例如: 適用於處理log、發送郵件、短信……等
我們不能因為短信沒發出去而沒有執行接下來的業務邏輯, 這個時候我們就應該去把這些耗時的任務弄成異步的
首先要在啟動類里面增加如下注解
@EnableAsync
定義異步任務類並使用@Component標記組件被容器掃描,異步方法加上@Async
如果整個類的操作都是異步的話 @Async 可以給類加上, 要把異步任務封裝到類里面,不能直接寫到Controller
package com.cj.tool.comtool.controller; import com.cj.tool.comtool.task.AsyncTask; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestTaskController { @Autowired private AsyncTask asyncTask; // 直接調起異步任務。正常執行肯定是堵塞的 @GetMapping("/api/v1/test_task") public long testTask() throws InterruptedException { long begin = System.currentTimeMillis(); asyncTask.task1(); asyncTask.task2(); asyncTask.task3(); long end = System.currentTimeMillis(); System.out.println("Controller 執行時間" + (end - begin)); return end - begin; } }
// 直接調起異步任務。正常執行肯定是堵塞的
package com.cj.tool.comtool.task; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component @Async public class AsyncTask { public void task1() throws InterruptedException { long begin = System.currentTimeMillis(); Thread.sleep(1000); long end = System.currentTimeMillis(); System.out.println("task1耗時:"+ (end - begin)); } public void task2() throws InterruptedException { long begin = System.currentTimeMillis(); Thread.sleep(2000); long end = System.currentTimeMillis(); System.out.println("task2耗時:"+ (end - begin)); } public void task3() throws InterruptedException { long begin = System.currentTimeMillis(); Thread.sleep(3000); long end = System.currentTimeMillis(); System.out.println("task3耗時:"+ (end - begin)); } }
可以看到在AsyncTask里面都是有sleep的, 但是我們使用了異步
Controller執行時間 是先輸出的, 我們的任務去開另外的線程執行, 這樣大大增加了我們的程序效率, 在項目里面合適使用異步任務, 可以大大提高我們的QPS
獲取異步返回數據
上面例子雖然解決了堵塞的問題, 但是有的時候我們希望獲取異步任務的返回結果, 再進行后續工作。放心 這個也有方案
添加異步返回任務
public Future<String> task4() throws InterruptedException { long begin = System.currentTimeMillis(); Thread.sleep(4000); long end = System.currentTimeMillis(); System.out.println("task4耗時:"+ (end - begin)); return new AsyncResult<>("Task4的數據"); } public Future<Integer> task5() throws InterruptedException { long begin = System.currentTimeMillis(); Thread.sleep(5000); long end = System.currentTimeMillis(); System.out.println("task5耗時:"+ (end - begin)); return new AsyncResult<>(123); } public Future<String> task6() throws InterruptedException { long begin = System.currentTimeMillis(); Thread.sleep(6000); long end = System.currentTimeMillis(); System.out.println("task6耗時:"+ (end - begin)); return new AsyncResult<>("Task6的數據"); }
@GetMapping("/api/v1/test_task") public long testTask() throws InterruptedException, ExecutionException { long begin = System.currentTimeMillis(); // asyncTask.task1(); // asyncTask.task2(); // asyncTask.task3(); Future<String> task4Result = asyncTask.task4(); Future<Integer> task5Result = asyncTask.task5(); Future<String> task6Result = asyncTask.task6(); // 等每個任務執行完了就跳出 for (;;) { if (task4Result.isDone() && task5Result.isDone() && task6Result.isDone()) { break; } } // 獲取返回結果 String task4res = task4Result.get(); int task5res = task5Result.get(); System.out.println(task4res); System.out.println(task5res); long end = System.currentTimeMillis(); System.out.println("Controller 執行時間" + (end - begin)); return end - begin; }
說一下流程
1)增加Future<String> 返回結果需呀 new AsyncResult<String>("task執行完成");
2)如果需要拿到結果 需要判斷全部的 task.isDone(), 然后再task.get() 獲取返回數據
效果
可以看到 還是異步的, 最長耗時6000, 這樣就可以應對不同的業務了, 如果是同步的話肯定需要 15000