JMS和ActiveMQ的兩種發送和接收示例
1 Java消息服務和JMS概述
兩個系統之間或者分佈式系統之間的信息通信,是我們開發中比較常見的場景,比如系統A要把信息發送給系統B,這個問題我們應該如何去處理?
1999年,原來的SUN公司領銜提出了一種面向消息的中間件服務–JMS規範(標準);
JMS即Java消息服務(Java Message Service的簡稱),是Java EE 的標準/規範之一。這種規範(標準)指出:消息的發送應該是異步的、非阻塞的。也就是說消息的發送者發送完消息後就直接返回了,不需要等待接收者接收到消息後才能返回,發送者和接收者可以說是互不影響。所以這種規範(標準)能夠減輕或消除系統瓶頸,實現系統之間去除耦合,提高系統的整體可伸縮性和靈活性。
JMS是Java EE中定義的一組標準API,它自身並不是一個消息服務系統,它是消息傳送服務的一個抽象,也就是說它定義了消息傳送的接口而並沒有具體實現。
2 JMS的發展歷程
JavaEE | JMS版本 |
---|---|
Java EE 8 | Java Message Service API 2.1 |
Java EE 7 | Java Message Service API 2.0 |
Java EE 6 | Java Message Service API 1.1 |
Java EE 5 | Java Message Service API 1.1 |
J2EE1.4 | Java Message Service API 1.1 |
J2EE1.3 | Java Message Service API 1.0 (不成熟,不夠完善) |
3 ActiveMQ概述
ActiveMQ是Apache下的一個項目,採用Java語言開發;ActiveMQ 是一款非常流行的開源消息服務器,實現了JMS規範;官網: http://activemq.apache.org/
4 ActiveMQ環境搭建
4.1 Linux下環境搭建
ActiveMQ運行需要Java的支持,首先需要配置Java環境變量;
1、下載ActiveMQ
2、解壓:其中 -C /usr/local/ 指定把文件解壓到哪裏去
tar -zxvf apache-activemq-5.15.7-bin.tar.gz -C /usr/local/
解壓後就安裝完成了,即可使用,不需要其他操作
Java開發的服務器,都是直接解壓即可使用:tomcat、maven、zookeeper、ActiveMQ、Mycat
3、切換到解壓後的activemq的bin目錄下
cd /usr/local/apache-activemq-5.15.7 去啓動
4、切換到bin目錄下,
啓動:./activemq start
5、切換到bin目錄下,
關閉:./activemq stop
無法關閉的問題:
1 把data目錄下的所有文件及目錄都刪除
2 把activeMQ主目錄下的 tmp 目錄也刪除
啓動後有兩個端口號,一個是web控制檯:8161,一個是消息服務broker連接端口:61616
web管理控制檯admin URL地址:http://localhost:8161 默認登錄賬號 admin 密碼 admin,注意:Linux防火前要關閉
消息服務broker URL地址 :
tcp://localhost:61616
4.2 windows下環境搭建
1、下載activeMQ windows版本的壓縮包
2、解壓下載下來的壓縮包,解壓後就可以直接使用
3、進入解壓後的目錄的bin下面
4、在地址欄輸入 cmd 後確定
5、在dos窗口裏面輸入: activemq start 進行啓動
6、關閉:activemq stop 或者Ctrl + c
5 Java消息服務JMS整體設計結構
基本要素:
1、生產者producer
2、消費者consumer
3、消息服務broker
交互模型
JMS兩種消息傳送模式:
1、點對點( Point-to-Point):專門用於使用隊列Queue傳送消息;
2、發佈/訂閱(Publish/Subscribe):專門用於使用主題Topic傳送消息。
基於隊列Queue的點對點消息只能被一個消費者消費,如多個消費者都註冊到同一個消息隊列上,當生產者發送一條消息後,而只有其中一個消費者會接收到該消息,而不是所有消費者都能接收到該消息。 (1 對 1)
基於主題的發佈與訂閱消息能被多個消費者消費,生產者發送的消息,所有訂閱了該topic的消費者都能接收到。(1 對 多)
6 Java消息服務JMS API總體概覽
JMS API可以分爲3個主要部分:
1、公共API:可用於向一個隊列或主題發送消息或從其中接收消息;
2、點對點API:專門用於使用隊列Queue傳送消息;
3、發佈/訂閱API:專門用於使用主題Topic傳送消息。
1、JMS公共API
在JMS公共API內部,和發送與接收消息有關的JMS API接口主要是:
ConnectionFactory
Connection
Session
Message
Destination
MessageProducer
MessageConsumer
它們的關係是:一旦有了ConnectionFactory,就可以創建Connection,一旦有了Connection,就可以創建Session,而一旦有了Session,就可以創建Message、MessageProducer和MessageConsumer。
2、JMS點對點API
點對點(p2p)消息傳送模型API是指JMS API之內基於隊列(Queue)的接口:
QueueConnectionFactory
QueueConnection
QueueSession
Message
Queue
QueueSender
QueueReceiver
從接口的命名可以看出,大多數接口名稱僅僅是在公共API接口名稱之前添加Queue一詞。一般來說,使用點對點消息傳送模型的應用程序將使用基於隊列的API,而不使用公共API 。
3、JMS發佈/訂閱API
發佈/訂閱消息傳送模型API是指JMS API之內基於主題(Topic)的接口:
TopicConnectionFactory
TopicConnection
TopicSession
Message
Topic
TopicPublisher
TopicSubscriber
由於基於主題(Topic)的JMS API類似於基於隊列(Queue)的API,因此在大多數情況下,Queue這個詞會由Topic取代。
7 ActiveMQ點對點發送與接收消息示例
1、加入jms 和 activemq 的相關依賴
2、參考樣例代碼編寫一個消費發送者 和 一個消息接收者
3、運行發送者和接收者的代碼,在activeMQ的web控制檯觀察消息數據
<dependencies>
<!-- JMS規範的jar依賴 -->
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0.1</version>
</dependency>
<!-- activeMQ對jms具體實現的jar依賴 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.15.8</version>
</dependency>
<!--slf4j日誌的簡易實現-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
public class QueueSender {
public static final String BROKER_URL = "tcp://localhost:61616";
public static final String DESTINATION = "myQueue";
public static void main(String[] args) {
//1、創建連接工廠
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
Connection connection = null;
Session session = null;
MessageProducer producer = null;
try {
//2、創建一個連接
connection = connectionFactory.createConnection();
//3、創建一個Session,第一個參數表示是否爲事務消息,第二個參數表示消息確認方式
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
//4、創建消息
Message message = session.createTextMessage("Hello,ActiveMQ");
//5、消息目的地
Destination destination = session.createQueue(DESTINATION);
//6、消息生產者
producer = session.createProducer(destination);
//7、發送消息
producer.send(message);
} catch (Exception e) {
} finally {
if (producer != null) {
try {
producer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
//連接的關閉
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
public class QueueReceiver {
public static final String BROKER_URL = "tcp://localhost:61616";
public static final String DESTINATION = "myQueue";
public static void main(String[] args) {
//1、創建連接工廠
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
Connection connection = null;
Session session = null;
MessageConsumer messageConsumer = null;
try {
//2、創建一個連接
connection = connectionFactory.createConnection();
//接收消息多一個步驟:把連接啓動
connection.start();
//3、創建一個Session,第一個參數表示是否爲事務消息,第二個參數表示消息確認方式
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
//4、創建消息
Message message = session.createTextMessage("Hello,ActiveMQ");
//5、消息目的地
Destination destination = session.createQueue(DESTINATION);
//6、消息消費者
messageConsumer = session.createConsumer(destination);
//7、接收消息:receive方法是一個阻塞方法,它會一直等待,直到接收到消息
// receive(Long 超時毫秒數) receiveNoWait()不等待
Message receiveMessage = messageConsumer.receive();
if (receiveMessage instanceof TextMessage) {
String text = ((TextMessage) message).getText();
System.out.println("接收到的消息爲:" + text);
}
} catch (Exception e) {
} finally {
if ( messageConsumer!= null) {
try {
messageConsumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
//連接的關閉
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
接收到的消息爲:Hello,ActiveMQ
Process finished with exit code 0
8 ActiveMQ發佈與訂閱示例
public class TopicPublisher {
public static final String BROKER_URL = "tcp://localhost:61616";
public static final String DESTINATION = "myTopic";
public static void main(String[] args) {
//1、創建連接工廠
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
Connection connection = null;
Session session = null;
MessageProducer producer = null;
try {
//2、創建一個連接
connection = connectionFactory.createConnection();
//3、創建一個Session,第一個參數表示是否爲事務消息,第二個參數表示消息確認方式
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
//4、創建消息
Message message = session.createTextMessage("Hello,ActiveMQ");
//5、消息目的地
Destination destination = session.createTopic(DESTINATION);
//6、消息生產者
producer = session.createProducer(destination);
//7、發送消息
producer.send(message);
} catch (Exception e) {
} finally {
if (producer != null) {
try {
producer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
//連接的關閉
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
public class TopicSubscriber {
public static final String BROKER_URL = "tcp://localhost:61616";
public static final String DESTINATION = "myTopic";
public static void main(String[] args) {
//1、創建連接工廠
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(BROKER_URL);
Connection connection = null;
Session session = null;
MessageConsumer messageConsumer = null;
try {
//2、創建一個連接
connection = connectionFactory.createConnection();
//接收消息多一個步驟:把連接啓動
connection.start();
//3、創建一個Session,第一個參數表示是否爲事務消息,第二個參數表示消息確認方式
session = connection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
//4、創建消息
Message message = session.createTextMessage("Hello,ActiveMQ");
//5、消息目的地
Destination destination = session.createTopic(DESTINATION);
//6、消息消費者
messageConsumer = session.createConsumer(destination);
//7、接收消息:receive方法是一個阻塞方法,它會一直等待,直到接收到消息
// receive(Long 超時毫秒數) receiveNoWait()不等待
while (true) {
Message receiveMessage = messageConsumer.receive();
if (receiveMessage instanceof TextMessage) {
String text = ((TextMessage) message).getText();
//可以進行業務邏輯的處理
System.out.println("接收到的消息爲:" + text);
}
}
} catch (Exception e) {
} finally {
if ( messageConsumer!= null) {
try {
messageConsumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
if (session != null) {
try {
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
//連接的關閉
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
注意:
發佈訂閱模式必須先啓動訂閱者,再啓動發佈者,否則消息接收不到
9 Queue與Topic比較
Queue模式,如果消息沒有接收,消息存放在服務器,重啓後消息還在。
Topic模式,沒有被接收的消息,重啓服務器後,消息就不存在。
10 拉模式與推模式
點對點消息,如果沒有消費者在監聽隊列,消息將保留在隊列中,直至消息消費者連接到隊列爲止。在這種模型中,消息不是自動推動給消息消費者的,而是要由消息消費者從隊列中請求獲得 (拉模式)。
pub/sub消息傳遞模型基本上是一個推模型。在該模型中,消息會自動廣播,消息消費者無須通過主動請求或輪詢主題的方法來獲得新的消息。