1. 程式人生 > >java訊息佇列理解

java訊息佇列理解

今天看到我們的招聘資訊有對訊息佇列有要求,然後就思索了一翻,網上一搜一大堆。

訊息佇列,顧名思義 首先是個佇列。
佇列的操作有入隊出隊

也就是你有一個程式在產生內容然後入隊(生產者) 另一個程式讀取內容,內容出隊(消費者)

這是最最基本的概念。

我想你應該是缺乏一個使用場景。

當你不需要立即獲得結果,但是併發量又不能無限大的時候,差不多就是你需要使用訊息佇列的時候。

比如你寫日誌,因為可能一個客戶端有多個操作去寫,又有很多個客戶端,顯然併發不能無窮大,於是你就需要把寫日誌的請求放入到訊息佇列裡,在消費者那邊依次把佇列中產生的日誌寫到資料庫裡。

至於怎麼實現訊息佇列,其實你本身一個普通的佇列就行呀~看你需要什麼附加功能而已。


我可以舉個小例子先說明應用場景

假設你的伺服器每分鐘的處理量為200個,但客戶端再峰值的時候可能一分鐘會發1000個訊息給你,這時候你就可以把他做成佇列,然後按正常有序的處理,先進後出(LIFO),先進先出(FIFO)可根據自己的情況進行定奪

stack  先進後出(LIFO)--------Java 對應的類 Stack

佇列 先進先出(FIFO)--------java對應的類Queue

這兩種都可用Linkedlist進行封裝和實現,下面是我自己寫的一個棧的例子

  1. /** 
  2.  * @author 劉伊凡 
  3.  * --------->>>>>>佇列的實現--------------
     
  4.  */
  5. publicclass MyStack<T> {  
  6.     private LinkedList<T> storage = new LinkedList<T>();  
  7.     publicsynchronizedvoid push(T e) {//需要加上同步
  8.         storage.addFirst(e);  
  9.     }  
  10.     public T peek() {  
  11.         return storage.getFirst();  
  12.     }  
  13.     publicvoid pop() {  
  14.         storage.removeFirst();  
  15.     }  
  16.     publicboolean empty() {  
  17.         return storage.isEmpty();  
  18.     }  
  19.     @Override
  20.     public String toString() {  
  21.         return storage.toString();  
  22.     }  
  23. }  

下面是一個測試類
  1. /** 
  2.  * @author 劉伊凡 
  3.  * 
  4.  */
  5. publicclass StackTest {  
  6.     publicstaticvoid main(String[] args) {  
  7.         MyStack<String> stack = new MyStack<String>();  
  8.         for(String s : "the prefect code".split(" ")){//LIFO
  9.             stack.push(s);  
  10.         }  
  11.         while(!stack.empty()){  
  12.             System.out.print(stack.peek()+" ");  
  13.             stack.pop();  
  14.         }  
  15.         System.out.println();  
  16.         for(char s : "寫了個一句話倒起來說的程式".toCharArray()){//用例:正話反說
  17.             stack.push(String.valueOf(s));  
  18.         }  
  19.         while(!stack.empty()){  
  20.             System.out.print(stack.peek());  
  21.             stack.pop();  
  22.         }  
  23.     }  
  24. }  

挺有意思的,讓我想了,以前在學校的晚會上,主持人互動的時候會讓人上臺去答題拿獎品,其中有一個題目就是主持人說一句話,然後要求選手倒起來說,我們的這個程式很符合需求嘛,哈哈,我們可以用java來作弊,學以致用

訊息佇列的應用場景,補充(來自網際網路)

個人認為訊息佇列的主要特點是非同步處理,主要目的是減少請求響應時間和解耦。所以主要的使用場景就是將比較耗時而且不需要即時(同步)返回結果的操作作為訊息放入訊息佇列。同時由於使用了訊息佇列,只要保證訊息格式不變,訊息的傳送方和接收方並不需要彼此聯絡,也不需要受對方的影響,即解耦和。

使用場景的話,舉個例子:
假設使用者在你的軟體中註冊,服務端收到使用者的註冊請求後,它會做這些操作:
  1. 校驗使用者名稱等資訊,如果沒問題會在資料庫中新增一個使用者記錄
  2. 如果是用郵箱註冊會給你傳送一封註冊成功的郵件,手機註冊則會發送一條簡訊
  3. 分析使用者的個人資訊,以便將來向他推薦一些志同道合的人,或向那些人推薦他
  4. 傳送給使用者一個包含操作指南的系統通知
  5. 等等……
但是對於使用者來說,註冊功能實際只需要第一步,只要服務端將他的賬戶資訊存到資料庫中他便可以登入上去做他想做的事情了。至於其他的事情,非要在這一次請求中全部完成麼?值得使用者浪費時間等你處理這些對他來說無關緊要的事情麼?所以實際當第一步做完後,服務端就可以把其他的操作放入對應的訊息佇列中然後馬上返回使用者結果,由訊息佇列非同步的進行這些操作。

或者還有一種情況,同時有大量使用者註冊你的軟體,再高併發情況下注冊請求開始出現一些問題,例如郵件介面承受不住,或是分析資訊時的大量計算使cpu滿載,這將會出現雖然使用者資料記錄很快的新增到資料庫中了,但是卻卡在發郵件或分析資訊時的情況,導致請求的響應時間大幅增長,甚至出現超時,這就有點不划算了。面對這種情況一般也是將這些操作放入訊息佇列(生產者消費者模型),訊息佇列慢慢的進行處理,同時可以很快的完成註冊請求,不會影響使用者使用其他功能。

所以在軟體的正常功能開發中,並不需要去刻意的尋找訊息佇列的使用場景,而是當出現效能瓶頸時,去檢視業務邏輯是否存在可以非同步處理的耗時操作,如果存在的話便可以引入訊息佇列來解決。否則盲目的使用訊息佇列可能會增加維護和開發的成本卻無法得到可觀的效能提升,那就得不償失了。