消息隊列的使用<一>:介紹、使用場景和JMS概念知識

首發時間:2019-05-16java


介紹

  • 消息隊列,也能夠稱爲「消息中間件」(中間件是一種獨立的系統軟件或服務程序,請注意獨立二字,它是一個相似mysql服務端的獨立運行的程序)
  • 消息隊列它自己是一個隊列結構的信息存儲組件,某種程度上它跟redis有點相像,其實主要是用來存儲數據,而它的用途主要環繞「消息」和「隊列」這兩個詞,這將在下面講。
  • 常見的消息隊列有:ActiveMQ,RabbitMQ,kafka。這些消息隊列大多兼容大部分語言。
    • 在java行業,可能ActiveMQ,kafka用的會比較多。
    • 在python行業,可能RabbitMQ可能會比較多。




消息隊列的理解

消息,說明與消息有關;隊列,說明是一個先入先出的結構;
消息隊列能夠用於不一樣應用系統之間的信息交換,這裏所謂的不一樣的應用系統是指具備不一樣功能的應用系統,好比一個商城系統和一個短信系統,這兩個系統具備不一樣的職能。(一般來講,同一職能的平臺須要消息隊列的需求要低,此時消息傳遞功能可使用別的東西來實現)
一個應用往消息隊列中存入數據,而另外一個應用從消息隊列中獲取數據,若是這個數據是有消息傳遞意義的,那這時候他們就完成了數據通訊。特別的是,它們的數據通訊此時是異步的,由於沒有要求數據是何時存入的,因此A存入數據後,B能夠挺久以後再去處理。
此外,因爲是一個隊列,那麼保證了先存進來的消息會被先取出,因此這保證了先進來的消息會被先解析。python




舉個栗子

一個電商平臺可能有發短信的業務需求。在一個普通的架構中,可能會整合了短信接口,那麼直接帶數據給短信接口就能夠發短信了。
mysql

而若是在一個短信業務獨立的架構中(短信接口獨立多是由於有一些別的業務平臺也須要,若是每一個平臺都建立新的短信平臺,那麼就太耗費資源了),此時每一個業務平臺都須要向這個獨立的平臺來發請求來發短信。例如一個公司可能有多個項目都有發短信的需求,那麼這時候抽取短信功能到一個獨立的項目較好。

而上面會遇到一個問題,就是併發問題,若是一時間有太多的發短信請求可能會對短信平臺形成較大的處理壓力。
因此這時候就可使用消息隊列來解決這個問題。電商業務平臺能夠把須要發短信的手機號之類的信息存儲到消息隊列中,而後短信平臺能夠「慢慢」地從消息隊列來獲取數據來發短信。這樣不會有什麼超常的壓力而致使短信平臺宕機了。(固然,這個時候有有點效率過低了,不過這個是別的架構優化問題,這裏不談)

或許有人還不是很懂這樣的好處,那多提一點,因爲此時短信平臺是從消息隊列中獲取數據的,其一好處是避免了太高的訪問請求壓迫短信平臺,其二好處是因爲短信平臺是單獨運行的,那其實我能夠建立多個短信平臺來提升對請求的處理能力,只要它們都遵循從消息隊列中取數據便可。web




使用場景

  • 流量削鋒,做爲一個前置組件,幫助後面的關鍵組件頂住大訪問量的壓力。【上面的舉個栗子就是】
  • 異步處理,如你所見,一方只負責向消息隊列存消息,存完就能夠去幹點別的事情了。好比消息隊列用在備份平臺中,我要備份什麼東西告訴你一下就能夠了,我不必卡在那裏等你備份完畢再去作別的東西的,否則會很浪費時間。
  • 應用解耦,在上面的舉個栗子中其實也展現了一點應用解耦的功能,如上面把短信平臺從電商平臺中解耦出來了,並且這時候若是在多個短信平臺中的「某個」短信平臺掛了也不會影響電商平臺的運行。
  • 其餘。。。【這個東西是能夠用來作不少事情的,上面的是常見的幾種,甚至還有人用來作最基礎的消息通信】



消息隊列的模型與概念理解

上面講了消息隊列的做用,下面將講述消息隊列的模型、機制和API規範等這些偏基礎理念的東西,瞭解了這些基礎知識你才能更容易去掌握後面的實際使用,固然也會展現部分代碼來輔助理解。在下一節纔會以使用activeMQ爲例來使用消息隊列。redis


JMS模型

基本概念:

JMS全稱Java Message Service,即Java消息服務,能夠說是一種開發規範,一些消息隊列的開發要基於這種開發規範,好比說ActiveMQ的API結構要遵循JMS的規範。遵循了這種開發規範後,咱們就能夠很輕鬆地使用各類基於JMS開發規範的消息隊列。(JDBC也是一種開發規範)sql


