標籤:set list 上下文 接收 try 處理 叢集配置 int 學習筆記
#### 訊息中介軟體
訊息中介軟體:關注於資料的發送和接受,利用高效可靠的非同步訊息傳遞機制整合分布式系統
JMS:JavaMessage Service,Java平台中關於面向訊息中介軟體的API
AMQP:提供整合通訊服務的應用程式層標準協議
常見訊息中介軟體
ActiveMQ
RabbitMQ
Kafka
JMS規範
提供者:實現JMS規範的訊息中介軟體伺服器
用戶端:發送或接受訊息的應用程式
生產者/發行者:建立並發送訊息的用戶端
消費者/訂閱者:接收並處理訊息的用戶端
訊息:應用程式之間傳遞的資料內容
訊息模式:在用戶端之間傳遞訊息的方式,JMS中定義了主題和隊列兩種模式
JMS訊息模式
隊列模型:
- 用戶端包括生產者和消費者
- 訊息只能被一個消費者消費
- 隨時消費
主題模型:
- 用戶端包括髮布者和訂閱者
- 訊息能被所有訂閱者消費
- 消費者不能消費訂閱之前就發送到主題中的訊息
JMS編碼介面:
- ConnectionFactory:用於建立串連到訊息中介軟體的串連工廠
- Connection:代表了應用程式和Message Service器之間的通訊鏈路
- Destination:訊息發布和接收的地點,包括隊列和主題
- Session:表示一個單線程的上下文,用於發送和接收訊息
- MessageConsumer:由會話建立,用於接收發送到目標的訊息
- MessageProducer:由會話建立,用於發送訊息到目標
- Message:在消費者和生產者之間傳送的對象,包括訊息頭,一組訊息屬性,一個訊息體
使用ActiveMQ
隊列模型
producer
//1. 建立ConnectionFactory ConnectionFactory factory = new ActiveMQConnectionFactory(url); //2. 建立Connection Connection connection = factory.createConnection(); //3. 啟動Connection connection.start(); //4. 建立Session Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5. 建立Destination Destination destination = session.createQueue(queueName); //6. 建立MessageProducer MessageProducer producer = session.createProducer(destination); for (int i = 0; i < 100; i++) { //7. 建立訊息 TextMessage message = session.createTextMessage("test" + i); //8. 發布訊息 producer.send(message); System.out.println("發送訊息: " + message.getText()); } //9. 關閉串連 connection.close();
consumer
//1. 建立ConnectionFactory ConnectionFactory factory = new ActiveMQConnectionFactory(url); //2. 建立Connection Connection connection = factory.createConnection(); //3. 啟動Connection connection.start(); //4. 建立Session Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5. 建立Destination Destination destination = session.createQueue(queueName); //6. 建立MessageConsumer MessageConsumer consumer = session.createConsumer(destination); //7. 建立訊息監聽器 consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("接收訊息: " + textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }); //9. 關閉串連(訊息監聽非同步執行,需程式全部運行結束才能關閉串連)// connection.close();
主題模型
producer
//1. 建立ConnectionFactory ConnectionFactory factory = new ActiveMQConnectionFactory(url); //2. 建立Connection Connection connection = factory.createConnection(); //3. 啟動Connection connection.start(); //4. 建立Session Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5. 建立Destination Destination destination = session.createTopic(topicName); //6. 建立MessageProducer MessageProducer producer = session.createProducer(destination); for (int i = 0; i < 100; i++) { //7. 建立訊息 TextMessage message = session.createTextMessage("test" + i); //8. 發布訊息 producer.send(message); System.out.println("發送訊息: " + message.getText()); } //9. 關閉串連 connection.close();
consumer
//1. 建立ConnectionFactory ConnectionFactory factory = new ActiveMQConnectionFactory(url); //2. 建立Connection Connection connection = factory.createConnection(); //3. 啟動Connection connection.start(); //4. 建立Session Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5. 建立Destination Destination destination = session.createTopic(topicName); //6. 建立MessageConsumer MessageConsumer consumer = session.createConsumer(destination); //7. 建立訊息監聽器 consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("接收訊息: " + textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }); //9. 關閉串連(訊息監聽非同步執行,需程式全部運行結束才能關閉串連)// connection.close();
spring jms
- ConnectionFactory 用於管理串連的串連工廠
- 由spring提供
- SingleConnectionFactory和CachingConnectionFactory
- JmsTemplate 用於發送和接收訊息的模板類
- 由spring提供,在容器中註冊就可以使用
- 安全執行緒
- MessageListener 訊息監聽器
- 實現一個onMessage方法,只接收一個Message參數
spring使用jms樣本
common.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:annotation-config /> <!-- ActiveMQ為我們提供的ConnectionFactory --> <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://127.0.0.1:61616" /> </bean> <!-- spring jms為我們提供串連池 --> <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory" ref="targetConnectionFactory" /> </bean> <!-- 一個隊列目的地,點對點的 --> <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> <constructor-arg value="queue" /> </bean> <!-- 一個主題目的地,發布訂閱訊息 --> <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic"> <constructor-arg value="topic"/> </bean></beans>
producer.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <import resource="common.xml" /> <!-- 配置JmsTemplate,用於發送訊息--> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory" /> </bean> <bean class="com.qyluo.jms.spring.producer.ProducerServiceImpl" /></beans>
cosumer.xml
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 匯入公用配置 --> <import resource="common.xml" /> <!-- 配置訊息監聽器 --> <bean id="consumerMessageListener" class="com.qyluo.jms.spring.consumer.ConsumerMessageListener" /> <!-- 配置訊息監聽容器 --> <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="topicDestination"/> <property name="messageListener" ref="consumerMessageListener"/> </bean></beans>
ProducerServiceImpl
public class ProducerServiceImpl implements ProducerService { @Autowired JmsTemplate jmsTemplate; @Resource(name = "topicDestination") Destination destination; @Override public void sendMessage(final String message) { //使用JmsTemplate發送訊息 jmsTemplate.send(destination, new MessageCreator() { //建立一個訊息 @Override public Message createMessage(Session session) throws JMSException { TextMessage textMessage = session.createTextMessage(message); return textMessage; } }); System.out.println("發送訊息: " + message); }}
AppProducer
public class AppProducer { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("producer.xml"); ProducerService service = context.getBean(ProducerService.class); for (int i = 0; i < 100; i++) { service.sendMessage("text" + i); } context.close(); }}
ConsumerMessageListener
public class ConsumerMessageListener implements MessageListener { @Override public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("接收訊息: " + textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }
AppConsumer
public class AppConsumer { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml"); }}
ActiveMQ叢集
叢集方式
- 用戶端叢集:多個消費者消費同一個隊列
- Broker clusters:多個Broker之間同步訊息
- Master Slave:實現高可用
ActiveMQ失效轉移(failover)
允許當其中一台Message Service器宕機時,用戶端在傳輸層上重新串連到其它Message Service器
文法:failover:(uri1,...,uriN)?transportOptions
transportOptions參數說明
- randomize 預設為true,表示在URI列表中選擇URI串連時是否採用隨機策略
- initialReconnectDelay 預設為10,單位毫秒,表示第一次嘗試重新串連之間等待的時間
- maxReconnectDelay 預設為30000,單位毫秒,最長重連的時間間隔
Broker Cluster叢集配置
NetworkConnector(網路連接器):ActiveMQ伺服器之間的網路通訊方式
分為靜態連接器和動態連接器
靜態連接器:
<networkConnectors> <networkConnector uri="static:(tcp://127.0.0.1:61617,tcp://127.0.0.1:61618)"/></networkConnectors>
動態連接器:
<networkConnectors> <networkConnector uri="multicast://default"/></networkConnectors><transportConnectors> <transportConnector uri="tcp://localhost:0" discoverUri="multicast://default"/></transportConnectors>
Master/Slave叢集配置
ActiveMQ Master Slave叢集方案
- Share nothing storage master/slave (已淘汰,5.8+後移除)
- Shared storage master/slave 共用儲存
- Replicated LevelDB Store 基於複製的LevelDB Store
兩種叢集方式對比
方式 | 高可用 | 負載平衡 |
--|----------|--------------|
Master/Slave | 是 | 否 |
Broker Cluster | 否 | 是 |
三台伺服器的完美叢集方案
Node A和Node B做訊息同步,Node A和Node C做訊息同步,Node B和Node C做Master / Slave對資源進行持久化
| 伺服器 |
服務連接埠 |
管理連接埠 |
儲存 |
網路連接器 |
用途 |
| Node-A |
61616 |
8161 |
- |
Node-B、Node-C |
消費者 |
| Node-B |
61617 |
8162 |
/share_file/kahadb |
Node-A |
生產者,消費者 |
| Node-C |
61618 |
8163 |
/share_file/kahadb |
Node-A |
生產者,消費者 |
企業系統中的最佳實務
實際業務情境特點
- 子業務系統都有叢集的可能性
- 同一個訊息會廣播給關注該類訊息的所有子業務系統
- 同一類訊息在叢集中被負載消費
- 業務的發生和訊息的發布最終一致性
使用ActiveMQ的虛擬主題解決方案
- 發行者:將訊息發布到一個主題中,主題名以VirtualTopic開頭,如VirtualTopic.TEST
- 消費者:從隊列中擷取訊息,在隊列名中表明自己身份,如Consumer.A.VirtualTopic.TEST
使用JMS中XA系列介面保證強一致性
使用訊息表的本地事務解決方案
使用記憶體日誌的解決方案
基於訊息機制的事件匯流排
事件驅動架構
RabbitMQ
RabbitMQ:使用交換器綁定到隊列
- 建立ConnectionFactory
- 建立Connection
- 建立Channel
- 定義Exchange,類型I必須為fanout
- 定義Queue並且綁定隊列
Kafka
Kafka使用group.id分組消費者
- 配置訊息者參數group.id相同時對訊息進行負載處理
- 設定管理員partitions參數,控制同一個group.id下的consumer數量小於partitions
- kafka只保證同一個group.id下的訊息是有序的
學習筆記--Java訊息中介軟體