JMS與Active MQ入門
JMS是什麼
JMS(Java Messaging Service)是Java平臺上有關面向消息中間件的技術規範,實際上是一套api,它便於消息系統中的Java應用程序進行消息交換,並且通過提供標準的產生、發送、接收消息的接口簡化企業應用的開發,ActiveMQ而是這個規範的一個具體實現。JMS是一種與廠商無關的 API,用來訪問收發系統消息,它類似於JDBC.
JMS對象模型
- 連接工廠:連接工廠負責創建一個JMS連接。
- JMS連接:JMS連接(Connection)表示JMS客戶端和服務器端之間的一個活動的連接,是由客戶端通過調用連接工廠的方法建立的。
- JMS會話:JMS會話(Session)表示JMS客戶與JMS服務器之間的會話狀態。JMS會話建立在JMS連接上,表示客戶與服務器之間的一個會話線程。
- JMS目的/ Broker:客戶用來指定它生產的消息的目標和它消費的消息的來源的對象,一個消息中間件的實例。
- JMS生產者和消費者:生產者(Message Producer)和消費者(Message Consumer)對象由Session對象創建,用於發送和接收消息。
JMS中的消息
JMS 消息由以下三部分組成:
- 消息頭:每個消息頭字段都有相應的getter 和setter 方法。
- 消息屬性:如果需要除消息頭字段以外的值,那麼可以使用消息屬性。
- 消息體:JMS 定義的消息類型有TextMessage、MapMessage、BytesMessage、StreamMessage 和 ObjectMessage。ActiveMQ也有對應的實現。
JMS消息模型
上次的《基於異步消息模式的通信》中已對此有所介紹,這裏再詳細說一下:
Point-to-Point(P2P) / 點對點
消息通過稱爲隊列的一個虛擬通道來進行交換。隊列是生產者發送消息的目的地和接受者消費消息的消息源。
每條消息通僅會傳送給一個接受者。可能會有多個接受者在一個隊列中偵聽,但是每個隊列中的消息只能被隊列中的一個接受者消費。
消息存在先後順序。一個隊列會按照消息服務器將消息放入隊列中的順序,把它們傳送給消費者當消息已被消費時,就會從隊列頭部將它們刪除。
每個消息只有一個消費者(Consumer)(即一旦被消費,消息就不再在消息隊列中)
發送者發送了消息之後,不管接收者有沒有正在運行,它不會影響到消息被髮送到隊列
接收者在成功接收消息之後需向隊列應答成功
如果希望發送的每個消息都應該被成功處理的話,使用這個P2P模式。
Topic/ 主題(發佈訂閱(Pub/Sub) )
消息生產者(發佈)將消息發佈到topic中,同時有多個消息消費者(訂閱)消費該消息。和點對點方式不同,發佈到topic的消息會被所有訂閱者消費。
如果你希望發送的消息可以不被做任何處理、或者被一個消息者處理、或者可以被多個消費者處理的話,那麼可以採用topic模型
消息的消費方式
- 同步消費:通過調用 消費者的receive 方法從目的地中顯式提取消息。receive 方法可以一直阻塞到消息到達。
- 異步消費:客戶可以爲消費者註冊一個消息監聽器,以定義在消息到達時所採取的動作。
Active MQ入門
Active MQ 下載與啓動
- 下載:http://activemq.apache.org/components/classic/download/,下載後解壓
- 啓動:運行bin目錄下的activemq.bat即可。Linux下操作類似(進入bin目錄運行./activemq start啓動,./activemq stop關閉)
運行後在瀏覽器中訪問http://127.0.0.1:8161/admin,即可看到ActiveMQ的管理控制檯
ActiveMQ中,61616爲服務端口,8161爲管理控制檯端口。
使用原生Active MQ
新建一個maven項目,導入Active MQ的pom依賴。
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.8.0</version>
</dependency>
消息生產端
public class JmsProducer {
/*默認連接用戶名*/
private static final String USERNAME
= ActiveMQConnection.DEFAULT_USER;
/* 默認連接密碼*/
private static final String PASSWORD
= ActiveMQConnection.DEFAULT_PASSWORD;
/* 默認連接地址*/
private static final String BROKEURL
= ActiveMQConnection.DEFAULT_BROKER_URL;
private static final int SENDNUM = 3;
public static void main(String[] args) {
/* 連接工廠*/
ConnectionFactory connectionFactory;
/* 連接*/
Connection connection = null;
/* 會話*/
Session session;
/* 消息的目的地*/
Destination destination;
/* 消息的生產者*/
MessageProducer messageProducer;
/* 實例化連接工廠*/
connectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD,
BROKEURL);
try {
/* 通過連接工廠獲取連接*/
connection = connectionFactory.createConnection();
/* 啓動連接*/
connection.start();
/* 創建session
* 第一個參數表示是否使用事務,第二次參數表示是否自動確認*/
session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
/* 創建一個名爲HelloWorld消息隊列*/
//destination = session.createTopic("HelloActiveMq");
destination = session.createQueue("HelloActiveMqQueue");
/* 創建消息生產者*/
messageProducer = session.createProducer(destination);
/* 循環發送消息*/
for(int i=0;i<SENDNUM;i++){
String msg = "發送消息"+i+" "+System.currentTimeMillis();
TextMessage textMessage = session.createTextMessage(msg);
System.out.println("標準用法:"+msg);
messageProducer.send(textMessage);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消息消費端
public class JmsConsumer {
/*默認連接用戶名*/
private static final String USERNAME
= ActiveMQConnection.DEFAULT_USER;
/* 默認連接密碼*/
private static final String PASSWORD
= ActiveMQConnection.DEFAULT_PASSWORD;
/* 默認連接地址*/
private static final String BROKEURL
= ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args) {
/* 連接工廠*/
ConnectionFactory connectionFactory;
/* 連接*/
Connection connection = null;
/* 會話*/
Session session;
/* 消息的目的地*/
Destination destination;
/* 消息的消費者*/
MessageConsumer messageConsumer;
/* 實例化連接工廠*/
connectionFactory
= new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKEURL);
try {
/* 通過連接工廠獲取連接*/
connection = connectionFactory.createConnection();
/* 啓動連接*/
connection.start();
/* 創建session*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/* 創建一個名爲HelloWorld消息隊列*/
//destination = session.createTopic("HelloActiveMq");
destination = session.createQueue("HelloActiveMqQueue");
/* 創建消息消費者*/
messageConsumer = session.createConsumer(destination);
Message message;
while((message = messageConsumer.receive())!=null){
System.out.println(((TextMessage)message).getText());
}
} catch (JMSException e) {
e.printStackTrace();
}finally {
if(connection!=null){
try {
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
}
消息異步消費端
public class JmsConsumerAsyn {
/*默認連接用戶名*/
private static final String USERNAME
= ActiveMQConnection.DEFAULT_USER;
/* 默認連接密碼*/
private static final String PASSWORD
= ActiveMQConnection.DEFAULT_PASSWORD;
/* 默認連接地址*/
private static final String BROKEURL
= ActiveMQConnection.DEFAULT_BROKER_URL;
public static void main(String[] args) {
/* 連接工廠*/
ConnectionFactory connectionFactory;
/* 連接*/
Connection connection = null;
/* 會話*/
Session session;
/* 消息的目的地*/
Destination destination;
/* 消息的消費者*/
MessageConsumer messageConsumer;
/* 實例化連接工廠*/
connectionFactory
= new ActiveMQConnectionFactory(USERNAME,PASSWORD,BROKEURL);
try {
/* 通過連接工廠獲取連接*/
connection = connectionFactory.createConnection();
/* 啓動連接*/
connection.start();
/* 創建session*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
/* 創建一個名爲HelloWorld消息隊列*/
//destination = session.createTopic("HelloActiveMq");
destination = session.createQueue("HelloActiveMqQueue");
/* 創建消息消費者*/
messageConsumer = session.createConsumer(destination);
/* 設置消費者監聽器,監聽消息*/
messageConsumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
try {
System.out.println(((TextMessage)message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
} catch (JMSException e) {
e.printStackTrace();
}
}
}
Active MQ在Spring中的使用
1、添加依賴
首先我們先要搭建一個Spring 的maven項目。
然後我們在pom文件中添加active mq的依賴與:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.11.RELEASE</version>
</dependency>
2、配置文件applicationContext.xml
命名空間的添加
xmlns:amq="http://activemq.apache.org/schema/core"
http://activemq.apache.org/schema/core
http://activemq.apache.org/schema/core/activemq-core-5.8.0.xsd
消費者的命名空間要再額外添加如下:
xmlns:jms="http://www.springframework.org/schema/jms"
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-4.0.xsd
ActiveMQ 連接工廠
<amq:connectionFactory id="amqConnectionFactory"
brokerURL="tcp://127.0.0.1:61616" userName="" password="" />
Spring Caching連接工廠
<!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="amqConnectionFactory"></property>
<property name="sessionCacheSize" value="100"></property>
</bean>
3、消息生產者配置以及代碼的編寫
Spring JmsTemplate 的消息生產者
<!-- 定義JmsTemplate的Queue類型 -->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<constructor-arg ref="connectionFactory"></constructor-arg>
<!-- 隊列模式-->//true爲發佈訂閱模式
<property name="pubSubDomain" value="false"></property>
</bean>
然後便可以使用JmsTemplate
@Autowired
@Qualifier("jmsQueueTemplate")
private JmsTemplate jmsTemplate;
public void send(String queueName,final String message){
jmsTemplate.send(queueName, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
Message msg = session.createTextMessage(message);
//TODO 應答
return msg;
}
});
3、消息消費者配置以及代碼的編寫
定義Queue監聽器
<jms:listener-container destination-type="queue" container-type="default"
connection-factory="connectionFactory" acknowledge="auto">
<jms:listener destination="test.queue" ref="queueReceiver1"></jms:listener>
<jms:listener destination="test.queue" ref="queueReceiver2"></jms:listener>
</jms:listener-container>
queueReceiver1的編寫
@Component
public class QueueReceiver1 implements MessageListener {
public void onMessage(Message message) {
try {
String textMsg = ((TextMessage)message).getText();
System.out.println("QueueReceiver1 accept msg : "+textMsg);
} catch (JMSException e) {
e.printStackTrace();
}
}
}