內容:

1.JMS定義了Java中關於消息隊列的各類規範與標準。例如關於消息提供者的接口規範、關於存儲到消息隊列的消息的格式的規範、關於消息消費者的接口規範,關於消息域的規範,關於消息的可靠性的規範等等。


2.JMS定義了JMS的消息由如下三部分組成:【這三個部分的內容下面有講】
* 消息頭:主要是關於消息的傳輸一些屬性,如消息的目的地、消息的發送模式、消息的過時時間。
* 消息屬性:消息屬性存儲的是一些與消息頭以外的屬性,包括髮送者標識這些與消息相關屬性,也能夠包括一些與消息無關但開發者想同時攜帶的屬性
* 消息體:存儲的是具體的消息數據。


3.JMS定義了JMS Provider和JMS Consumer的建立方式,由Session對象建立,能夠用於發送和接收消息。


4.JMS也定義了消息的消費方式:
* 同步消費,即阻塞式監聽消費,消費者使用消費者的receive方法阻塞式地等待消息到達。receive方法獲得的是消息。
* 異步消費,能夠爲消費者註冊一個消息監聽器,消息監聽器會監聽消息的達到,在消息監聽器中能夠定義在消息到達時執行的操做。

5.JMS也定義了兩種消息傳遞方式:PTP(點對點) 和pub/sub(發佈/訂閱)。
* 點對點消息傳遞是生產者把消息存放到消息隊列中,而後消費者從消息隊列中取出數據,並且每一條消息都只能被消費一次
* 這種傳遞方式中,消息的生產者與消費者之間沒有時間上的相關性,消費者能夠很遲纔來隊列中取出消息。沒有被消費過的消息會一直存儲在消息隊列中,直到有人把它消費纔會從隊列中移除。
* api

  • 發佈/訂閱模式是生產者把消息存到消息隊列中,而後這個消息隊列上的「訂閱器」會把消息傳遞給訂閱器的所屬者。這就好像訂報紙,當有了新報紙後,負責你家的發報員會把報紙發給你。
    • 這種傳遞模式中,因爲消息是經過「訂閱」來發送給消費者的,因此每一個消息能夠有多個消費者。
    • 訂閱一個主題的消費者只能獲取在他訂閱以後生產者所生產的消息,也就是說你沒法接收以前的消息。
    • 訂閱模式的消息是能夠選擇是否持久化的。
      • 若是是非持久化的消息,那麼訂閱器的所屬者不在線的時候,是沒法接收到生產者生產的消息的。
      • 若是是持久化的消息,那麼消息隊列會把消費者標記成不在線,而後等他在線的時候再把消息發給他。
      • 在點對點消息傳遞中,目的地被稱爲隊列(queue);在發佈/訂閱模式中,目的地被稱爲主題(topic)。在實際的代碼中,使用什麼模式來進行消息傳遞取決你是建立的目的地是什麼類型的。




JMS定義的消息結構:

