JMS 是 Java Message Service 的簡稱,即 Java 消息服務。什麼是消息服務呢,咱們來看一下 Oracle 官方的定義:java
The Java Message Service (JMS) API is a messaging standard that allows application components based on the Java Platform Enterprise Edition (Java EE) to create, send, receive, and read messages. It enables distributed communication that is loosely coupled, reliable, and asynchronous. 翻譯過來就是說,JMS 是一個消息標準,這種標準容許基於 JavaEE 的應用組件能夠建立,發送,接受及讀取消息。經過 JMS 能夠實現鬆耦合,可依賴及異步的分佈式通訊。那麼在這一段話中咱們應該注意兩點,即首先這是一個標準,相似於 Servlet 標準,並非一個具體的實現。其次,這是一種分佈式的通訊機制,而且是異步的。
JMS 只定義了 Java 中訪問消息中間件的接口,並無給予實現,實現 JMS 接口的消息中間件稱爲 JMS Provider。如 Apache 的 ActiveMQ、以及阿里巴巴的 RocketMQ。編程
在 Jave 中,若是兩個應用程序之間對各自都不瞭解,甚至這兩個程序可能部署在不一樣的大洲上,那麼它們之間如何發送消息呢?服務器
當前,CORBA、DCOM、RMI 等 RPC 中間件技術已普遍應用於各個領域。可是面對規模和複雜度都愈來愈高的分佈式系統,這些技術也顯示出其 侷限性:網絡
面向消息的中間件(Message Oriented Middleware,MoM)較好的解決了以上問題。發送者將消息發送給消息服務器,消息服務器將消息存放在若干隊列中,在合適的時候再將消息轉發給接收者。session
面向消息的中間件的優勢:數據結構
舉個例子,一個應用程序 A 部署在印度,另外一個應用程序部署在美國,而後每當 A 觸發某件過後,B 想從 A 獲取一些更新信息。固然,也有可能不止一個 B 對 A 的更新信息感興趣,可能會有 N 個相似 B 的應用程序想從 A 中獲取更新的信息。在這種狀況下,Jave 提供了最佳的解決方案 JMS,完美解決了上面討論的問題。app
JMS 一樣適用於基於事件的應用程序,如聊天服務,它須要一種發佈事件機制向全部與服務器鏈接的客戶端發送消息。JMS 與 RMI 不一樣,發送消息的時候,接收者不須要在線。服務器發送了消息,而後就無論了;等到客戶端上線的時候,能保證接收到服務器發送的消息。這是一個很強大的解決方案,能處理當今世界不少廣泛問題。異步
異步:JMS 天生就是異步的,客戶端獲取消息的時候,不須要主動發送請求,消息會自動發送給可用的客戶端。async
可靠:JMS 保證消息只會遞送一次。你們都遇到太重複建立消息問題,而 JMS 能幫你避免該問題,只是避免而不是杜絕,因此在一些糟糕的環境下仍是有可能會出現重複。分佈式
消息中間件(JMS Provider):指提供了對 JMS 協議的第三方組件,好比 ActiveMQ 就是一個消息中間件,另外比較知名的還有 KFA, Rabbit MQ 等;
消息模式:分爲點對點(Point-to-Point,即P2P)和發佈/訂閱(Pub/Sub),對應的數據結構分別是隊列(Queue)和主題(Topic);
消息(Message): 通訊內容的載體,其結構主要分爲消息頭,屬性和消息體,而且根據存儲結構的不一樣分爲好幾種,後面會詳細提到;
消息生產者(Provider):產生消息的一方,在 P2P 模式下,指消息發送者(Sender),在 P/S 模式下指消息發佈者(Publisher);
消息消費者(Consumer):接收消息的一方,對應於兩種模式分別是消息接收者(Receiver)和消息訂閱者(Subscriber);
涉及到的概念:
每一個消息都被髮送到一個特定的隊列,接收者從隊列中獲取消息。隊列保留着消息,直到他們被消費或超時。
P2P 的特色:
總結:若是你但願發送的每一個消息都應該被成功處理的話,那麼你須要 P2P 模式。
涉及到的概念:
客戶端將消息發送到主題。多個發佈者將消息發送到 Topic,系統將這些消息傳遞給多個訂閱者。
Pub/Sub 的特色:
總結:若是你但願發送的消息能夠不被作任何處理、或者被一個消息者處理、或者能夠被多個消費者處理的話,那麼能夠採用 Pub/Sub 模型
在 JMS 中,消息的產生和消費是異步的。對於消費來講,JMS 的消費者能夠經過兩種方式來消費消息。
同步:訂閱者或接收者調用 receive 方法來接收消息,receive 方法在可以接收到消息以前(或超時以前)將一直阻塞。
consumer = session.createConsumer(destination); Message message = consumer.receive();
異步:訂閱者或接收者能夠註冊爲一個消息監聽器。當消息到達以後,系統自動調用監聽器的 onMessage 方法。
consumer.setMessageListener(new MessageListener(){ @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage)message; try { String value = textMessage.getText(); System.out.println("value: "+value); } catch (JMSException e) { // TODO Auto-generated catch block e.printStackTrace(); } } });
在 JMS 的標準協議裏,有幾個重要的接口,先簡單羅列以下:
ConnectionFactory(鏈接工廠):建立 Connection 的工廠,經過這個對象來建立一個到某個消息服務的鏈接。
用戶用來建立到JMs提供者的鏈接的被管對象。JMS 客戶經過可移植的接口訪問鏈接,這樣當下層的實現改變時,代碼不須要進行修改。管理員在 JNDI 名字空間中配置鏈接工廠,這樣,JMS 客戶纔可以查找到它們。根據消息類型的不一樣,用戶將使用隊列鏈接工廠,或者主題鏈接工廠。
Connection(鏈接) :一個具體的鏈接,由 ConnectionFactory 建立。
鏈接表明了應用程序和消息服務器之間的通訊鏈路。在得到了鏈接工廠後,就能夠建立一個與 JMS 提供者的鏈接。根據不一樣的鏈接類型,鏈接容許用戶建立會話,以發送和接收隊列和主題到目標。
Destination(目標) :消息存儲的位置,發送者把消息發送到指定位置,消費者從指定位置取消息,那麼這個指定位置多是一個 topic 也多是一個 queue,由這個來表示。
目標是一個包裝了消息目標標識符的被管對象,消息目標是指消息發佈和接收的地點,或者是隊列,或者是主題。JMS 管理員建立這些對象,而後用戶經過 JNDI 發現它們。和鏈接工廠同樣,管理員能夠建立兩種類型的目標,點對點模型的隊列,以及發佈者/訂閱者模型的主題。
Session(會話) :一個發送或接收消息的線程,由 Connection 建立的用於操做消息的接口,本接口能夠直接用來建立消息的生產者對象。
表示一個單線程的上下文,用於發送和接收消息。因爲會話是單線程的,因此消息是連續的,就是說消息是按照發送的順序一個一個接收的。會話的好處是它支持事務。若是用戶選擇了事務支持,會話上下文將保存一組消息,直到事務被提交才發送這些消息。在提交事務以前,用戶可使用回滾操做取消這些消息。一個會話容許用戶建立消息生產者來發送消息,建立消息消費者來接收消息。
MessageProducer(消息生產者) :消息的生產者,包括 QueueSender 和 TopicPublisher
由會話建立的對象,用於發送消息到目標。用戶能夠建立某個目標的發送者,也能夠建立一個通用的發送者,在發送消息時指定目標。
MessageConsumer(消息消費者) :消息的消費者, 包括 QueueReceiver 和 TopicSubscriber
會話建立的對象,用於接收發送到目標的消息。消費者能夠同步地(阻模式),或異步(非阻寒)接收隊列和主題類型的消息。
MessageListener(消息監聽器) :提供給消費者監聽消息使用的,在添加了某個監聽器以後,一旦消費到達,則會調用其 onMessage 方法。
Message接口(消息):是在消費者和生產者之間傳送的對象,也就是說從一個應用程序創送到另外一個應用程序。
JMS 客戶端使用 JMS 消息與系統通信,JMS 消息雖然格式簡單可是很是靈活。一個消息有三個主要部分:
JMS 消息頭預約義了若干字段用於客戶端與JMS提供者之間識別和發送消息,預編譯頭以下:
JMSDestination:消息發送的目的地,主要是指 Queue 和 Topic,由 session 建立而由生產者的 send 方法設置。
JMSDeliveryMode:傳送模式,有兩種即久模式和非持久模式。一條持久性的消息應該被傳輸"一次僅僅一次",這就意味着若是 JMS 提供者出現故障,該消息並不會丟失,它會在服務器恢復以後再次傳遞。一條非持久的消息最多會傳遞一次,這意味着服務器出現故障,該消息將永遠丟失。由 session 建立而由消息生產者的 send 方法設置。
JMSMessageID:惟一識別每一個消息的標識,由 JMS 消息生產者產生。由 send 方法設置。
JMSTimestamp:一個 JMS Provider 在調用 send() 方法時自動設置,它是消息被髮送和消費者實際接收的時間差。由客戶端設置。
JMSCorrelationI`:用來鏈接到另一個消息,典型的應用是在回覆消息中鏈接到原消息。在大多數狀況下,JMSCorrelationID 用於將一條消息標記爲對 JMSMessageID 標示的上一條消息的應答,不過,JMSCorrelationID 能夠是任何值,不只僅是 JMSMessageID。由客戶端設置
JMSReplyTo:提供本消息回覆消息的目的地址,由客戶端設置。
JMSRedelivered:若是一個客戶端收到一個設置了 JMSRedelivered 屬性的消息,則表示可能客戶端曾經在早些時候收到過該消息,但並無簽收(acknowledged)。若是該消息被從新傳送,JMSRedelivered=true 不然 JMSRedelivered=flase 。由 JMS Provider 設置。
JMSType:消息類型的標識符,由客戶端設置。
JMSExpiration:消息過時時間,等於 Destination 的 send 方法中的 timeToLive值加上發送時刻的GMT的時間值。若是timeToLive 值等於零,則 JMSExpiration 被設置爲零,表示該消息永不過時。若是發送後,在消息過時時間以後消息尚未被髮送到目的地,則該消息被清除。由 send 方法設置。
JMSPriority:消息優先級,從 0-9 十個級別,0-4 是普通消息,5-9 是加急消息。JMS 不要求 JMS Provider 嚴格按照這十個優先級發送消息,但必須保證加急消息要先於普通消息到達,默認是 4 級。由 send 方法設置。
一個消息的消息頭有這些屬性,咱們能夠按照須要對這個消息的消息進行設計,在將這個消息使用消息生產者的 send() 方法發送到消息服務上。
咱們能夠給消息設置自定義屬性,這些屬性主要是提供給應用程序的。對於實現消息過濾功能,消息屬性很是有用,JMS API 定義了一些標準屬性,JMS 服務提供者能夠選擇性的提供部分標準屬性。
在消息體中,JMS API 定義了五種類型的消息格式,讓咱們能夠以不一樣的形式發送和接受消息,並提供了對已有消息格式的兼容。不一樣的消息類型以下: