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

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

  本來想說說檢索的,不過不知道什麼鬼,下載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,可以了,執行應用,測試結果如下:

 

  簡單的看了看非同步任務的用法,應該還是比較容易的,用起來也沒什麼難度,就是真實專案碰到的情況可能就會很複雜,要考慮的情況比較多了。