上面說了,JMS定義的消息結構包括三部分:消息頭,消息屬性,消息體服務器

  • 消息頭:包含屬性以下:
    • JMS Destination: 定義消息的發送目的地,由生產者端設置,主要有隊列Queue和主題Topic,用於標識把消息發送到指定的隊列或主題。
    • JMS DeliveryMode: 定義消息的發送模式,由生產者端設置,主要有持久模式和非持久模式,主要用於標識發佈/訂閱模式下的消息是否持久化。
    • JMS Expiration: 消息過時時間。若是值爲0,表明永不過時,過時後消息將被自動清除。默認永不過時。
    • JMS Priority: 消息優先級,值爲0-9,0-4是普通消息,5-9是加急消息,並無嚴格說2級比1級必定要優先發送,但要求加急的必須優先於普通消息。
    • JMS MessageID: 惟一標識每一個消息,由JMS Provider自動生成。
    • JMS Timestatmp: 時間戳,是生產者把消息發送到消息隊列的時間。
    • JMS CorrelationID: 由消費者端設置,用來鏈接到另外一消息,典型的應用是在回覆消息中使用CorrelationID鏈接到原消息,用來標明傳遞的消息與哪一個消息有關。
    • JMS ReplyTo: 由消費者端設置,值爲一個JMS Destination,能夠稱爲消息回覆地址,能夠用於但願消費者回傳一個消息給生產者的狀況。
    • JMS Type:消息類型的識別符,標明消息是什麼結構的(有普通文本結構,有map結構),由消費者端設置。
    • JMS Redelivered:標明是不是第一次發送,若是是,那麼值爲false,反之true。所謂不是第一次發送,指以前發送過,但沒有成功簽收消息。

  • 消息屬性:
    • 開發者自我添加的屬性:好比除了消息以外,我想讓消息攜帶一些我本身定義的數據就可使用消息屬性來作到。【就好像其實在web開發中有時候也把一些數據存儲到請求頭中】
    • JMS定義的屬性:這些屬性都是以JMSX開頭的屬性,包括如下屬性:
      • JMSXUserID: 發送消息的用戶標識
      • JMSXAppID: 發送消息的應用標識
      • JMSXDeliveryCount , JMSXGroupID , JMSXGroupSeq , JMSXProducerTXID , JMSXConsumerTXID , JMSXRevTimestamp , JMSXState
      • 【這些屬性能夠經過api來獲取: Enumeration jmsxPropertyNames = connection.getMetaData().getJMSXPropertyNames();】
    • JMS供應商特定的屬性:這些屬性JMS供應商決定,所謂供應商,也就是不一樣的消息隊列,好比activemq,rabbitmq
  • 消息體:JMS 定義了五種消息體格式,可使用不一樣形式發送和接受數據。
    • TextMessage:以普通字符文本格式存儲消息,
    • MapMessage:以Map格式存儲消息,取的時候也是以key來取。
    • BytesMessage:以字節文本格式存儲消息,取到的數據是字節格式的。
    • StreamMessage:以流格式存儲消息,取到的數據是流。
    • ObjectMessage:以對象格式來存儲消息,取到的數據是一個對象。
    • 【以什麼格式發送消息取決與使用session建立消息的時候建立的是什麼消息】




PTP式消息傳遞

  • PTP全稱點對點,點對點即一方發,一方收。【發的一方能夠有多個生產者,收的一方能夠有多個消費者,但每個消息的傳遞只能有一個生產者和一個消費者】
  • PTP式的消息傳遞就有點像發郵件,A要向B發郵件,那麼A把郵件發到郵件服務器上,而後B從郵件服務器上獲取消息。
  • PTP式的消息傳遞的特性:
    • 消息只能被消費一次。【當消息發送到某一個目的地的時候,消費者均可以從這個目的地取消息,但每一個消息只能被消費一次】
  • 想要使用PTP模式的消息傳遞,那麼建立消息目的地的時候須要建立成隊列Queue。
Destination destination = session.createQueue("tempqueue");




PUB/SUB式消息傳遞

  • PUB/SUB中文稱爲發佈/訂閱。所謂發佈訂閱,以「微博」爲例,博主能夠發送微博到微博服務器,而後訂閱了這個博主的人都會「收到」這個博主發的微博。這個過程是一點對多點的過程。因此發佈訂閱模式適用於一些廣播消息的狀況。
  • PUB/SUB式消息傳遞的特性:
    • 是一點對多點的消息廣播。
    • 消息發佈訂閱有分爲非持久訂閱和持久訂閱。非持久訂閱,那麼某個時間不在線的客戶將不能接收到那個時間發送的消息。持久訂閱會把那時不在線的客戶進行標記,等到客戶上線的時候會從新發送。
  • 想要使用PTP模式的消息傳遞,那麼建立消息目的地的時候須要建立成主題Topic。
Destination destination = session.createTopic("tempTopic");
  • 持久化訂閱的實現:
    • 非持久化訂閱只要求消費者處在消息接收狀態便可。而持久化訂閱須要解決離線問題。
    • 在JMS中,是經過建立持久化訂閱器DurableSubscriber來解決離線問題的,持久化訂閱器是與消費者進行綁定的,消費者經過持久化訂閱器來獲取消息,當消費者不在線的時候,持久化訂閱器會存儲這些離線消息。等到消費者上線,再次調用建立持久化訂閱器方法的時候,消息隊列不會從新建立持久化訂閱器,而是把這認爲是消費者上線的標識,而後把離線的消息都發給消費者。
    • 建立持久化訂閱器的方法是createDurableSubscriber,第一個參數是一個Topic,第二個參數是訂閱的名稱(只是一個標識)。




可靠性機制

    可靠性機制是保證消息被正常消費的機制,有時候消費者接收了消息,但處理過程當中忽然宕機了,這樣消息的消費就會失敗。因此爲了確保消息被消費成功,消息隊列使用「確認(acknowledge)」來標識一個消息被消費成功,若是消費者沒有對消息進行確認,那麼消息隊列會認爲這個消息被消費失敗了,(不一樣的需求狀況會有不一樣的針對消費失敗的處理,在ptp中,若是消費失敗,那麼消息不會被刪除,因此下一次消費者還能取到以前消費失敗的消息)。
    消息的成功消費一般包含三個階段:客戶接受消息,客戶處理消息和消息被確認。

