《RabbitMQ 消息中間件》RabbitMQ詳解

前言

安裝完RabbitMQ之後,我們就來了解一下RabbitMQ的管理命令。

詳情

第一條命令:幫助命令

rabbitmqctl help

第二條命令:插件管理

rabbitmq-plugins

list 插件列表,enable啓用插件,disable禁用插件,set重置插件。

管理界面

RabbitMQ使用的是AMQP的協議。

RabbitMQ的7中消息模型:

 

第一種消息模型-直連(SpringBoot演練)

加入依賴Jar包(Gradle)

compile('org.springframework.boot:spring-boot-starter-amqp')

封裝類:

import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class MqUtil {
    public static Connection getConnection() throws IOException, TimeoutException {
        // 創建rabbitmq的連接工廠
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 設置主機IP
        connectionFactory.setHost("47.105.72.224");
        // 設置端口
        connectionFactory.setPort(5672);
        // 設置主機名
        connectionFactory.setVirtualHost("/my");
        // 設置訪問虛擬主機的IP和端口
        connectionFactory.setUsername("rabbitmq");
        connectionFactory.setPassword("rabbitmq");
        return connectionFactory.newConnection();
    }
}

發送消息代碼:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class Provoder {

    public void sendMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();
        /**
         * 參數1:隊列名稱,不存在自動創建
         * 參數2:是否持久化隊列
         * 參數3:exclusive 是否獨佔隊列(當前隊列只能被當前連接使用)
         * 參數4:autoDelete 是否需要消費完之後,刪除參數。
         * 參數5:額外信息
         */
        channel.queueDeclare("hello",true,false,false,null);

        /**
         * 發佈消息
         * 參數1:交互機
         * 參數2:隊列
         * 參數3:消息額外設置(MessageProperties.MINIMAL_PERSISTENT_BASIC)
         * 參數4:具體內容
         */
        channel.basicPublish("","hello",MessageProperties.MINIMAL_PERSISTENT_BASIC,"Hello World!".getBytes());
        channel.close();
    }
}

測試Controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@RestController
public class TestController3 {

    @Autowired
    SendMqProvoder sendMqProvoder;

    @RequestMapping("/rabbitmq.do")
    public String event() throws IOException, TimeoutException {
        sendMqProvoder.sendMessage();
        return "success";
    }
}

效果展示:

網頁訪問後的RabbitMq消息情況,rabbitmq的管理界面中就出現了Hello隊列,並且有兩條未消費的消息。

消費者:

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class Customer {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();
        /**
         * 參數1:隊列名稱,不存在自動創建
         * 參數2:是否持久化隊列
         * 參數3:exclusive 是否獨佔隊列(當前隊列只能被當前連接使用)
         * 參數4:autoDelete 是否需要消費完之後,刪除參數。
         * 參數5:額外信息
         */
        channel.queueDeclare("hello",true,false,false,null);

        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume("hello",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties,byte[] body) throws IOException{
                System.out.println(new String(body));
            }
        });
        
        //消費者需要一直監聽消息。
        //channel.close();
        //connection.close();
    }
}

測試類:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@RestController
public class TestController3 {

    @Autowired
    Provoder provoder;

    @Autowired
    Customer customer;

    @RequestMapping("/rabbitmq.do")
    public String event() throws IOException, TimeoutException {
        provoder.sendMessage();
        return "success";
    }

    @RequestMapping("/receive.do")
    public String receive() throws IOException, TimeoutException {
        customer.receiveMessage();
        return "success";
    }
}

接收消息的結果:

第二種消息模型-work queue(SpringBoot演練)

1. 支持多個消費者,一個消息只能被消費一次。

2. 消息被多個消費者平均分配消費。

生產者:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class WorkProvoder {
    public void sendMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();
        /**
         * 參數1:隊列名稱,不存在自動創建
         * 參數2:是否持久化隊列
         * 參數3:exclusive 是否獨佔隊列(當前隊列只能被當前連接使用)
         * 參數4:autoDelete 是否需要消費完之後,刪除參數。
         * 參數5:額外信息
         */
        channel.queueDeclare("work",true,false,false,null);

        /**
         * 發佈消息
         * 參數1:交互機
         * 參數2:隊列
         * 參數3:消息額外設置(MessageProperties.MINIMAL_PERSISTENT_BASIC)
         * 參數4:具體內容
         */
        for (int i = 0; i < 100; i++) {
            channel.basicPublish("","work",MessageProperties.MINIMAL_PERSISTENT_BASIC,(i+"Hello Work Queue!").getBytes());
        }
        channel.close();
        connection.close();
    }
}

