消息中間件
介紹
什麼是JMS:
jms的全稱是Java message service(Java消息服務),jms是jdk底層定義的規範,各大廠商都有實現這個規範的技術。
作用:在soa分佈式架構系統中,或企業的多個項目中,進行多個系統異步傳遞消息。
同步和異步:
- 同步技術:
dubbo是一中同步技術, 實時性高, controller調用service項目, 調用就執行, 如果service項目中的代碼沒有執行完, controller裏面的代碼一致等待結果 - 異步技術:
mq消息中間件技術(jms) 是一種異步技術, 消息發送方, 將消息發送給消息服務器, 消息服務器未必立即處理.什麼時候去處理, 主要看消息服務器是否繁忙, 消息進入服務器後會進入隊列中, 先進先出.實時性不高.
使用場景:
場景一:使用消息服務器當做大的隊列使用, 先進先出, 來處理高併發寫入操作
場景二:使用消息服務器可以將業務系統的串行執行改爲並行執行, 處理效率高, 更合理的榨取服務器的性能.
缺點:
消息中間件實時性不高, 因爲發送方將消息發送給消息服務器後, 消息服務器未必能夠立即處理, 主要看消息服務器是否繁忙, 隊列中先進先出, 如果數據量大, 要排隊等待執行
jms消息服務器同類型技術:
ActiveMQ
:
是apache的一個比較老牌的消息中間件, 它比較均衡, 既不是最安全的, 也不是最快的.RabbitMQ
:
是阿里巴巴的一個消息中間件, 更適合金融類業務, 它對數據的安全性比較高.能夠保證數據不丟失.ZeroMQ
:
史上最快的消息隊列系統Kafka
:
Apache下的一個子項目。特點:高吞吐,在一臺普通的服務器上既可以達到10W/s的吞吐速率;完全的分佈式系統。適合處理海量數據。
JMS中支持的消息類型:
- TextMessage–一個字符串對象
- MapMessage–一套名稱-值對
- ObjectMessage–一個序列化的 Java 對象(可傳遞集合)
- BytesMessage–一個字節的數據流
- StreamMessage – Java 原始值的數據流
JMS消息傳遞類型
兩種發送模式
- 點對點模式: 一個發送方, 一個接收方. 也可以多個發送方, 一個接收方, 主要是接收方必須是第一個.
- 訂閱發佈模式: 一個發送方, 多個接收方. 發送方也可以是多個, 主要看接收方, 接收方必須是多個
ActiveMQ小例子
代碼只做瞭解,因爲不同的jms,代碼完全不同,所以之後會通過spring來進行管理
點對點
當發送方發送數據之後,接收方不論是先啓動還是後啓動,都能夠接收到數據。
接收方
package cn.itcast.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* 點對點模式 接收方
*/
public class QueueConsumer {
public static void main(String[] args) throws Exception{
//1.創建連接工廠
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.200.128:61616");
//2.獲取連接
Connection connection = connectionFactory.createConnection();
//3.啓動連接
connection.start();
//4.獲取session (參數1:是否啓動事務,參數2:消息確認模式)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.創建隊列對象
Queue queue = session.createQueue("test-queue");
//6.創建消息消費
MessageConsumer consumer = session.createConsumer(queue);
//7.監聽消息
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage=(TextMessage)message;
try {
System.out.println("接收到消息:"+textMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
//8.等待鍵盤輸入
System.in.read();
//9.關閉資源
consumer.close();
session.close();
connection.close();
}
}
發送方
package cn.itcast.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* 點對點模式 發送方
*/
public class QueueProducer {
public static void main(String[] args) throws Exception{
//1.創建連接工廠
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.200.128:61616");
//2.獲取連接
Connection connection = connectionFactory.createConnection();
//3.啓動連接
connection.start();
//4.獲取session (參數1:是否啓動事務,參數2:消息確認模式)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.創建隊列對象,指定發送的隊列名稱,可以隨意起名,但發送到哪裏,就要從哪裏接收
Queue queue = session.createQueue("test-queue");
//6.創建消息生產者
MessageProducer producer = session.createProducer(queue);
//7.創建消息
TextMessage textMessage = session.createTextMessage("歡迎來到神奇的品優購世界");
//8.發送消息
producer.send(textMessage);
//9.關閉資源
producer.close();
session.close();
connection.close();
}
}
訂閱發佈
發送方發完數據之後,數據就被髮送出去了,如果接收方沒有提前啓動,則無法收到數據。
發送方
package cn.itcast.topic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* 訂閱發佈模式:發送方
*/
public class TopicProducer {
public static void main(String[] args) throws Exception{
//1.創建連接工廠
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.200.128:61616");
//2.獲取連接
Connection connection = connectionFactory.createConnection();
//3.啓動連接
connection.start();
//4.獲取session (參數1:是否啓動事務,參數2:消息確認模式)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.創建主題對象
Topic topic = session.createTopic("test-topic");
//6.創建消息生產者
MessageProducer producer = session.createProducer(topic);
//7.創建消息
TextMessage textMessage = session.createTextMessage("歡迎來到神奇的品優購世界");
//8.發送消息
producer.send(textMessage);
//9.關閉資源
producer.close();
session.close();
connection.close();
}
}
n個接收方
package cn.itcast.topic;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
* 訂閱發佈模式:接收方
*/
public class TopicConsumer1 {
public static void main(String[] args) throws Exception{
//1.創建連接工廠
ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.200.128:61616");
//2.獲取連接
Connection connection = connectionFactory.createConnection();
//3.啓動連接
connection.start();
//4.獲取session (參數1:是否啓動事務,參數2:消息確認模式)
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.創建主題對象
//Queue queue = session.createQueue("test-queue");
Topic topic = session.createTopic("test-topic");
//6.創建消息消費
MessageConsumer consumer = session.createConsumer(topic);
//7.監聽消息
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage=(TextMessage)message;
try {
System.out.println("接收到消息:"+textMessage.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
//8.等待鍵盤輸入
System.in.read();
//9.關閉資源
consumer.close();
session.close();
connection.close();
}
}
如何保證消息服務器不丟消息
答: 我們使用的是activemq消息服務器, 它有兩種發送模式點對點和訂閱發佈
- 點對點:
不用管, 消息服務器有內部機制保證絕對不會丟數據.
原理:
發送方將消息發送給消息服務器後, 消息服務器會將隊列中的消息發送給接收方, 接收方接收到消息後會給消息服務器返回一個響應, 消息服務器接收到響應後認爲消息接收成功, 將隊列中這個發送完的消息刪除。
如果消息服務器在一定時間內沒有接受到響應, 則認爲消息接收方宕機, 這個時候會將發送完的這個消息進行持久化保存, 如果消息接收方服務器重新啓動連接消息服務器, 消息服務器會監聽到, 會將之前持久化的消息重新發送給消息服務器. - 訂閱發佈:
訂閱發佈模式沒有實現保證數據安全的機制, 如果消息服務器將消息發送給接收方, 接收方接收到就算接收到了,沒接收到就算丟了, 也不進行持久化.