在一個公司創立初期,他可能只有幾個應用,系統之間的關聯也不是那麼大,A系統調用B系統就直接調用B提供的API接口;後來這個公司作大了,他一步步發展有了幾十個系統,這時候A系統要調用B系統的接口,可是B系統前幾天剛改了一下接口A並不知情。因此A發現調不通因而給B系統管理員打電話,小王啊,改了接口咋不告訴我呢。我還覺得咱們系統出錯了呢。弄得小王一頓尷尬,我這本身改個東西還的通知這個通知那個的。java
咱們看到上面的故事中的小王他真的是很累啊。本身修改一個接口還的給全部調用接口的系統管理員打電話告知API發生變化。說到這個問題啊,仍是的說咱們系統之間的耦合。對於一個小公司來講是無所謂,可是對於一個大公司這種狀況簡直是致命的。因而最近幾年這些愈來愈大的互聯網公司在這種挑戰下提出了中間件這個概念:中間件在操做系統軟件,網絡和數據庫之上,應用軟件之下,總的做用是爲處於本身上層的軟件提供靈活的開發環境。於是中間件是指一類軟件,是基於分佈式處理的軟件,最突出的特色是其網絡通訊功能。也可認爲中間件是位於平臺和應用之間的通用服務,這些服務具備標準的程序接口和協議。針對不一樣的操做系統和硬件平臺,能夠有符合接口和協議的多種實現。數據庫
中間件能夠分爲六類:編程
1) 終端仿真/屏幕轉換緩存
2) 數據訪問中間件(UDA)tomcat
3) 遠程過程調用中間件(RPC)安全
4) 消息中間件(MOM)服務器
5) 交易中間件(TPM)markdown
6) 對象中間件網絡
然而在實際應用中,通常將中間件分爲兩大類:數據結構
一類是底層中間件,用於支撐單個應用系統或解決一類問題,包括交易中間件、應用服務器、消息中間件、數據訪問中間件等;
另外一類是高層中間件,更多的用於系統整合,包括企業應用集成中間件、工做流中間件、門戶中間件等,他們一般會與多個應用系統打交道,在系統中層次較高,並大多基於前一類的底層中間件運行。
終端仿真/屏幕轉換
此類中間件用於實現客戶機圖形用戶接口與已有的字符接口方式的服務器應用程序之間的互操做,應用與早期的大型機系統,如今已不多使用。
數據訪問中間件
此類中間件是爲了創建數據應用資源互操做的模式,對異構環境下的數據庫或文件系統實現聯接。
遠程過程調用中間件
此類中間件可使開發人員在須要時調用位於遠端服務器上的過程,屏蔽了在調用過程當中的通訊細節。一個應用程序使用RPC來遠程執行一個位於不一樣地址空間裏的過程,在效果上看和執行本地調用相同。
交易中間件
此類中間件是專門針對聯機交易系統而設計的。聯機交易系統須要處理大量併發進程,處理併發涉及到操做系統,文件系統,編程語言,數據通訊,數據庫系統,系統管理,應用軟件等。而交易中間件根據分佈式交易處理的標準及參考模型,對資源管理,交易管理和應用進行了實現,從而使得基於交易中間件開發應用程序更爲簡單。交易中間件基本上只適用於聯機交易系統,是一種較爲專用的中間件。
消息中間件
此類中間件是指利用高效可靠的消息傳遞機制進行平臺無關的數據交流,並基於數據通訊來進行分佈式系統的集成。經過提供消息傳遞和消息排隊模型,它能夠在分佈式環境下擴展進程間的通訊。
消息中間件能夠即支持同步方式,又支持異步方式。異步中間件比同步中間件具備更強的容錯性,在系統故障時能夠保證消息的正常傳輸。異步中間件技術又分爲兩類:廣播方式和發佈/訂閱方式。因爲發佈/訂閱方式能夠指定哪一種類型的用戶能夠接受哪一種類型的消息,更加有針對性,事實上已成爲異步中間件的非正式標準。目前主流的消息中間件產品有IBM的MQSeries,BEA的MessageQ和Sun的JMS等[1]。
對象中間件
傳統的對象技術經過封裝、繼承及多態提供了良好的代碼重用功能。但這些對象只存在與一個程序中,外界並不知道它們的存在,也沒法訪問它們。對象中間件提供了一個標準的構建框架,能使不一樣廠家的軟件經過不一樣的地址空間,網絡和操做系統實現交互訪問。對象中間件的目標是爲軟件用戶及開發者提供一種應用級的即插即用的互操做性。目前主流的對象中間件有OMG的CORBA,Microsoft 的COM以及IBM的SOM,Sun的RMI等。
中間件的特色
通常來說,中間件具備如下一些特色:知足大量應用的需求,運行於多種硬件和操做系統平臺,支持分佈式計算,支持標準接口和協議。開發人員經過調用中間件提供的大量API,實現異構環境的通訊,從而屏蔽異構系統中複雜的操做系統和網絡協議。
因爲標準接口對於可移植性和標準協議對於互操做性的重要性,中間件已成爲許多標準化工做的主要部分。分佈式應用軟件藉助中間件能夠在不一樣的技術之間共享資源。
總的來講,中間件屏蔽了底層操做系統的複雜性,使程序開發人員面對一個簡單而統一的開發環境,減小了程序設計的複雜性,將注意力集中與本身的業務上,沒必要再爲程序在不一樣軟件系統上的移植而重複工做,從而大大減小了技術上的負擔。
面向消息的中間件(MOM),提供了以鬆散耦合的靈活方式集成應用程序的一種機制。它們提供了基於存儲和轉發的應用程序之間的異步數據發送,即應用程序彼此不直接通訊,而是與做爲中介的MOM通訊。MOM提供了有保證的消息發送(至少是在儘量地作到這一點),應用程序開發人員無需瞭解遠程過程調用(RPC)和網絡/通訊協議的細節。
消息隊列技術是分佈式應用間交換信息的一種技術。消息隊列可駐留在內存或磁盤上,隊列存儲消息直到它們被用程序讀走。經過消息隊列,應用程序可獨立地執行–它們不須要知道彼此的位置、或在繼續執行前不須要等待接收程序接收此消息。在分佈式計算環境中,爲了集成分佈式應用,開發者須要對異構網絡環境下的分佈式應用提供有效的通訊手段。爲了管理須要共享的信息,對應用提供公共的信息交換機制是重要的。設計分佈式應用的方法主要有:遠程過程調用(RPC)–分佈式計算環境(DCE)的基礎標準成分之一;對象事務監控(OTM)–基於CORBA的面向對象工業標準與事務處理(TP)監控技術的組合;消息隊列(MessageQueue)–構造分佈式應用的鬆耦合方法。
MOM將消息路由給應用程B,這樣消息就能夠存在於徹底不一樣的計算機上,MOM負責處理網絡通訊。若是網絡鏈接不可用,MOM會存儲消息,直到鏈接變得可用時,再將消息轉發給應用程序B。
靈活性的另外一方面體如今,當應用程序A發送其消息時,應用程序B甚至能夠不處於執行狀態。MOM將保留這個消息,直到應用程序B開始執行並試着檢索消息爲止。這還防止了應用程序A由於等待應用程序B檢索消息而出現阻塞。這種異步通訊要求應用程序的設計與如今大多數應用程序不一樣,不過,對於時間無關或並行處理,它多是一個極其有用的方法。
消息中間件通常有兩種傳遞模式:點對點模式(P2P)和發佈-訂閱模式(Pub/Sub)。
點對點模式
Point-to-Point(P2P)咱們很容易理解,即生產者和消費者之間的消息往來。
每一個消息都被髮送到特定的消息隊列,接收者從隊列中獲取消息。隊列保留着消息,直到他們被消費或超時。
P2P的特色:
發佈-訂閱模式(Pub/Sub)
咱們能夠聯想到賣報紙的過程:印刷廠把當天的報紙印好而後送到郵遞員手裏,郵遞員風雨兼程的把報紙送到每一位訂閱者手裏。由此咱們能夠看到發佈-訂閱模式的一些特色:
由上介紹咱們能夠看出這兩種模式各有千秋,若是你須要點對點的發送消息那麼使用P2P更專一,若是你是羣發消息,顯然pub/sub模式更適合。
目前市場上對於網絡消息傳遞的協議版本不少,不一樣的協議有不一樣的規範,咱們在使用時要比對實現不一樣協議的產品。下面咱們看一下目前主流的消息傳遞協議:
AMQP,即Advanced Message Queuing Protocol,高級消息隊列協議,是應用層協議的一個開放標準,爲面向消息的中間件設計。AMQP協議是一種二進制協議,提供客戶端應用與消息中間件之間異步、安全、高效地交互。
AMQP是一個應用層的異步消息傳遞協議,爲面向消息的中間件而設計。其目的是經過協議使應用模塊之間或應用程序與中間件等進行充分解耦。而在設計初期,AMQP的原始用途只是爲金融界提供一個能夠彼此協做的消息協議。如今已經有至關一部分遵循AMQP的服務器和客戶端供使用。其中RabbitMQ是AMQP的一款開源標準實現。
支持全部消息中間件的功能:消息交換、文件傳輸、流傳輸、遠程進程調用等。
AMQP的服務器(Broker)主要由交換器、消息、隊列組成。Broker的主要功能是消息的路由和緩存。對於須要保障可靠性的消息,RabbitMQ能夠將消息、隊列和交換器的數據寫入本地硬盤。而對於響應時間敏感的消息,RabbitMQ能夠不配置持久化機制。
解決的問題:
1)信息的發送者和接收者如何維持這個鏈接,若是一方的鏈接中斷,這期間的數據如何防止丟失?
2)如何下降發送者和接收者的耦合度?
3)如何讓Priority高的接收者先接到數據?
4)如何作到load balance?有效均衡接收者的負載?
5)如何有效的將數據發送到相關的接收者?也就是說將接收者subscribe 不一樣的數據,如何作有效的filter。
6)如何作到可擴展,甚至將這個通訊模塊發到cluster上?
7)如何保證接收者接收到了完整,正確的數據?
AMQP協議解決了以上的問題,而RabbitMQ實現了AMQP。
STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,簡單(流)文本定向消息協議。
它提供了一個可互操做的鏈接格式,容許STOMP客戶端與任意STOMP消息代理(Broker)進行交互。STOMP協議因爲設計簡單,易於開發客戶端,所以在多種語言和多種平臺上獲得普遍地應用。
STOMP協議的前身是TTMP協議(一個簡單的基於文本的協議),專爲消息中間件設計。
STOMP是一個很是簡單和容易實現的協議,其設計靈感源自於HTTP的簡單性。儘管STOMP協議在服務器端的實現可能有必定的難度,但客戶端的實現卻很容易。例如,可使用Telnet登陸到任何的STOMP代理,並與STOMP代理進行交互。
STOMP是除AMQP開放消息協議以外地另一個選擇, 實現了被用在JMS brokers中特定的有線協議,好比OpenWire。它僅僅是實現通用消息操做中的一部分,並不是想要覆蓋全面的消息API。
STOMP server就好像是一系列的目的地, 消息會被髮送到這裏。STOMP協議把目的地看成不透明的字符串,其語法是服務端具體的實現。 此外STOMP沒有定義目的地的交付語義是什麼,語義的目的地能夠從服務器到服務器,甚至從目的地到目的地。這使得服務器有可創造性的語義,去支持STOMP。
STOMP client的用戶代理能夠充當兩個角色(可能同時):
STOMP協議工做於TCP協議之上,使用了下列命令:
SEND 發送
SUBSCRIBE 訂閱
UNSUBSCRIBE 退訂
BEGIN 開始
COMMIT 提交
ABORT 取消
ACK 確認
DISCONNECT 斷開
目前最流行的STOMP消息代理是Apache ActiveMQ。
JMS是Java Message Service的縮寫,即Java消息服務。
在大型互聯網中,咱們採用消息中間件能夠進行應用之間的解耦以及操做的異步,這是消息中間件兩個最基礎的特色,也正是咱們所須要的。在此基礎上,咱們着重思考的是消息的順序保證、擴展性、可靠性、業務操做與消息發送一致性,以及多集羣訂閱者等方面的問題。固然,這些咱們要思考的東西,JMS都已經想到了,先看下JMS能幫開發者作什麼:
一、定義一組消息公用概念和實用工具
全部Java應用程序均可以使用JMS中定義的API去完成消息的建立、接收與發送,任何實現了JMS標準的MOM均可以做爲消息的中介,完成消息的存儲轉發
二、最大化消息應用程序的可移植性
MOM提供了有保證的消息發送,應用程序開發人員無需瞭解遠程過程調用(RPC)和網絡/通訊協議的細節,提供了程序的可移植性
三、最大化下降應用程序與應用程序之間的耦合度
因爲MOM的存在,各個應用程序只關心和MOM之間如何進行消息的接收與發送,而無須關注MOM的另外一邊,其餘程序是如何接收和發送的
JMS定義了一套通用的接口和相關語義,提供了諸如持久、驗證和事務的消息服務,它最主要的目的是容許Java應用程序訪問現有的消息中間件。JMS規範沒有指定在消息節點間所使用的通信底層協議,來保證應用開發人員不用與其細節打交道,一個特定的JMS實現可能提供基於TCP/IP、HTTP、UDP或者其它的協議。
因爲沒有統一的規範和標準,基於消息中間件的應用不可移植,不一樣的消息中間件也不能互操做,這大大阻礙了消息中間件的發展。 Java Message Service(JMS, Java消息服務)是SUN及其夥伴公司提出的旨在統一各類消息中間件系統接口的規範。
目前許多廠商採用並實現了JMS API,如今,JMS產品可以爲企業提供一套完整的消息傳遞功能,目前咱們看到的比較流行的JMS商業軟件和開源產品:WebLogic、SonicMQ、ActiveMQ、OpenJMS都是基於JMS規範的實現。
在 JMS 以前,每一家 MOM 廠商都用專有 API 爲應用程序提供對其產品的訪問,一般可用於許多種語言,其中包括 Java 語言。JMS 經過 MOM 產品爲 Java 程序提供了一個發送和接收消息的標準的、便利的方法。用 JMS 編寫的程序能夠在任何實現 JMS 標準的 MOM 上運行。
JMS 可移植性的關鍵在於:JMS API 是由 Sun 做爲一組接口而提供的。提供了 JMS 功能的產品是經過提供一個實現這些接口的提供者來作到這一點的。開發人員能夠經過定義一組消息和一組交換這些消息的客戶機應用程序創建 JMS 應用程序。
JMS 支持兩種消息類型P2P 和Pub/Sub,在JMS消息模型中,根據點對點模式和發佈/訂閱模式,這些要素由擴展出了各自的內容:
JMS標準 | 點對點模式 | 發佈/訂閱模式 |
---|---|---|
ConnectionFactory | QueueConnectionFactory | TopicConnectionFactory |
Connection | QueueConnection | TopicConnection |
Destination | Queue | Topic |
Session | QueueSession | TopicSession |
MessageProducer | QueueSender | TopicPublisher |
MessageConsumer | QueueReceiver | TopicSubscriber |
JMS爲發開者提供了不少的要素,看一下比較重要的幾個:
要 素 | 做 用 |
---|---|
Destination | 表示消息所走通道的目標定義,用來定義消息從發送端發出後要走的通道,而不是接收方。Destination屬於管理類對象 |
ConnectionFactory | 顧名思義,用於建立鏈接對象,ConnectionFactory屬於管理類的對象 |
Connection | 鏈接接口,所負責的重要工做時建立Session |
Session | 會話接口,這是一個很是重要的對象,消息發送者、消息接收者以及消息對象自己,都是經過這個會話對象建立的 |
MessageConsumer | 消息的消費者,也就是訂閱消息並處理消息的對象 |
MessageProducer | 消息的生產者,也就是用來發送消息的對象 |
XXXMessage | 指各類類型的消息對象,包括ByteMesage、ObjectMessage、StreamMessage和TextMessage這5種 |
JMS消息模型
JMS 消息由如下幾部分組成:消息頭,屬性,消息體。
JMS編程模型
通常來講咱們在開發基於JMS協議的客戶端由一下幾部構成:
1) 用JNDI 獲得ConnectionFactory對象;
2) 用JNDI 獲得目標隊列或主題對象,即Destination對象;
3) 用ConnectionFactory建立Connection 對象;
4) 用Connection對象建立一個或多個JMS Session;
5) 用Session 和Destination 建立MessageProducer和MessageConsumer;
6) 通知Connection 開始傳遞消息。
由於jms須要使用到J2EE服務器,咱們日常用的tomcat屬於J2SE類型的服務器,常見的J2EE服務器包括:Geronimo,JBoss 4, GlassFish,WebLogic 。咱們在這裏使用glassfish 容器。安裝和使用有不少教程,在此就不貼了。首先咱們進去glassfish的控制檯,設置一下咱們的發送者和接受者對象:
下面咱們用oracle提供的jms接口來寫一個服務端,咱們先來寫一個P2P模式的例子:
MySender.java
import java.io.BufferedReader; import java.io.InputStreamReader; import javax.naming.*; import javax.jms.*; public class MySender { public static void main(String[] args) { try { //1)建立一個connection InitialContext ctx=new InitialContext(); QueueConnectionFactory f=(QueueConnectionFactory)ctx.lookup("myQueueConnectionFactory"); QueueConnection con=f.createQueueConnection(); con.start(); //2) 建立一個會話接口 QueueSession ses=con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); //3) 獲取會話接口對象 Queue t=(Queue)ctx.lookup("myQueue"); //4)建立一個發送者對象 QueueSender sender=ses.createSender(t); //5) 建立一個消息對象 TextMessage msg=ses.createTextMessage(); //6) 把咱們的消息寫入msg對象中 BufferedReader b=new BufferedReader(new InputStreamReader(System.in)); while(true) { System.out.println("Enter Msg, end to terminate:"); String s=b.readLine(); if (s.equals("end")) break; msg.setText(s); //7) 發送消息 sender.send(msg); System.out.println("Message successfully sent."); } //8) 關閉鏈接 con.close(); }catch(Exception e){System.out.println(e);} } }
MyReceiver.java
import javax.jms.*;
import javax.naming.InitialContext;
public class MyReceiver { public static void main(String[] args) { try{ //1) 建立一個connection InitialContext ctx=new InitialContext(); QueueConnectionFactory f=(QueueConnectionFactory)ctx.lookup("myQueueConnectionFactory"); QueueConnection con=f.createQueueConnection(); con.start(); //2) 建立一個會話接口 QueueSession ses=con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); //3) 獲取會話接口對象 Queue t=(Queue)ctx.lookup("myQueue"); //4)建立一個發送者對象 QueueReceiver receiver=ses.createReceiver(t); //5) 建立一個消監聽對象 MyListener listener=new MyListener(); //6) 將監聽器註冊到receiver,用來監聽receiver receiver.setMessageListener(listener); System.out.println("Receiver1 is ready, waiting for messages..."); System.out.println("press Ctrl+c to shutdown..."); while(true){ Thread.sleep(1000); } }catch(Exception e){System.out.println(e);} } }
MyListener.java
import javax.jms.*; public class MyListener implements MessageListener { public void onMessage(Message m) { try{ TextMessage msg=(TextMessage)m; System.out.println("following message is received:"+msg.getText()); }catch(JMSException e){System.out.println(e);} } }
Pub/Sub模式:
MySender.java
import javax.jms.*; import javax.naming.InitialContext; import java.io.BufferedReader; import java.io.InputStreamReader; public class MySender { public static void main(String[] args) { try { //1)建立一個connection InitialContext ctx=new InitialContext(); TopicConnectionFactory f=(TopicConnectionFactory)ctx.lookup("myTopicConnectionFactory"); TopicConnection con=f.createTopicConnection(); con.start(); //2) 建立一個會話接口 TopicSession ses=con.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); //3) 獲取會話接口對象 Topic t=(Topic)ctx.lookup("myTopic"); //4)建立一個發送者對象 TopicPublisher publisher=ses.createPublisher(t); //5) 建立一個消息對象 TextMessage msg=ses.createTextMessage(); //6) 把咱們的消息寫入msg對象中 BufferedReader b=new BufferedReader(new InputStreamReader(System.in)); while(true) { System.out.println("Enter Msg, end to terminate:"); String s=b.readLine(); if (s.equals("end")) break; msg.setText(s); //7) 發送消息 publisher.publish(msg); System.out.println("Message successfully sent."); } //8) 關閉鏈接 con.close(); }catch(Exception e){System.out.println(e);} } }
MyReceiver.java
import javax.jms.*;
import javax.naming.InitialContext;
public class MyReceiver { public static void main(String[] args) { try{ //1) 建立一個connection InitialContext ctx=new InitialContext(); TopicConnectionFactory f=(TopicConnectionFactory)ctx.lookup("myTopicConnectionFactory"); TopicConnection con=f.createTopicConnection(); //2) 建立一個會話接口 TopicSession ses=con.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); //3) 獲取會話接口對象 Topic t=(Topic)ctx.lookup("myTopic"); //4)建立一個發送者對象 TopicSubscriber receiver=ses.createSubscriber(t); //5) 建立一個消監聽對象 MyListener listener=new MyListener(); //6) 將監聽器註冊到receiver,用來監聽receiver receiver.setMessageListener(listener); System.out.println("Receiver1 is ready, waiting for messages..."); System.out.println("press Ctrl+c to shutdown..."); while(true){ Thread.sleep(1000); } }catch(Exception e){System.out.println(e);} } }
MyListener.java
import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.TextMessage; public class MyListener implements MessageListener { public void onMessage(Message m) { try{ TextMessage msg=(TextMessage)m; System.out.println("following message is received:"+msg.getText()); }catch(JMSException e){System.out.println(e);} } }
上面兩個案例咱們運行能夠看到消息成功的發送出去了。熟悉了JMS的語法,使用起來仍是很簡單。
上面咱們介紹到了JMS,JMS是一個用於提供消息服務的技術規範,它制定了在整個消息服務提供過程當中的全部數據結構和交互流程。JMS即Java消息服務(Java Message Service)應用程序接口,是一個Java平臺中關於面向消息中間件(MOM)的API。 Java消息服務是一個與具體平臺無關的API,絕大多數MOM提供商都對JMS提供支持。
下面咱們引入另外一個概念:MQ(Message Queue)。
應用程序經過寫和檢索出入列隊的針對應用程序的數據(消息)來通訊,而無需專用鏈接來連接它們。消息傳遞指的是程序之間經過在消息中發送數據進行通訊,而不是經過直接調用彼此來通訊,直接調用一般是用於諸如遠程過程調用的技術。排隊指的是應用程序經過隊列來通訊。隊列的使用除去了接收和發送應用程序同時執行的要求。
MQ和JMS相似,但不一樣的是JMS是SUN Java消息中間件服務的一個標準和API定義,而MQ則是遵循了AMQP協議的具體實現和產品。JMS是一個用於提供消息服務的技術規範,它制定了在整個消息服務提供過程當中的全部數據結構和交互流程。而MQ則是消息隊列服務,是面向消息中間件(MOM)的最終實現,是真正的服務提供者;MQ的實現能夠基於JMS,也能夠基於其餘規範或標準。MQ 有不少產品:IBM的,rabbitmq, activemq 等,rabbitmq 只支持點對點的方式。因此沒有徹底實現JMS的標準,因此說它不是一個JMS產品,而rabitmq 和Jobss JMS 它們實現了JMS的各項標準,是開源的JMS產品。目前徹底實現JMS協議的mq是activemq,因此接下來咱們先重點看一下activemq。從activemq入手去探索javaEE的世界。