消費者1

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class WorkCustomer {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();
        /**
         * 參數1:隊列名稱,不存在自動創建
         * 參數2:是否持久化隊列
         * 參數3:exclusive 是否獨佔隊列(當前隊列只能被當前連接使用)
         * 參數4:autoDelete 是否需要消費完之後,刪除參數。
         * 參數5:額外信息
         */
        channel.queueDeclare("work",true,false,false,null);

        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume("work",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-1:"+new String(body));
            }
        });
    }
}

消費者2

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class WorkCustomer2 {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();
        /**
         * 參數1:隊列名稱,不存在自動創建
         * 參數2:是否持久化隊列
         * 參數3:exclusive 是否獨佔隊列(當前隊列只能被當前連接使用)
         * 參數4:autoDelete 是否需要消費完之後,刪除參數。
         * 參數5:額外信息
         */
        channel.queueDeclare("work",true,false,false,null);

        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume("work",true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-2:"+new String(body));
            }
        });
    }
}

測試類:

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@RestController
public class TestController4 {

    @Autowired
    WorkProvoder workProvoder;

    @Autowired
    WorkCustomer customer1;

    @Autowired
    WorkCustomer2 customer2;

    @RequestMapping("/workSend.do")
    public String workSend() throws IOException, TimeoutException {
        workProvoder.sendMessage();
        return "success";
    }

    @RequestMapping("/workReceive1.do")
    public String workReceive1() throws IOException, TimeoutException {
        customer1.receiveMessage();
        return "success";
    }

    @RequestMapping("/workReceive2.do")
    public String workReceive2() throws IOException, TimeoutException {
        customer2.receiveMessage();
        return "success";
    }
}

運行結果:

現在的模式會出現消息丟失,平均分配的問題。

修改一下代碼,加入手動確認和每次消費一條消息,來保證消息的完整。

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class WorkCustomer2 {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();
        /**
         * 參數1:隊列名稱,不存在自動創建
         * 參數2:是否持久化隊列
         * 參數3:exclusive 是否獨佔隊列(當前隊列只能被當前連接使用)
         * 參數4:autoDelete 是否需要消費完之後,刪除參數。
         * 參數5:額外信息
         */
        channel.queueDeclare("work",true,false,false,null);

        // 一次處理一個
        channel.basicQos(1);

        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume("work",false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-2:"+new String(body));
                // 傳一個標記確認消息。
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
    }
}

第三種消息模型-廣播(fanout)

生產者:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class FanProvider {

    public void sendMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 設置交換機
         * 參數1:交換機的名字(自取)。
         * 參數2:交換機類型:fanout:就是廣播。
         */
        channel.exchangeDeclare("exchange1","fanout");

        /**
         * 發佈消息
         * 參數1:交互機
         * 參數2:隊列
         * 參數3:消息額外設置(MessageProperties.MINIMAL_PERSISTENT_BASIC:消息持久化)
         * 參數4:具體內容
         */
        for (int i = 0; i < 100; i++) {
            channel.basicPublish("exchange1","",MessageProperties.MINIMAL_PERSISTENT_BASIC,(i+"Hello Fan Queue!").getBytes());
        }
        channel.close();
        connection.close();
    }
}

消費者:

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class FanCustomer {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 綁定交換機
         * 參數1:交換機名
         * 參數2:交換機類型
         */
        channel.exchangeDeclare("exchange1","fanout");

        /********* 創建一個臨時隊列 *********/
        String queue = channel.queueDeclare().getQueue();

        /********* 綁定交換機和隊列 *********/
        channel.queueBind(queue,"exchange1","");


        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-1:"+new String(body));
            }
        });
    }
}

運行結果:

兩個消費者都能消費到消息。

第四種消息模型-訂閱(Routing):實現不同的人去訂閱不同的消息。

