1. 程式人生 > >帶著新人學springboot的應用09(springboot+異步任務)

帶著新人學springboot的應用09(springboot+異步任務)

time sea 自動 等等 超過 銷毀 訪問 概念 執行c

  本來想說說檢索的,不過不知道什麽鬼,下載ElasticSearch太慢了,還是放一下,後面有機會再補上!今天就說個簡單的東西,來說說任務。

  什麽叫做任務呢?其實就是類中實現了一個什麽功能的方法。常見的任務就是異步任務,定時任務,發郵件。

  異步任務:其實就是一個很特別的方法,這個方法沒有返回值(也可以有返回值,後面會說的),但是方法內部的邏輯會耗費很多時間!例如,用戶請求每次到controller,要執行到這個異步方法的時候,我們只需要命令一個空閑狀態的線程去執行它即可,由於沒有返回值不影響後續代碼的運行,controller直接去執行後續的代碼。這樣可以極為迅速的響應用戶,用戶體驗非常好。

  定時任務:這個其實看名字就知道了,你可以選定一個月的哪一天哪個小時的具體時分秒,去執行一個方法。這個方法是自動執行的,極大的減輕了我們的工作量。

  發郵件:這個還是比較常見的吧!註冊什麽的大多都要郵件激活,我們也可以用java程序的方式,來給你郵箱發郵件。

  提前準備:大概了解一下javase中多線程和線程池概念,重點是ThreadPoolTaskExecutor這個類。

1.簡單說說異步任務和RabbitMQ

  不知道看了我上面說了異步任務,大家有沒有想到前面說過的一個東西,就是RabbitMQ啊!這個消息隊列,不就是也是這樣的嗎?生產者發消息給交換器之後,就不管了,也許生產者就可以直接響應用戶響應成功了。而內部也許還有交換器發消息給隊列,然後消費者消費消息,但這個過程就跟用戶就沒什麽關系了,幹嘛要用戶浪費時間在那瞎等呢?

  就我而言,異步任務和RabbitMQ最大的區別應該是訪問量的差異;可以將異步任務看作是簡化版RabbitMQ 。

  你想啊,假如你做了一個系統,最大訪問量總共那麽點人,你還要去用個RabbitMQ去搞這搞那的,麻不麻煩啊。。。

  但是假如你在一家大公司,做的系統是給幾十萬甚至幾百萬用戶用的,那麽肯定要用RabbitMQ啊。這個時候如果用異步任務,哪有這麽多空閑線程去給你執行這個任務啊,而且線程到了一個數目之後,效率反而是降低的。

  補充小知識:大家知不知道服務器怎麽處理多個請求的啊?

  假如都是同步任務:請用請求一到,服務器就出用一個線程去執行,並且到了service層之後,假設會有個任務需要幾十秒才能執行完畢;那麽執行到這裏就會卡住了,請求到這裏都要卡住幾十秒,人多的時候可能還要排隊,等時間過了你才會得到響應。

  那就等著唄,然而不巧的是,這個時候又有幾千人來來給服務器發請求,於是這幾千人每人等幾十秒,而且由於服務器又比較水,一下子開了幾千個線程還有點卡於是要等更長時間。(咳,所以越貴的服務器配置高,線程能夠開得多,運行速度快,處理多人請求的能力就很牛),於是這幾千人用了這個系統之後,心中大罵日了狗了哦,下次再也不用這個鬼系統了。

  那假如是異步任務:還是用訂單/庫存系統舉例,幾千人都在買買買,一時間幾千個訂單請求到controller,然後調用service(註:service還是跟上面一樣要幾十秒),一到service,判斷是個異步方法,於是趕緊讓處理異步任務的線程過來慢慢處理就好,controller可以直接響應用戶“訂單成功”。用戶極短時間就收到響應,於是可以繼續買買買。

  這裏就要說個東西,那個處理異步方法的線程哪裏來的啊?其實是從異步方法線程池裏拿的。大家應該知道連接池啊,是處理與數據庫連接問題的,還能設置個數,最大超時時間等等,還能防止頻繁的銷毀創建線程的資源消耗。這裏的線程池也差不多,可以設置個數啊什麽的。。。。後面我們就會試試簡單的配置一個連接池專門處理異步任務。

2.簡單使用異步任務

  創建一個簡單的springboot項目,web模塊+1.5.xx版本。

  簡單寫個controller和一個處理邏輯的類,先看看同步處理:

技術分享圖片

技術分享圖片

  ok,你可以測試一下,等5秒鐘瀏覽器才會響應,emmm...瑪德,等得真煩。。。

  測試異步處理,加兩個註解就ok了。

技術分享圖片

技術分享圖片

  然後這次就能不超過一秒鐘,就返回響應成功了,你們也可以自己用System.currentTimeMillis()這個方法去測測時間,這裏就不多說了。

  這裏就需要考慮了,假如訪問量很多,每個請求都過來調用HelloTask方法,那就要拿到相應數量的處理異步的線程,來處理這個HelloTask方法!

  處理異步任務的線程也是屬於服務器的線程啊,線程數太多服務器會很卡的,默認是有線程池的!但是我們根據自己項目的需求,於是要自定義線程池的一些配置。

  假如線程池裏就設置最多30個線程(這裏也會有個類似RabbitMq的簡單消息隊列),那麽即使幾千人來調用HelloTask方法,那麽最多也就拿到30個處理異步的線程,來處理這個方法;至於其他的那麽多請求就丟到請求隊列裏,等這30個線程中某些率先處理完任務的線程就會從這消息隊列中拿任務繼續處理。

3.自定義異步任務的線程池

  剛剛查一下資料,其實也是可以用多線程來做的,只是每次都用多線程比較麻煩,不過小夥伴們可以試試。

  線程池就相當於包裝了一下多線程的操作,做了很多配置!就類似ArrayList和Object數組的關系。

技術分享圖片

  

  在下面方法打印當前線程(註意,這裏註解@Async(“自己配置的線程池的名字”),這裏可以指定配置的哪個線程池,可以自己試試,我們這裏要寫應該是taskExecutor)

技術分享圖片

 然後運行,打開瀏覽器,瘋狂刷新,控制臺打印如下;(註意:可以把線程池中線程數目弄少一點,把sleep時間調整短一點,就能明顯看到線程不會按順序使用了,而是隨機的!這個自己可以測試一下)

技術分享圖片

4.給異步方法設置返回值

  前面說的都是異步方法返回值為void的情況,但是有的時候我們要拿到異步方法的返回值。

  這個邏輯是怎麽回事呢?有了返回值還能異步嗎?答案是可以的。

  舉個例子,當請求到controller,線程就會去執行controller方法,碰到了要執行一個異步方法,於是異步方法立馬返回一個Future對象敷衍controller一下(瑪德,controller真是老實人...),然後繼續執行controller後面的方法,不會停頓;

  這個Future就是異步方法的返回值,只是剛開始還沒有數據,等異步方法執行完畢,自動的就會將數據放入Future,比較常見的是利用Future來捕獲異常。

  其中,Future是個接口,實現類是AsyncResult,下面就看看修改後的controller:

 技術分享圖片

  再看看異步方法內部代碼:

技術分享圖片

  ok,可以了,運行應用,測試結果如下:

技術分享圖片

  簡單的看了看異步任務的用法,應該還是比較容易的,用起來也沒什麽難度,就是真實項目碰到的情況可能就會很復雜,要考慮的情況比較多了。

帶著新人學springboot的應用09(springboot+異步任務)