Spring MVC - DeferredResult 以及自己實現一個DeferredResult

詳細介紹請看Spring DeferredResult 如何實現非同步化,從而大大增加長連線數量。

DeferredResult

provides an alternative to using a

Callable

for asynchronous request processing。 While a

Callable

is executed concurrently on

behalf of

(代表) the application, with a

DeferredResult

the application can produce the result from a thread of its choice。

DeferredResult

提供了一種使用

Callable

介面的方式來實現非同步請求 。 雖然

Callable

是代表應用程式併發執行的,但是使用

DeferredResultapplication

可以得到結果從它執行的執行緒中。

Prepare to using the DeferredResult application

// 我目前捕捉不到 他的onError()事件

@Slf4j@CrossOrigin@RestController@SpringBootApplicationpublic class DeferredresultSpringmvcApplication { public static void main(String[] args) { SpringApplication。run(DeferredresultSpringmvcApplication。class, args); } private static ExecutorService executors = Executors。newFixedThreadPool(10); @RequestMapping(“/get0”) public DeferredResult> getData() { log。info(“controller 呼叫的 thread : {}”,Thread。currentThread()。getName()); log。info(“controller 開始執行 ”); // 設定超時時間 ,其實只有非同步有效 我測試了 ,同步根本不會執行 DeferredResult> result = new DeferredResult<>(1000L); result。setResult(getMap()); //如果不是執行緒執行 , 則返回true executors。execute(new Runnable() { @Override public void run() { // 結果是處理是否成功 有返回結果直接返回 success , 如果 非同步請求已過期(必須是非同步處理 才會生效) 返回false boolean isok = false; isok = result。setResult(getMap()); log。info(“DeferredResult 處理是否成功 : {}”, isok); // 成功以後的處理結果 (必須 放到 呼叫的 Runnable接口裡) result。setResultHandler(new DeferredResult。DeferredResultHandler() { @Override public void handleResult(Object result) { log。info(“DeferredResult 處理結果 : {} ”,result。toString()); } }); } }); // 我這裡有問題 // 我呼叫執行緒異常全部都是在 超時裡面 // 處理異常 , 我捕捉不到這個 異常 ,不知道 哪個地方的 , 其實可以自定義異常 ,然後 根據異常型別進行判斷 result。onError((e) -> { log。info(“onError”); }); // 完成執行 , 處理結果不管是啥 都會執行 result。onCompletion(new Runnable() { @Override public void run() { log。info(“處理 onCompletion”); } }); // 處理超時的處理 , 只有非同步結果才會處理 result。onTimeout(new Runnable() { @Override public void run() { log。info(“處理超時 onTimeout”); } }); log。info(“controller 執行完畢 ”); // 記住 只能返回 result , 不能返回null return result; } /** * 不用執行緒池執行 * @return */ @RequestMapping(“/get1”) public DeferredResult> getByNoExecutor(){ log。info(“controller 呼叫的 thread : {}”,Thread。currentThread()。getName()); log。info(“controller 開始執行 ”); DeferredResult> result = new DeferredResult<>(3000L); result。setResult(getMap()); log。info(“controller 執行完畢 ”); return result; } /** * 用執行緒池執行 * @return */ @RequestMapping(“/get2”) public DeferredResult> getByExecutor(){ log。info(“controller 呼叫的 thread : {}”,Thread。currentThread()。getName()); log。info(“controller 開始執行 ”); DeferredResult> result = new DeferredResult<>(3000L); executors。execute(new Runnable() { @Override public void run() { result。setResult(getMap()); } }); log。info(“controller 執行完畢 ”); return result; } /** * 模擬service 服務 * @return */ public HashMap getMap(){ log。info(“service 開始執行 ”); // 模擬 service 延遲服務 HashMap map = new HashMap<>(); try { Thread。sleep(2000);// int i = 1/0; map。put(“key”,Thread。currentThread()。getName()); log。info(“service 服務呼叫的執行緒 : {} ”,Thread。currentThread()。getName()); } catch (Exception e) { throw new RuntimeException(); } log。info(“service 執行完畢 ”); return map; }}

我們看列印結果 發現

1。 get1 執行結果2019-10-12 12:44:17。549 INFO 2020 ——- [nio-8080-exec-2] c。s。DeferredresultSpringmvcApplication : controller 呼叫的 thread : http-nio-8080-exec-22019-10-12 12:44:17。549 INFO 2020 ——- [nio-8080-exec-2] c。s。DeferredresultSpringmvcApplication : controller 開始執行 2019-10-12 12:44:17。549 INFO 2020 ——- [nio-8080-exec-2] c。s。DeferredresultSpringmvcApplication : service 開始執行 2019-10-12 12:44:19。549 INFO 2020 ——- [nio-8080-exec-2] c。s。DeferredresultSpringmvcApplication : service 服務呼叫的執行緒 : http-nio-8080-exec-2 2019-10-12 12:44:19。549 INFO 2020 ——- [nio-8080-exec-2] c。s。DeferredresultSpringmvcApplication : service 執行完畢 2019-10-12 12:44:19。549 INFO 2020 ——- [nio-8080-exec-2] c。s。DeferredresultSpringmvcApplication : controller 執行完畢 2。 get2執行結果2019-10-12 12:44:47。993 INFO 2020 ——- [nio-8080-exec-4] c。s。DeferredresultSpringmvcApplication : controller 呼叫的 thread : http-nio-8080-exec-42019-10-12 12:44:47。993 INFO 2020 ——- [nio-8080-exec-4] c。s。DeferredresultSpringmvcApplication : controller 開始執行 2019-10-12 12:44:47。994 INFO 2020 ——- [nio-8080-exec-4] c。s。DeferredresultSpringmvcApplication : controller 執行完畢 2019-10-12 12:44:47。997 INFO 2020 ——- [pool-1-thread-1] c。s。DeferredresultSpringmvcApplication : service 開始執行 2019-10-12 12:44:49。999 INFO 2020 ——- [pool-1-thread-1] c。s。DeferredresultSpringmvcApplication : service 服務呼叫的執行緒 : pool-1-thread-1 2019-10-12 12:44:49。999 INFO 2020 ——- [pool-1-thread-1] c。s。DeferredresultSpringmvcApplication : service 執行完畢

那我們如何自己實現一個呢 ?

/** * 自定義簡單使用 * @return */ @RequestMapping(“/get3”) public MyCallable> get(){ log。info(“controller 呼叫的 thread : {}”,Thread。currentThread()。getName()); log。info(“controller 開始執行 ”); MyCallable> objectMyCallable = new MyCallable<>(); log。info(“controller 執行完畢 ”); return objectMyCallable; } /** * 簡單的執行邏輯 * @param */ private class MyCallable implements Callable { @Override public T call() throws Exception { return (T)getMap(); } } /** * 模擬service 服務 * @return */ public HashMap getMap(){ log。info(“service 開始執行 ”); // 模擬 service 延遲服務 HashMap map = new HashMap<>(); try { Thread。sleep(2000);// int i = 1/0; map。put(“key”,Thread。currentThread()。getName()); log。info(“service 服務呼叫的執行緒 : {} ”,Thread。currentThread()。getName()); } catch (Exception e) { throw new RuntimeException(); } log。info(“service 執行完畢 ”); return map; }

讓我們看一下我們實現的 執行結果

2019-10-12 12:42:23。869 INFO 2020 ——- [nio-8080-exec-5] c。s。DeferredresultSpringmvcApplication : controller 呼叫的 thread : http-nio-8080-exec-52019-10-12 12:42:23。870 INFO 2020 ——- [nio-8080-exec-5] c。s。DeferredresultSpringmvcApplication : controller 開始執行 2019-10-12 12:42:23。872 INFO 2020 ——- [nio-8080-exec-5] c。s。DeferredresultSpringmvcApplication : controller 執行完畢 2019-10-12 12:42:23。875 INFO 2020 ——- [ task-1] c。s。DeferredresultSpringmvcApplication : service 開始執行 2019-10-12 12:42:25。875 INFO 2020 ——- [ task-1] c。s。DeferredresultSpringmvcApplication : service 服務呼叫的執行緒 : task-1 2019-10-12 12:42:25。875 INFO 2020 ——- [ task-1] c。s。DeferredresultSpringmvcApplication : service 執行完畢

這裡的task 執行緒是誰呢 ?

ThreadPoolTaskExecutor

是 springboot自帶的預設的 非同步執行緒池 , 我們可以自己設定

@Configurationpublic class AsyncPoolConfig { @Bean public ThreadPoolTaskExecutor asyncThreadPoolTaskExecutor(){ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor。setCorePoolSize(20); executor。setMaxPoolSize(200); executor。setQueueCapacity(25); executor。setKeepAliveSeconds(200); executor。setThreadNamePrefix(“asyncThread”); executor。setWaitForTasksToCompleteOnShutdown(true); executor。setAwaitTerminationSeconds(60); executor。setRejectedExecutionHandler(new ThreadPoolExecutor。CallerRunsPolicy()); executor。initialize(); return executor; }}

// 複雜的呢

/** * 自定義靈活性 增加程式碼的可拓展性,加入執行緒池 * @return */ @RequestMapping(“/get4”) public MyCallable2> get2(){ log。info(“controller 呼叫的 thread : {}”,Thread。currentThread()。getName()); log。info(“controller 開始執行 ”); MyCallable2> objectMyCallable = new MyCallable2<>(new Callable>() { @Override public HashMap call() throws Exception { return getMap(); } }); log。info(“controller 執行完畢 ”); return objectMyCallable; } /** * 這裡 其實 就是 一個 deferred result * @param */ private class MyCallable2 implements Callable { private ExecutorService executors = Executors。newFixedThreadPool(10); private Callable callable; public MyCallable2(Callable callable) { this。callable = callable; } // 讓一個新的執行緒執行 call方法 @Override public T call() throws Exception { Future submit = executors。submit(callable); return submit。get(); } } /** * 模擬service 服務 * @return */ public HashMap getMap(){ log。info(“service 開始執行 ”); // 模擬 service 延遲服務 HashMap map = new HashMap<>(); try { Thread。sleep(2000);// int i = 1/0; map。put(“key”,Thread。currentThread()。getName()); log。info(“service 服務呼叫的執行緒 : {} ”,Thread。currentThread()。getName()); } catch (Exception e) { throw new RuntimeException(); } log。info(“service 執行完畢 ”); return map; }

執行結果

2019-10-12 12:42:44。879 INFO 2020 ——- [nio-8080-exec-8] c。s。DeferredresultSpringmvcApplication : controller 呼叫的 thread : http-nio-8080-exec-82019-10-12 12:42:44。880 INFO 2020 ——- [nio-8080-exec-8] c。s。DeferredresultSpringmvcApplication : controller 開始執行 2019-10-12 12:42:44。882 INFO 2020 ——- [nio-8080-exec-8] c。s。DeferredresultSpringmvcApplication : controller 執行完畢 2019-10-12 12:42:44。883 INFO 2020 ——- [pool-2-thread-1] c。s。DeferredresultSpringmvcApplication : service 開始執行 2019-10-12 12:42:46。884 INFO 2020 ——- [pool-2-thread-1] c。s。DeferredresultSpringmvcApplication : service 服務呼叫的執行緒 : pool-2-thread-1 2019-10-12 12:42:46。884 INFO 2020 ——- [pool-2-thread-1] c。s。DeferredresultSpringmvcApplication : service 執行完畢

​ 感覺我寫的很詳細 , 其他的就不多說了 , 其實他的應用場景就是 當一個處理請求消耗很長的時間時 , 需要去使用這個 , 主要就是採用發揮一個非同步處理的執行緒事件, 不是返回一個結果 , 這點要明白