在非事務會話中,消息被什麼時候確認取決於建立會話時的應答模式acknowledagement mode,該參數有三個可選值:session

  • Session.auto_acknowledge: 自動確認,當客戶成功的從receive返回的時候,或者從MessageListener.onMessage方法成功返回的時候,會話會自動確認客戶接收到的消息。
  • Session.client_acknowledge: 消費者手動確認,客戶經過調用消息的acknowledge方法來確認消息。須要注意的是,在這種模式中,確認是在會話層中進行的,確認一個被消費的消息將自動確認全部已被會話消費的消息。即即便僅確認第一個,其他接受的也會被同時確認。
  • session.dups_ok_acknowledge: Session沒必要確保對傳送消息的簽收,這個模式可能會引發消息的重複,可是下降了Session的開銷,因此只有客戶端能容忍重複的消息,纔可以使用。


在事務性會話中【此時使用事務來確保多條消息的消費】,當一個事務被提交的時候,確認會自動發生,不須要使用額外的消息確認語句,只須要進行事務提交。




事務

  • 事務用來確保某一系列操做的共同成功或共同失敗。
  • JMS Session接口提供了commit和rollback方法。事務提交意味着生產的全部消息被髮送(生產者)或消費的全部消息被確認(消費者)。事務回滾意味着生產的全部消息被銷燬(生產者),消費的全部消息被恢復並從新提交(消費者),除非它們已通過期。
  • 當session開啓事務時,此時的session是一個事務性會話,若是你直接關閉session,那麼他會回退其中沒有commit的事務。
  • 須要注意的是,事務過程當中發送的消息會知道事務提交纔會真正發送。
  • 在建立Session時,第一個參數決定是否開啓事務,true爲開啓,false爲不開啓。




消息持久性規範DeliveryMode

  • JMS支持以持久模式和非持久模式來進行消息提交
  • 以持久模式PERSISTENT來發送的消息,那麼會要求JMS Provider持久保存消息,以保證消息不會由於JMS Provider的失敗而丟失
  • 以非持久模式NON_PERSISTENT來發送的消息,那麼不會要求JMS Provider持久保存消息。
  • 在消息發送時可使用生產者.setDeliveryMode來設置發送到消息隊列的消息是不是持久化的。也能夠在send的時候針對每一條消息來設置。




消息優先級規範Priority

  • 消息優先級能夠指示JMS Provider首先提交緊急的消息。
  • 優先級分10個級別,爲0-9。
  • 若是不指定優先級,默認級別是4.須要注意的是,JMS Provider並不必定按照優先級的順序提交消息。只確保加急消息優先於普通消息。


消息過時規範Expiration

  • 能夠在消息頭中設置消息在必定時間後過時,默認是永不過時的。


消息的臨時目的地

  • 能夠經過會話上的createTemploraryQueue方法和createTemporaryTopic方法來建立臨時的目的地,它們的存在時間只限於建立它們的鏈接所保持的時間。只有建立該臨時目的地的鏈接上的消息消費者纔可以從臨時目的地中提取消息。
  • 固然,因爲臨時目的地也是一個目的地,咱們能夠把這個臨時目的地放在relpyTo中,這樣讓消費者把回覆放到這個臨時目的地中。這就實現了某種稱呼上的「消息回覆」。


JMS應用開發基本步驟:

1.建立一個JMS connection factory
2.經過connection factory來建立JMS Connection
3.啓動JMS Connection
4.經過connection建立JMS Session
5.建立JMS Destination
6.建立JMS Producer,或者建立JMS Message,並設置Destination.
7.建立JMS Consumer,或者註冊一個JMS Message Listener
8.發送或者接受JMS Message
9.關閉全部的JMS資源(Connection,Session)




總結

1.從上面的內容你應該瞭解到了PTP和PUB/SUB的思想,或許你還沒懂怎麼使用,但你應該內心應該知道它們的應用場景。【對於PTP/SUB你還須要瞭解到持久化訂閱和非持久化訂閱,持久化訂閱須要使用到持久化訂閱器來解決消費者離線問題】
2.你能夠了解到事務(session.commit())、可靠性機制(acknowledge)。
3.持久化:解決宕機問題。
4.其餘的屬性:優先級、過時時間、臨時目的地
5.JMS應用開發的概念步驟。
6.【若是上面的幾點你看了沒印象,建議你從新看過】
7.下面將使用activeMQ來實踐這些理論知識:http://www.javashuo.com/article/p-cawgalqe-bn.html

相關文章
相關標籤/搜索