RabbitMQ訊息佇列

Yang`發表於2021-07-29

目錄:

  1、介紹

  2、linux安裝

  3、工作模式

  4、整合spring

介紹:

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);
        }
    }
}
View Code

 

 

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);
                    }
                }
    
    
    
    
}
View Code

 

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);
        }

    }
    
    
    
}
View Code

 

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);
        }
    }
}
View Code

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);    
        }
    }
}
View Code

 


 

整合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>
applicationContext-rabbitmq-send.xml
    <!-- 訂閱釋出模式 -->
    <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>
applicationContext-rabbitmq-receive.xml

 

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("訊息佇列入庫成功~~~");
    }
}
orderServiceImpl

 

相關文章