RabbitMQ(1)---基本概念及簡單demo

一、安裝RabbitMQ

安裝直接用docker安裝,如果手動安裝的話比較繁瑣,還要安裝erlang語言的環境。在安裝有docker機器上執行官網提供的指令(https://www.rabbitmq.com/download.html):docker run -it --rm --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

 

 安裝好之後 瀏覽器訪問15672端口,可以看到web管理端。默認的是用戶名密碼都是guest

 二、AMQP協議(Advanced message queue protocol 高級消息隊列協議)核心概念和RabbitMQ整體架構

RabbitMQ是通過Erlang語言基於AMQP協議開發的。erlang語言常用於交換機上,因爲它的高效,自然rabbitMQ也很高效了。

AMQP模型描述了一套模塊化的組件以及這些組件之間進行連接的標準規則,核心概念:
server:服務,又稱broker,接受客戶端連接,對外提供amqp實體服務

connection:連接,客戶端與server的連接

channel:網絡通道,幾乎所有的操作都是在channel中進行的,是進行消息對象的通道,客戶端可以建立多個通道,每一個channel表示一個會話任務

virtual host:虛擬主機,是一個邏輯上的概念,一個虛擬主機中可以包含多個exhange 和 message queue但是一個虛擬主機中不能有名稱相同的exchange 和 message queue
 
exchange:接收發布應用程序發送的消息,並根據一定的規則將這些消息路由到消息隊列
 
message queue:消息隊列,存儲消息,直到這些消息被消費者安全處理完爲止
 
binding:定義了exchange和message queue之間的關聯,提供路由規則,包含route_key
 
route_key:路由key ,作用是在交換機上通過route_key來把消息路由到某個隊列上
 
RabbitMQ整體架構:
 

 

這些概念在rabbitMQ的web控制檯都可以看見:

 

 

 

 

三、交換機詳解
1.交換機屬性:
name:交換機名字
type:類型:direct,topic,fanout,headers
durability:是否需要持久化
autodelete: 假如沒有隊列綁定到該交換機,那麼該交換機會自動刪除
Internal: 當前交換機是否用戶rabbitmq內部使用不常用,默認爲false
argurements: 擴展參數,用戶擴展AMQP定製化協議
 
2.直連交換機:direct exchange  消息投遞時的key與隊列綁定routekey完全相同的隊列上面
 
3.主題交換機:topic exchange 投遞消息是可以進行key的匹配,#匹配多個單詞,*匹配一個單詞(用.隔開),比如隊列A綁定的routekey:study.#。投遞消息時設置的routekey.abc或者routekey.abc.def都可以投遞到隊列A上面;隊列B設置的routekey:study.*,那麼就只有routekey.abc可以投遞到隊列B
 
4.扇形交換機: fanout exchange 不會進行routekey的匹配,只要是該交換機的隊列都會被投遞。因爲不會進行路由字符串的匹配,所以這種交換的消息投遞是最快的
 
5.頭部交換機:headers exchange  投遞的消息頭裏面有一個特殊的key:“x-match”,有兩個值:all(默認),發送消息的header裏面的所有key都對應,才投遞;any,任意一個對應就投遞
 
四、不同交換機的demo
依賴:使用的spring-boot-starter-amqp,這個裏面吧相應的原始的依賴的導入了的。還是使用最原始的方式來進行生產消費的,這樣能更好的理解,和上面的核心內容中的概念,以及交換機綁定都可以聯繫起來。
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.1.7.RELEASE</version>
        </dependency>
設置連接:
package com.nijunyang.rabbitmq.util;

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

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

/**
 * Description:
 * Created by nijunyang on 2020/5/27 10:24
 */
public abstract class RabbitMQUtils {

    private static ConnectionFactory connectionFactory = new ConnectionFactory();
    //設置連接工廠信息
    static {
        connectionFactory.setHost("*.*.*.*");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
    }

    public static Connection getConnection() throws IOException, TimeoutException {

        Connection connection = connectionFactory.newConnection();
        return connection;
    }

    public static void close(AutoCloseable...closeables) throws Exception {
        for (AutoCloseable closeable : closeables) {
            closeable.close();
        }
    }

}

消費者獲取連接,創建channel,聲明交換機和隊列,綁定交換機和隊列,準備接收消息

package com.nijunyang.rabbitmq.consumer;

import ch.qos.logback.core.net.SyslogOutputStream;
import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

import java.io.IOException;

/**
 * Description:
 * Created by nijunyang on 2020/5/27 10:42
 */
public class RabbitMQConsumer {

    public static void main(String[] args) throws Exception {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        /*------------------*/
        //1:默認消息投遞 消費
//        String queueName = "hello.queue";
//        //聲明一個隊列(如果沒有這個隊列,第一次是在消費者這邊在聲明的,所以要先啓動消費者,生產者消息纔有地方投遞)
//        channel.queueDeclare(queueName,true,false,true,null);
        /*------------------*/
        //2:直連交換機
//        String exchangeName = "njy.directchange";
////        String exchangeType = "direct";
////
////        String routingKey = "directchange.key";//發送和消費的要一模一樣
////        String queueName = "direct.queue";
////        //聲明一個交換機和隊列
////        channel.exchangeDeclare(exchangeName, exchangeType,true,false,null);
////        channel.queueDeclare(queueName,true,false,false,null);
////        //隊列和交換機通過routingKey綁定
////        channel.queueBind(queueName, exchangeName, routingKey);
////        /*------------------*/
        //3:topic交換機
        String exchangeName = "njy.topicchange";
        String routingKey = "directchange.#"; //.分割,#匹配多個,*匹配一個
        String exchangeType = "topic";
        String queueName = "topic.queue";
        //聲明一個交換機和隊列
        channel.exchangeDeclare(exchangeName, exchangeType,true,false,null);
        channel.queueDeclare(queueName,true,false,false,null);
        //隊列和交換機通過routingKey綁定
        channel.queueBind(queueName, exchangeName, routingKey);
        /*------------------*/
        //創建消費者
        Consumer consumer = new DefaultConsumer(channel) {
            //回調
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println(message);
            }
        };
        channel.basicConsume(queueName,true, consumer);
    }
}

生產者消費者獲取連接,創建channel,發送消息

package com.nijunyang.rabbitmq.producer;

import com.nijunyang.rabbitmq.util.RabbitMQUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;

import java.util.Random;

/**
 * Description:
 * Created by nijunyang on 2020/5/27 10:18
 */
public class RabbitMQProducer {
    public static void main(String[] args) throws Exception {

        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        String message = "hello rabbitMQ." + new Random().nextInt(100);

        /*------------------*/
        //1:默認消息投遞
//        //參數String exchange, String routingKey, AMQP.BasicProperties props, byte[] body
//        //不設置交換機會發送到默認上面
//        channel.basicPublish("", "hello.queue", null, message.getBytes("utf-8"));
        /*------------------*/
        //2:直連交換機
//        String exchangeName = "njy.directchange";
//        String routingKey = "directchange.key"; //發送和消費的要一模一樣
        /*------------------*/
        //3.topic交換機
        String exchangeName = "njy.topicchange";
        String routingKey = "directchange.key"; //.分割,#匹配多個,*匹配一個

        channel.basicPublish(exchangeName, routingKey, null, message.getBytes("utf-8"));
        /*------------------*/
        RabbitMQUtils.close(channel, connection);
    }
}

如果隊列不存在,需要先啓動消費者,纔會先將生成隊列,不然生產者發送的消息不知道路由到哪個隊列,因爲隊列的聲明,以及和交換機的綁定是在消費者這邊做的。

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