生產者:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class DirectProvider {

    public void sendMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 設置交換機
         * 參數1:交換機的名字(自取)。
         * 參數2:交換機類型:direct:路由模式。
         */
        channel.exchangeDeclare("logs","direct");

        /******* 設置路由key ******/
        String routingKey = "info";
        String routingKey1 = "warn";

        /**
         * 發佈消息
         * 參數1:交互機
         * 參數2:隊列
         * 參數3:消息額外設置(MessageProperties.MINIMAL_PERSISTENT_BASIC:消息持久化)
         * 參數4:具體內容
         */
        channel.basicPublish("logs",routingKey,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey+":Hello Fan Queue!").getBytes());
        channel.basicPublish("logs",routingKey1,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey1+":Hello Fan Queue!").getBytes());
        channel.basicPublish("logs",routingKey1,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey1+":Hello Fan Queue!").getBytes());

        channel.close();
        connection.close();
    }
}

消費者:

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class DirectCustomer {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 綁定交換機
         * 參數1:交換機名
         * 參數2:交換機類型
         */
        channel.exchangeDeclare("logs","direct");

        /********* 創建一個臨時隊列 *********/
        String queue = channel.queueDeclare().getQueue();

        /********* 綁定交換機和隊列和路由key *********/
        channel.queueBind(queue,"logs","info");


        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-1:"+new String(body));
            }
        });
    }
}

運行結果:

第五種消息模型-動態訂閱(Topic):實現不同的人去訂閱不同的消息,支持設置通配符。

* 匹配任意一個單詞。  *.rabbitmq

# 匹配多個任意單詞。 user.#

生產者:

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.MessageProperties;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class TopicProvider {

    public void sendMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 設置交換機
         * 參數1:交換機的名字(自取)。
         * 參數2:交換機類型:topic:動態路由模式。
         */
        channel.exchangeDeclare("topic_logs","topic");

        /******* 設置路由key ******/
        String routingKey = "user.info";
        String routingKey1 = "user.warn";

        /**
         * 發佈消息
         * 參數1:交互機
         * 參數2:隊列
         * 參數3:消息額外設置(MessageProperties.MINIMAL_PERSISTENT_BASIC:消息持久化)
         * 參數4:具體內容
         */
        channel.basicPublish("topic_logs",routingKey,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey+":Hello Topic Queue!").getBytes());
        channel.basicPublish("topic_logs",routingKey1,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey1+":Hello Topic Queue!").getBytes());

        channel.close();
        connection.close();
    }
}

消費者1:

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class TopicCustomer {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 綁定交換機
         * 參數1:交換機名
         * 參數2:交換機類型
         */
        channel.exchangeDeclare("topic_logs","topic");

        /********* 創建一個臨時隊列 *********/
        String queue = channel.queueDeclare().getQueue();

        /********* 綁定交換機和隊列和路由key *********/
        channel.queueBind(queue,"topic_logs","*.info");


        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-1:"+new String(body));
            }
        });
    }
}

消費者2:

import com.rabbitmq.client.*;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

@Component
public class TopicCustomer1 {

    public void receiveMessage() throws IOException, TimeoutException {
        Connection connection = MqUtil.getConnection();
        // 創建MQ渠道
        Channel channel = connection.createChannel();

        /**
         * 綁定交換機
         * 參數1:交換機名
         * 參數2:交換機類型
         */
        channel.exchangeDeclare("topic_logs","topic");

        /********* 創建一個臨時隊列 *********/
        String queue = channel.queueDeclare().getQueue();

        /********* 綁定交換機和隊列和路由key *********/
        channel.queueBind(queue,"topic_logs","user.*");

        /**
         * queue 隊列名稱
         * autoAck 開啓消息自動確認機制
         * callback 消費消息的回調函數
         */
        channel.basicConsume(queue,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{
                System.out.println("消費者-2:"+new String(body));
            }
        });
    }
}

運行結果:

第六種通道協議(RPC)略。

第7種消息模型-發佈確認模型 略。

總結

1. rabbitmq 目前支持6中消息模型,一種通道協議。

2. rabbitmq 消息支持隊列和消息持久化

3. 結構:生產者,消費者,隊列,交換機,虛擬主機。

4. 消息確認機制,默認平均分配給每一個消費者。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章