目錄:
1、介紹
介紹:
MQ全稱為Message Queue, 訊息佇列(MQ)是一種應用程式對應用程式的通訊方法。
應用程式通過讀寫出入佇列的訊息(針對應用程式的資料)來通訊,而無需專用連線來連結它們。
訊息傳遞指的是程式之間通過在訊息中傳送資料進行通訊,而不是通過直接呼叫彼此來通訊,直接呼叫通常是用於諸如遠端過程呼叫的技術。
排隊指的是應用程式通過 佇列來通訊。
佇列的使用除去了接收和傳送應用程式同時執行的要求。其中較為成熟的MQ產品有IBM WEBSPHERE MQ等等。
專案訪問資料庫的壓力問題
說明 :
雖然後臺的資料庫已經通過mycat實現了資料庫的高可用,並且可以抗擊部分高併發。但是如果併發壓力特別大。這時資料庫的執行必定滿負荷。很容易造成資料庫當機。
分析問題:
併發要求資料庫第一時間執行更新操作,但是資料庫現在滿負荷,沒有能力處理多餘的請求,導致容器產品當機風險。
解決辦法:
準備一個訊息佇列,如果有高併發的請求,資料庫處理不完,則將訊息存入佇列中.這時資料庫先處理自己的請求,當請求處理完成後,從訊息佇列中獲取請求.之後處理數.
這樣的做法,實現了使用者的請求和資料庫執行的非同步操作!!
架構升級:
說明:引入訊息佇列後,主要解決一個資料庫“更新併發壓力”較大的問題,使用訊息佇列的機制,可以讓後臺的整個架構的效能提升至少30%,
現在幾乎全部的軟體公司都在使用佇列。
伺服器的請求多。
mysql的處理有一定的峰值
使用佇列平衡這樣的關係。兩座上消除佇列的內在是無限的。
其他mq:
rocketMQ
kafkaMQ
linux安裝rabbitmq
1、匯入 rabbitmq-server-3.6.1-1.noarch.rpm
2、安裝命令:rpm -ivh rabbitmq-server-3.6.1-1.noarch.rpm
3、安裝完成
配置檔案修改:(rabbitmq.config)
{loopback_users, []}
複製配置檔案到cd etc/rabbitmq
啟動mq
rabbitmq-plugins enable rabbitmq_management
啟動:service rabbitmq-server start
停止:service rabbitmq-server stop
重啟:service rabbitmq-server restart
rabbitmq的埠號
1、15672 rabbitMQ控制檯埠
2、5672客戶端連線rabbitMQ的埠
瀏覽器遠端訪問:
192.168.220.134:15672
登陸賬號:guest 密碼:guest
channels:連結rabbitMQ的唯一通道
Exchanges:交換機,可以讓訊息發往指定的佇列中
queues:佇列:在訊息佇列中可以有無數個佇列
如果控制檯
使用者許可權設定
1、Admin > add user
2、定義虛擬主機:
自己維護的佇列的內容,包含路由/交換機/佇列
命名:/jt
3、為使用者分配虛擬主機(雙擊jtadmin進入)
rabbitMQ的工作模式
1、簡單模式
p:provider:訊息的提供者,訊息的發出者
c:Consumer:消費者,將訊息 進行處理
紅色區域:rabbitMQ
jar包匯入
<dependencies> <!-- 訊息佇列 --> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>1.4.0.RELEASE</version> </dependency> </dependencies>
測試程式碼:
package com.jt.rabbitmq; import java.io.IOException; import org.junit.Before; import org.junit.Test; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.ConsumerCancelledException; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.ShutdownSignalException; //測試rabbitMQ中的簡單模式 public class TestRabbitMQ_1_simple { private Connection connection = null; /** * rabbitMQ的連線步驟 * 1、通過使用者jtadmin連線rabbitMQ(ip:埠/使用者名稱/密碼/虛擬主機) * 2、定義訊息的提供者provider * 2.1、建立channel物件(控制佇列/路由/交換機等) * 2.2、定義訊息佇列 * 2.3、傳送訊息到佇列中 * @throws IOException */ //表示從rabbitMQ工廠中獲取連結 @Before public void initConnection() throws IOException{ //建立工廠物件 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("192.168.220.134"); factory.setPort(5672); factory.setUsername("jtadmin"); factory.setPassword("jtadmin"); factory.setVirtualHost("/jt"); //獲取連線 connection = factory.newConnection(); } /** * 建立生產者物件 * @throws IOException */ @Test public void provider() throws IOException{ //1、獲取channel物件,控制佇列和路由和交換機 Channel channel = connection.createChannel(); //2、定義佇列 /** * queue:佇列名稱 * durable:是否持久化,true:當訊息佇列重新啟動後,佇列還存在 * false:當佇列重啟後,佇列不存在。 * exclusive:獨有的 * 當前的訊息佇列是否由生產者獨佔,如果配置為true表示消費者不能 * autoDelete:是否自動刪除。如果為true,則訊息佇列中沒有訊息時,該佇列自動刪除。、 * arguments:提交的引數。一般為null */ channel.queueDeclare("queue_simple", false, false, false, null); //3、定義需要傳送的訊息 String msg = "我是簡單模式"; //將訊息與佇列繫結,並且傳送 /** * exchange:交換機名稱:如果沒有交換機,則為""串 * routingkey:訊息發往佇列的ID(引數),如果沒有路由key,則寫佇列名稱 * props:訊息傳送的額外的引數,如果沒有引數為null * body:傳送的訊息 的二進位制位元組碼檔案 */ channel.basicPublish("", "queue_simple", null, msg.getBytes()); //關閉流 channel.close(); connection.close(); System.out.println("佇列傳送成功!"); } //定義消費者 /** * 1、先獲取channel物件 * 2、定義訊息佇列 * 3、定義消費者物件 * 4、將消費者與佇列資訊繫結 * 5、通過迴圈的方式獲取佇列中的內容 * 6、將獲取的資料轉化為字串 * @throws IOException * @throws InterruptedException * @throws ConsumerCancelledException * @throws ShutdownSignalException */ @Test public void consumer() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ Channel channel = connection.createChannel(); //定義佇列 channel.queueDeclare("queue_simple", false, false, false, null); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //將佇列與消費者繫結 /** * queue:佇列的名稱 * autoAck:是否自動回覆,佇列確認後方能執行下次訊息 * callback:回撥引數,寫的是消費者物件 * */ channel.basicConsume("queue_simple", true,consumer); //通過迴圈方式獲取訊息 while(true){ //獲取訊息佇列的內容---delivery QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = new String(delivery.getBody()); System.out.println("訊息者佇列訊息 :"+msg); } } }
2、工作模式
工作原理:當生產者生產訊息 後,儲存到佇列中。
輪詢模式
測試程式碼:
package com.jt.rabbitmq; import java.io.IOException; import org.junit.Before; import org.junit.Test; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.ConsumerCancelledException; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.ShutdownSignalException; public class TestRabbitMQ_2_work { //工作模式:多個人一起消費一個佇列訊息.內部輪詢機制 /** * 1.定義rabbmq地址 ip:埠 * 2.定義虛擬主機 * 3.定義使用者名稱和密碼 * @throws IOException */ private Connection connection = null; @Before public void init() throws IOException{ ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.220.134"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/jt"); connectionFactory.setUsername("jtadmin"); connectionFactory.setPassword("jtadmin"); //獲取連結 connection = connectionFactory.newConnection(); } @Test public void provider() throws IOException{ //定義通道物件 Channel channel = connection.createChannel(); //定義佇列 channel.queueDeclare("queue_work", false, false, false, null); //定義廣播的訊息 String msg = "我是工作模式"; //傳送訊息 channel.basicPublish("", "queue_work", null, msg.getBytes()); //關閉流檔案 channel.close(); connection.close(); } //定義消費者 @Test public void consumer1() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ //定義通道 Channel channel = connection.createChannel(); //定義佇列 channel.queueDeclare("queue_work", false, false, false, null); //定義消費數 每次只能消費一條記錄.當訊息執行後需要返回ack確認訊息 才能執行下一條 channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //將佇列和消費者繫結 false表示手動返回ack channel.basicConsume("queue_work", false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = new String(delivery.getBody()); System.out.println("佇列A獲取訊息:"+msg); //deliveryTag 佇列下標位置 //multiple是否批量返回 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), true); } } //定義消費者 @Test public void consumer2() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ //定義通道 Channel channel = connection.createChannel(); //定義佇列 channel.queueDeclare("queue_work", false, false, false, null); //定義消費數 每次只能消費一條記錄.當訊息執行後需要返回ack確認訊息 才能執行下一條 channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //將佇列和消費者繫結 false表示手動返回ack channel.basicConsume("queue_work", false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = new String(delivery.getBody()); System.out.println("佇列B獲取訊息:"+msg); //deliveryTag 佇列下標位置 //multiple是否批量返回 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), true); } } //定義消費者 @Test public void consumer3() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ //定義通道 Channel channel = connection.createChannel(); //定義佇列 channel.queueDeclare("queue_work", false, false, false, null); //定義消費數 每次只能消費一條記錄.當訊息執行後需要返回ack確認訊息 才能執行下一條 channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //將佇列和消費者繫結 false表示手動返回ack channel.basicConsume("queue_work", false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = new String(delivery.getBody()); System.out.println("佇列C獲取訊息:"+msg); //deliveryTag 佇列下標位置 //multiple是否批量返回 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), true); } } }
3、訂閱模式
只要佇列繫結了交換機,當p生產者生產訊息 時,這時連線交換機的全部佇列都會收到這個訊息 。並且所有的消費者都會執行訊息 ,類似於廣播-郵箱(群發)
java測試程式碼
說明:釋出訂閱模式測試時,需要先啟動consumer,再啟動provider
package com.jt.rabbitmq; import java.io.IOException; import org.junit.Before; import org.junit.Test; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.ConsumerCancelledException; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.ShutdownSignalException; public class TestRabbitMQ_3_publish { //釋出訂閱模式 private Connection connection = null; //定義rabbit連線池 @Before public void initConnection() throws IOException{ //定義工廠物件 ConnectionFactory connectionFactory = new ConnectionFactory(); //設定引數 connectionFactory.setHost("192.168.220.134"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/jt"); connectionFactory.setUsername("jtadmin"); connectionFactory.setPassword("jtadmin"); //建立連線 connection = connectionFactory.newConnection(); } //定義生產者 @Test public void proverder() throws IOException{ //定義通道 Channel channel = connection.createChannel(); //定義交換機名稱 String exchange_name = "E1"; //定義釋出訂閱模式 fanout redirect 路由模式 topic 主題模式 channel.exchangeDeclare(exchange_name, "fanout"); for(int i=0;i<10; i++){ String msg = "釋出訂閱模式"+i; channel.basicPublish(exchange_name, "", null, msg.getBytes()); } channel.close(); connection.close(); } /** * 消費者需要定義佇列名稱 並且與交換機繫結 * @throws IOException * @throws InterruptedException * @throws ConsumerCancelledException * @throws ShutdownSignalException */ @Test public void consumer1() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ Channel channel = connection.createChannel(); String exchange_name = "E1"; String queue_name = "c_1"; //定義交換機模式 channel.exchangeDeclare(exchange_name, "fanout"); //定義佇列 channel.queueDeclare(queue_name, false, false, false, null); //將佇列和交換機繫結 channel.queueBind(queue_name, exchange_name, ""); //定義消費數量 channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //將消費者和佇列繫結,並且需要手動返回 channel.basicConsume(queue_name, false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = "釋出訂閱模式-消費者1"+new String(delivery.getBody()); System.out.println(msg); //false表示一個一個返回 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } } /** * 消費者需要定義佇列名稱 並且與交換機繫結 * @throws IOException * @throws InterruptedException * @throws ConsumerCancelledException * @throws ShutdownSignalException */ @Test public void consumer2() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ Channel channel = connection.createChannel(); String exchange_name = "E1"; String queue_name = "c_2"; //定義交換機模式 channel.exchangeDeclare(exchange_name, "fanout"); //定義佇列 channel.queueDeclare(queue_name, false, false, false, null); //將佇列和交換機繫結 channel.queueBind(queue_name, exchange_name, ""); channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //定義回覆方式 channel.basicConsume(queue_name, false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = "釋出訂閱模式-消費者2"+new String(delivery.getBody()); System.out.println(msg); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } } }
4、路由模式
說明:每一個佇列都有自己的路由key.當生產者傳送訊息時,會攜帶一個路由key,會將訊息發往路由Key一致的佇列中.
路由模式是釋出訂閱模式的升級版
java測試程式碼
package com.jt.rabbitmq; import java.io.IOException; import org.junit.Before; import org.junit.Test; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.ConsumerCancelledException; import com.rabbitmq.client.QueueingConsumer; import com.rabbitmq.client.ShutdownSignalException; public class TestRabbitMQ_4_redirect { //釋出訂閱模式 private Connection connection = null; //定義rabbit連線池 @Before public void initConnection() throws IOException{ //定義工廠物件 ConnectionFactory connectionFactory = new ConnectionFactory(); //設定引數 connectionFactory.setHost("192.168.126.146"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/jt"); connectionFactory.setUsername("jtadmin"); connectionFactory.setPassword("jtadmin"); //建立連線 connection = connectionFactory.newConnection(); } //定義生產者 @Test public void proverder() throws IOException{ Channel channel = connection.createChannel(); //定義交換機名稱 String exchange_name = "redirect"; //定義釋出訂閱模式 fanout direct 路由模式 topic 主題模式 channel.exchangeDeclare(exchange_name, "direct"); for(int i=0;i<10; i++){ String msg = "生產者傳送訊息"+i; String rontKey = "1707B"; /** * exchange:交換機名稱 * routingKey:路由key * props:引數 * body:傳送訊息 */ channel.basicPublish(exchange_name, rontKey, null, msg.getBytes()); } channel.close(); connection.close(); } /** * 消費者需要定義佇列名稱 並且與交換機繫結 * @throws IOException * @throws InterruptedException * @throws ConsumerCancelledException * @throws ShutdownSignalException */ @Test public void consumer1() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ //定義通道 Channel channel = connection.createChannel(); //定義交換機名稱 String exchange_name = "redirect"; //定義佇列名稱 String queue_name = "c_r_1"; //定義交換機模式 channel.exchangeDeclare(exchange_name, "direct"); //定義佇列 channel.queueDeclare(queue_name, false, false, false, null); //將佇列和交換機繫結 /** * 引數介紹: * queue:佇列名稱 * exchange:交換機名稱 * routingKey:路由key */ //channel.queueBind(queue, exchange, routingKey) channel.queueBind(queue_name, exchange_name, "1707A"); //定義消費個數 channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //繫結訊息與消費者 channel.basicConsume(queue_name, false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = "路由模式-消費者1"+new String(delivery.getBody()); System.out.println(msg); //手動回覆 一個一個回覆 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } } /** * 消費者需要定義佇列名稱 並且與交換機繫結 * @throws IOException * @throws InterruptedException * @throws ConsumerCancelledException * @throws ShutdownSignalException */ @Test public void consumer2() throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException{ Channel channel = connection.createChannel(); String exchange_name = "redirect"; String queue_name = "c_r_2"; //定義交換機模式 channel.exchangeDeclare(exchange_name, "direct"); //定義佇列 channel.queueDeclare(queue_name, false, false, false, null); //將佇列和交換機繫結 channel.queueBind(queue_name, exchange_name, "1707B"); channel.basicQos(1); //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //定義回覆方式 channel.basicConsume(queue_name, false, consumer); while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); String msg = "路由模式-消費者2"+new String(delivery.getBody()); System.out.println(msg); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } } }
5、主題模式
其實就是在路由模式上新增了萬用字元的概念,表示有一類滿足路由key的佇列可以接受訊息
#號:可以匹配一個或多個字元
Key:item.update.abc 消費者路由key item.#
*號:只能匹配單個字元或單詞
Key:item.update 消費者的路由key item.* 可以匹配
Key:item.update.abc 消費者的路由key item.* 不能匹配
java測試程式碼
package com.jt.rabbitmq; import java.io.IOException; import org.junit.Before; import org.junit.Test; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import com.rabbitmq.client.QueueingConsumer; //主題模式 public class TestRabbitMQ_5_topic { private Connection connection = null; @Before public void initConnection() throws IOException{ //1.定義ConnectionFactory物件 ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost("192.168.220.134"); connectionFactory.setPort(5672); connectionFactory.setVirtualHost("/jt"); connectionFactory.setUsername("jtadmin"); connectionFactory.setPassword("jtadmin"); //獲取連線 connection = connectionFactory.newConnection(); } //定義生產者 @Test public void proverder() throws Exception{ //獲取通道 Channel channel = connection.createChannel(); //定義交換機的名稱 String exchange_name = "TOP"; //建立交換機佇列 //exchange 交換機名稱 //type 定義型別 fanout 釋出訂閱模式 direct-路由模式 topic-主題模式 channel.exchangeDeclare(exchange_name, "topic"); //主題模式 for (int i = 0; i < 100; i++) { String msg = "主題模式"+i; /** * 引數說明: * exchange:交換機名稱 * routingKey:路由key * props:引數 * body:資料位元組碼 */ //channel.basicPublish(exchange, routingKey, props, body); channel.basicPublish(exchange_name,"item.update", null, msg.getBytes()); } channel.close(); connection.close(); } @Test public void consumer1() throws Exception{ //定義通道 Channel channel = connection.createChannel(); //定義交換機名稱 String exchange_name = "TOP"; //定義佇列名稱 String queue_name = "TOP1"; //宣告交換機名稱以及主題模式 channel.exchangeDeclare(exchange_name, "topic"); //定義佇列 channel.queueDeclare(queue_name, false, false, false, null); //將交換機和佇列進行繫結 //引數1.佇列名稱 引數2交換機名稱 引數3 路由key #號匹配多個字元 channel.queueBind(queue_name, exchange_name, "item.#"); channel.basicQos(1); //定義消費數量 1 //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); //將佇列和消費者繫結 channel.basicConsume(queue_name, false, consumer); //定義手動回覆 while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); //獲取訊息佇列中的資料 String msg = new String(delivery.getBody()); System.out.println("item.#消費者1:"+msg); //手動回覆 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } } @Test public void consumer2() throws Exception{ Channel channel = connection.createChannel(); String exchange_name = "TOP"; String queue_name = "TOP2"; //生命交換機模式 channel.exchangeDeclare(exchange_name, "topic"); //定義佇列 channel.queueDeclare(queue_name, false, false, false, null); //將交換機和佇列進行繫結 //引數1.佇列名稱 引數2交換機名稱 引數3定義路由key channel.queueBind(queue_name, exchange_name, "item.*"); channel.basicQos(1); //定義消費數量 1 //定義消費者 QueueingConsumer consumer = new QueueingConsumer(channel); channel.basicConsume(queue_name, false, consumer); //定義手動回覆 while(true){ QueueingConsumer.Delivery delivery = consumer.nextDelivery(); //獲取訊息佇列中的資料 String msg = new String(delivery.getBody()); System.out.println("item.*消費者2:"+msg); //定義回執 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); } } }
整合rabbitmq
1、模組劃分
1、order訂單伺服器,作為訊息佇列的生產者
a:訊息佇列中存的是order物件(1、order資訊, 2、訂單物流資訊,3、訂單商品資訊)
b:訊息的生產者需要指定路由key(使用路由模式)
c:標識交換機名稱
2、rabbitmq伺服器,作為訊息佇列的消費者
a:從訊息佇列中獲取訊息 ,資料可以直接進行網路傳輸,要求資料必須序列化
b:定義接收訊息的佇列
c:標識交換機
d:定義路由key
e:將資料進行入庫操作
檔案匯入:
模組位置:order
檔案1:applicationContext-rabbitmq-send.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:rabbit="http://www.springframework.org/schema/rabbit" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> <!-- 非同步的執行緒池,執行緒池的最在數不能設定太小,不然<rabbit:listener/>/@RabbitListener太多的話,會出現發無法正常消費問題 --> <task:executor id="taskExecutor" pool-size="4-256" queue-capacity="128" /> <!-- 定義RabbitMQ的連線工廠 --> <rabbit:connection-factory id="connectionFactory" host="${rabbit.ip}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}" virtual-host="${rabbit.vhost}" publisher-confirms="true" publisher-returns="true" channel-cache-size="5" executor="taskExecutor"/> <!-- 定義Rabbit模板,指定連線工廠以及定義exchange --> <rabbit:template id="amqpTemplate" connection-factory="connectionFactory" exchange="orderExchange" /> <!-- MQ的管理,包括佇列、交換器等 --> <rabbit:admin connection-factory="connectionFactory" /> <!-- 定義交換器,自動宣告交換機 ,durable持久化 --> <rabbit:direct-exchange name="" auto-declare="true" durable="true"> </rabbit:direct-exchange> </beans>
<!-- 訂閱釋出模式 --> <rabbit:fanout-exchange name=""></rabbit:fanout-exchange> <!-- 主題模式 --> <rabbit:topic-exchange name=""></rabbit:topic-exchange>
檔案2:rabbitmq連線配置檔案 rabbitmq.properties
rabbit.ip=192.168.220.134
rabbit.port=5672
rabbit.username=jtadmin
rabbit.password=jtadmin
rabbit.vhost=/jt
檔案3:
<?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:rabbit="http://www.springframework.org/schema/rabbit" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"> <!-- 非同步的執行緒池,執行緒池的最在數不能設定太小,不然<rabbit:listener/>/@RabbitListener太多的話,會出現發無法正常消費問題 --> <task:executor id="taskExecutor" pool-size="4-256" queue-capacity="128" /> <!-- 定義RabbitMQ的連線工廠 --> <rabbit:connection-factory id="connectionFactory" host="${rabbit.ip}" port="${rabbit.port}" username="${rabbit.username}" password="${rabbit.password}" virtual-host="${rabbit.vhost}" publisher-confirms="true" publisher-returns="true" channel-cache-size="5" executor="taskExecutor"/> <!-- MQ的管理,包括佇列、交換器等 --> <rabbit:admin connection-factory="connectionFactory" /> <!-- 定義訊息佇列 --> <rabbit:queue name="orderQueue" auto-declare="true"/> <!-- 定義交換機,並且完成佇列和交換機的繫結 --> <rabbit:direct-exchange name="orderExchange" auto-declare="true"> <rabbit:bindings> <!-- 前臺系統只接收商品更新的訊息,key路由key --> <!-- 將佇列和路由key進行繫結 --> <rabbit:binding queue="orderQueue" key="order.save"/> </rabbit:bindings> </rabbit:direct-exchange> <!-- 定義監聽 --> <rabbit:listener-container connection-factory="connectionFactory"> <!-- 監聽一個佇列,當佇列中有訊息,就會自動觸發類.方法,傳遞訊息就作為方法的引數,根據方法宣告的引數強轉 --> <rabbit:listener ref="ordermqService" method="saveOrder" queue-names="orderQueue"/> </rabbit:listener-container> <bean id="ordermqService" class="com.jt.order.rabbit.service.orderServiceImpl"></bean> </beans>
java程式碼:
通過模板傳送訊息 :
說明:通過訊息佇列,資料處理和傳送是非同步的,訊息已經發現,但是訊息還沒有處理。這時資料庫中還沒有資料,所以一般公司要求使用者半小時後查詢。
程式碼 :
//引入訊息佇列的模板工具類 @Autowired private RabbitTemplate rabbitTemplate; public String saveOrder(Order order){ String orderId = order.getUserId() + System.currentTimeMillis() + ""; //賦值orderId order.setOrderId(orderId); //通過訊息佇列處理訊息 /** * routingKey:生產者的路由key * object:需要傳送的物件 */ rabbitTemplate.convertAndSend("order.save", order); System.out.println("訊息佇列呼叫完成"); System.out.println("訂單入庫成功~~~"); return orderId; }
接收處理佇列訊息 ,此類直接交給rabbitmq處理
package com.jt.order.rabbit.service; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import com.jt.dubbo.pojo.Order; import com.jt.dubbo.pojo.OrderItem; import com.jt.dubbo.pojo.OrderShipping; import com.jt.order.mapper.OrderItemMapper; import com.jt.order.mapper.OrderMapper; import com.jt.order.mapper.OrderShippingMapper; public class orderServiceImpl { //將訊息佇列中的內容寫入資料庫中 @Autowired private OrderMapper orderMapper; @Autowired private OrderItemMapper orderItemMapper; @Autowired private OrderShippingMapper orderShippingMapper; public void saveOrder(Order order){ Date date = new Date(); order.setCreated(date); order.setStatus(1); order.setUpdated(date); System.out.println("order=" + order); orderMapper.insert(order); //完善物流資訊及入庫 OrderShipping shipping = order.getOrderShipping(); shipping.setCreated(date); shipping.setUpdated(date); shipping.setOrderId(order.getOrderId()); orderShippingMapper.insert(shipping); //完善商品資訊及入庫 //1、實現批量入庫操作,自己手寫sql //2、通過迴圈遍歷方式,實現多次入庫操作 List<OrderItem> orderItems = order.getOrderItems(); for(OrderItem orderItem : orderItems){ orderItem.setOrderId(order.getOrderId()); orderItemMapper.insert(orderItem); } System.out.println("訊息佇列入庫成功~~~"); } }