學習慕課網java訊息中介軟體記些筆記 1.概述
中介軟體
非底層作業系統軟體,非業務應用軟體,不是直接給終端使用者使用的,不能直接給客戶帶來價值的軟體統稱為中介軟體。
訊息中介軟體
管制關注於資料的發送和接收,利用高效可靠的非同步訊息傳遞機制整合分布式系統。
優點
① 解耦 ② 非同步 ③ 橫向擴充 ④ 安全可靠 ⑤ 順序保證(比如kafka)
jms
javaMessage Service(Java Message Service)即JMS,是一個Java平台中關於面向訊息中介軟體的api,用於在兩個應用程式之間,或分布式系統中發送訊息,進行非同步通訊
什麼是AMQP
AMQP(advanced message queuing protocol)是一個提供整合通訊服務的應用程式層標準協議,基於此協議的用戶端與訊息中介軟體可傳遞訊息,並不受用戶端/中介軟體不同產品,不同開發語言等條件的限制
常見訊息中介軟體
activeMQ(支援多語言,實現jms1.1,j2ee1.4規範),RabbitMQ(支援更多語言,基於AMQP規範),kafka(高輸送量,分布式,分區,O(1)磁碟順序提供訊息持久化)
JMS訊息模式
隊列模式
用戶端包括生產者和消費者
隊列中的訊息只能被一個消費者消費
消費者可以隨時消費隊列中的訊息
主題模式
用戶端包括髮布者和訂閱者
主題中的訊息被所有訂閱者消費
消費者不能消費訂閱之前就發送到主題中的訊息
JMS編碼介面
ConnectionFactory 用於建立串連到訊息中介軟體的串連工廠
Connection 代表了應用程式和Message Service器之間的通訊鏈路
Destination 指訊息發布和接收的地點,包括隊列或主題
Session 表示一個單線程的上下文,用於發送和接收訊息
MessageConsumer 由會話建立,用於接收發送到目標的訊息
MessageProducer 由會話建立,用於發送訊息到目標
Message 是在消費者和生產者之間傳送的對象,訊息頭,一組訊息屬性,一個訊息體
流程
2.安裝activeMQ
官網 : http://activemq.apache.org/
第一種啟動
進入bin ,activemq.bat 啟動
進入瀏覽器 http://127.0.0.1:8161
使用者名稱密碼預設為admin
第二種以服務啟動
InstallService.bat以管理員身份運行
服務中有ActiveMQ
linux
解壓壓縮包
進入bin 輸入 activemq start,啟動完成 3.jms 示範
建立maven項目
添加依賴
<dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-all</artifactId> <version>5.9.0</version> </dependency>
建立隊列模式的生產者AppProducer
package com.jms.queue;import javax.jms.Connection;import javax.jms.ConnectionFactory;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.MessageProducer;import javax.jms.Session;import javax.jms.TextMessage;import org.apache.activemq.ActiveMQConnectionFactory;public class AppProducer { public static final String url = "tcp://localhost:61616"; public static final String queueName = "queue-test "; public static void main(String[] args) throws JMSException { //1.建立串連工廠 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); //2.建立Connection Connection connection = connectionFactory.createConnection(); //3.啟動串連 connection.start(); //4.建立會話 第一個參數:是否在事務中去處理, 第二個參數.接聽模式 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5.建立一個目標 Destination destination = session.createQueue(queueName); //6建立一個生產者 MessageProducer producer = session.createProducer(destination); for (int i = 0; i < 100; i++) { //7.創基建訊息 TextMessage textMessage = session.createTextMessage("test:"+i); producer.send(textMessage); System.out.println("發送訊息:"+textMessage.getText()); } //9關閉串連 connection.close(); }}
運行項目,開啟activeMQ管理工具
100個訊息,沒有消費者消費,串連已關閉
消費者 AppConsumer
package com.jms.queue;import javax.jms.Connection;import javax.jms.ConnectionFactory;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageConsumer;import javax.jms.MessageListener;import javax.jms.Session;import javax.jms.TextMessage;import org.apache.activemq.ActiveMQConnectionFactory;public class AppConsumer { public static final String url = "tcp://localhost:61616"; public static final String queueName = "queue-test "; public static void main(String[] args) throws JMSException { //1.建立串連工廠 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); //2.建立Connection Connection connection = connectionFactory.createConnection(); //3.啟動串連 connection.start(); //4.建立會話 第一個參數:是否在事務中去處理, 第二個參數.接聽模式 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5.建立一個目標 Destination destination = session.createQueue(queueName); //6建立一個消費者 MessageConsumer consumber = session.createConsumer(destination); //7建立一個監聽器 consumber.setMessageListener(new MessageListener() { public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("接收訊息:"+textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }); //8關閉串連 訊息是非同步 ,在程式退出是關閉,在這裡不可以關閉 //connection.close(); }}
當啟動2個消費者,再啟動生產者,結果是2個消費者平均消費
建立主題模式發行者AppProducer
package com.jms.topic;import javax.jms.Connection;import javax.jms.ConnectionFactory;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.MessageProducer;import javax.jms.Session;import javax.jms.TextMessage;import org.apache.activemq.ActiveMQConnectionFactory;public class AppProducer { public static final String url = "tcp://localhost:61616"; public static final String topicName = "topic-test "; public static void main(String[] args) throws JMSException { //1.建立串連工廠 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); //2.建立Connection Connection connection = connectionFactory.createConnection(); //3.啟動串連 connection.start(); //4.建立會話 第一個參數:是否在事務中去處理, 第二個參數.接聽模式 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5.建立一個目標 Destination destination = session.createTopic(topicName); //6建立一個生產者 MessageProducer producer = session.createProducer(destination); for (int i = 0; i < 100; i++) { //7.創基建訊息 TextMessage textMessage = session.createTextMessage("test:"+i); producer.send(textMessage); System.out.println("發送訊息:"+textMessage.getText()); } //9關閉串連 connection.close(); }}
這時直接運行訂閱者接收不到訊息,因為發行者先運行了
訂閱者AppConsumer
package com.jms.topic;import javax.jms.Connection;import javax.jms.ConnectionFactory;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.MessageConsumer;import javax.jms.MessageListener;import javax.jms.Session;import javax.jms.TextMessage;import org.apache.activemq.ActiveMQConnectionFactory;public class AppConsumer { public static final String url = "tcp://localhost:61616"; public static final String topicName = "topic-test "; public static void main(String[] args) throws JMSException { //1.建立串連工廠 ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url); //2.建立Connection Connection connection = connectionFactory.createConnection(); //3.啟動串連 connection.start(); //4.建立會話 第一個參數:是否在事務中去處理, 第二個參數.接聽模式 Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); //5.建立一個目標 Destination destination = session.createTopic(topicName); //6建立一個消費者 MessageConsumer consumber = session.createConsumer(destination); //7建立一個監聽器 consumber.setMessageListener(new MessageListener() { public void onMessage(Message message) { TextMessage textMessage = (TextMessage) message; try { System.out.println("接收訊息:"+textMessage.getText()); } catch (JMSException e) { e.printStackTrace(); } } }); //8關閉串連 訊息是非同步 ,在程式退出是關閉 //connection.close(); }}
啟動兩個訂閱者,再啟動發行者,兩個訂閱者均可收到發行者的訊息 4.使用spring整合jms連結activeMQ
ConnectionFactory 用於管理串連的工廠
JmsTemplate 用於發送和接收訊息的模板類
MessageListerner 訊息監聽器
ConnectionFactory 是spring 為我們提供的串連池
兩種串連池SingleConnectionFactory 和 CachingConnectionFactory
SingleConnectionFactory 是對於jms建立請求,只會返回一個串連
CachingConnectionFactory 實現了SingleConnectionFactory 的所有功能,還提供了緩衝
JmsTemplate spring提供,安全執行緒,可以使用JmsTemplate 操作jms
MessageListerner 實現onMessage方法,接收Message參數
添加依賴
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-core</artifactId> <version>5.7.0</version> <exclusions> <exclusion> <artifactId>spring-context</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency>
這裡注意org.apache.activemq也依賴了spring-context 所以這裡要把他排除掉
生產者 建立介面ProducerService
package com.spring.producer;public interface ProducerService { void sendMessage(String message);}
實作類別
package com.spring.producer.impl;import javax.annotation.Resource;import javax.jms.Destination;import javax.jms.JMSException;import javax.jms.Message;import javax.jms.Session;import javax.jms.TextMessage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jms.core.JmsTemplate;import org.springframework.jms.core.MessageCreator;import org.springframework.stereotype.Service;import com.spring.producer.ProducerService;@Servicepublic class ProducerServiceImpl implements ProducerService { @Autowired private JmsTemplate jmsTemplate; @Resource(name= "queueDestination")//因為可能配置多個目的地,所以使用resource name進行區分 Destination destination; public void sendMessage(final String message) { //使用JmsTemplate發送訊息 jmsTemplate.send(destination,new MessageCreator() { //建立訊息 public Message createMessage(Session session) throws JMSException { TextMessage textMessage = session.createTextMessage(message); return textMessage; } }); System.out.println("發送訊息:"+message); }}
配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd"> <context:annotation-config></context:annotation-config> <context:component-scan base-package="com.spring.*" /> <!-- ActiveMQ提供 ConnectionFactory--> <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> <property name="brokerURL" value="tcp://localhost:61616"></property> </bean> <!-- spring-jms提供的串連池 --> <bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory"> <property name="targetConnectionFactory" ref="targetConnectionFactory"></property> </bean> <!-- 一個隊列目的地,點對點 --> <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> <!-- 指定隊列名字 --> <constructor-arg value="springQueue"></constructor-arg> </bean> <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"> <property name="connectionFactory" ref="connectionFactory"></property> </bean> <!-- <bean id="ProducerServiceImpl" class="com.spring.producer.impl.ProducerServiceImpl"></bean> --></beans>
啟動類
package com.spring.producer;import org.springframework.context.ApplicationContext;import org.springframework.context.support.AbstractApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class AppProducer { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("producer.xml"); ProducerService service = context.getBean(ProducerService.class); for(int i=0;i<100;i++){ service.sendMessage("test"+i); } //會自動清理資源 ((AbstractApplicationContext) context).close(); }}
可以把公用地方提取出來
<import resource="common.xml"/>
建立消費者
配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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-4.3.xsd