RabbitMQの基础篇

108 阅读13分钟

概述

RabbitMQ是一个开源的消息代理和队列服务器, 用来通过普通协议在完全不同的应用之间共享数据, RabbitMQ使用Erlang语言来编写的, 并且RabbitMQ是基于AMQP协议的

优点

  • 开源, 性能有效, 稳定性好
  • 提供可靠性消息投递模式(confirm), 返回模式(return)等
  • 与Spring完美整合, API丰富
  • 集群模式丰富, 支持表达式配置, 高可用HA模式, 镜像队列模型
  • 可以保证数据不丢失的前提下做到高可靠性, 可用性

RabbitMQ高性能的原因

  • Erlang语言最初用于交换机领域的架构模式, 这样使得RabbitMQ在Broker之间进行数据交互的性能是非常优秀的
  • Erlang的优点 : Erlang有着和原生Socket一样的延迟

AMQP协议

AMQP简介

  • AMQP全称 : Advanced Message Queuing Protocol
  • 中文 : 高级消息队列协议
  • AMQP定义 : 是具有现代特征的二进制协议, 是一个提供统一消息服务的应用层标准高级消息队列协议, 是应用层协议的一个开放标准, 为面向消息的中间件设计

AMQP协议模型

AMQP协议模型.png

AMQP核心概念

  • Server : 又称Broker, 接受客户端连接, 实现AMQP实体服务
  • Connection : 连接, 应用程序与Broker的网络连接
  • Channel : 网络信道, 几乎所有的操作都在Channel中进行, Channel是进行消息读写的通道。客户端可以建立多个Channel, 每个Channel代表一个会话任务。
  • Message : 消息, 服务器和应用程序之间传送的数据, 有Properties和Body组成。Properties可以对消息进行修饰, 比如消息的优先级, 延迟等高级特性; Body就是消息体内容。
  • Virtual Host : 虚拟地址, 用于进行逻辑隔离, 最上层的消息路由。一个Virtual Host里面可以有若干个Exchange和Queue, 同一个Virtual Host里面不能有相同名称的Exchange或Queue
  • Exchange : 交换机, 用于接收消息, 根据路由键转发消息到绑定的队列
  • Binding : Exchange和Queue之间的虚拟连接, binding中可以包含routing key
  • Routing Key : 一个路由规则, 虚拟机可用它来确定如何路由一个特定消息
  • Queue : 也称Message Queue, 消息队列, 用于保存消息并将它们转发给消费者

RabbitMQ整体架构

架构图

image.png

  • 生产者只需要将消息发送到Exchange即可
  • 消费者只需要监听对应的消息队列即可
  • Exchange绑定多个Queue时, 要通过Routing Key进行路由

RabbitMQ消息流转

RabbitMQ消息流转.jpg

RabbitMQ安装与使用

  1. 环境安装

    1. erlang下载

    官网下载地址

    wget https://packages.erlang-solutions.com/erlang-solutions-2.0-1.noarch.rpm
    
    1. erlang安装

    解压

    rpm -Uvh erlang-solutions-2.0-1.noarch.rpm
    

    安装

    yum install -y erlang
    

    验证是否安装成功

    erl -v
    
  2. RabbitMq下载、安装

    1. RabbitMq下载
      GitHub下载RabbitMq
    2. RabbitMq 安装 解压
    rpm -Uvh rabbitmq-server-3.8.14-1.el7.noarch.rpm
    

    安装

    yum install rabbitmq-server -y
    

    启动RabbimtMq

    # 启动rabbiMq-server
    systemctl start rabbitmq-server 
    # 查看是否启动成功 rabbiMq-Server
    systemctl status rabbitmq-server
    # 关闭rabbiMq-server
    systemctl stop rabbitmq-server
    # 开机重启
    systemctl enable rabbitmq-server
    

注意:在Centos8中有可能缺少 socat,可以使用命令 yum install -y socat 进行安装

  1. WEB管理界面安装

rabbitmq-plugins enable rabbitmq_management

注意这需要开放端口号:15672,5672

firewall-cmd --permanent --zone=public --add-port=15672/tcp

firewall-cmd --permanent --zone=public --add-port=5672/tcp

重新加载配置使配置生效

firewall-cmd --reload

  1. 添加账户设置密码、授权

虚拟主机配置

  • 创建虚拟主机 : rabbitmqctl add_vhost vhostpath
  • 列出所有虚拟主机 : rabbitmqctl list_vhosts
  • 列出虚拟主机上所有权限 : rabbitmqctl list_permissions -p vhostpath
  • 删除虚拟主机 : rabbitmqctl delete_vhostpath

用户配置

  • 添加用户: rabbitmqctl add_user [账号] [密码]
  • 添加角色: rabbitmqctl set_user_tags [账号]  [角色]
  • 变更密码: rabbitmqctl change_password [用户名称] [新密码]
  • 删除用户: rabbitmqctl delete_user [用户名称]
  • 列举所有用户:rabbitmqctl list_users
  • 赋予、删除权限:rabbitmqctl set_permissions -p [vhostpath] [用户名] ".*" ".*" ".*"

"." "." ".*" 表示所有资源的写权限和读权限。

  • 清除用户权限:rabbitmqctl clear_permissions -p [vhostpath] [用户名]

队列相关 :

  • 查看所有队列信息 : rabbitmqctl list_queues
  • 清除队列里的消息 : rabbitmqctl -p vhostpath purge_queue blue

编码

引入依赖

<dependency>
  <groupId>com.rabbitmq</groupId>
  <artifactId>amqp-client</artifactId>
  <version>5.9.0</version>
</dependency>

主要对象

  • ConnectionFactory : 获取连接工厂
  • Connection : 一个连接
  • Channel : 数据通信信道, 可发送和接收消息
  • Queue : 具体的消息存储队列
  • Producer & Consumer生产者和消费者

快速入门

Producer

public class Producer {

    public static void main(String[] args) {

        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String queueName = "queue_simple";
            // 声明队列

            /**
             * queueName 队列名称
             * durable 是否持久化 消息是否存盘 如果是非之久化队列消息也会存盘 但是消息随着服务器的重启会丢失,持久化了则不会
             * exclusive:是否排外的,有两个作用,
             * 一:当连接关闭时connection.close()该队列是否会自动删除;
             * 二:该队列是否是私有的private,如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,
             * 如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常。
             * autoDelete 是否自动删除 对最后一个消息被消费者消费后 ,队列是否删除
             * argument 队列附带的属性
             */
            channel.queueDeclare(queueName, false, false, false, null);
            //发送消息
            /**
             * exchangeName 交换机 如果没有定义交换机 则使用默认的交换机
             * routingKey 队列名称/routingkey
             * props 消息的其他属性 - 路由标头等
             * body 需要发送的消息内容
             */
            for (int i = 0; i < 10000; i++) {
                 channel.basicPublish("", queueName, null, ("hello rabbitmq" + i).getBytes());
            }
            System.out.println("消息发送成功");
        } catch (Exception e) {
            e.printStackTrace();

        }finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

Consumer

/**
 * @ClassName Consumer
 * @Description TODO
 * @Author Axel
 * @Date 2021/4/17 18:55
 * @Version 1.0
 */
@Slf4j
public class Consumer {
    public static void main(String[] args) {
        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String queueName = "queue_simple";
            channel.basicConsume(queueName, true,new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("收到的消息是:{} ------- {}", consumerTag, new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                    log.info("消息接收失败!{}", consumerTag);
                }
            });
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

Exchange 交换机

Direct Exchange:

所有发送到Direct Exchange的消息被转发到RoutingKey中指定的Queue。相当于把指定的RoutingKey和Queue建立路由关系。 image.png

注意 : Direct模式可以使用RabbitMQ自带的Exchange(default Exchange), 所以不需要将Exchange进行任何绑定(binding)操作, 消息传递时, RoutingKey必须完全匹配才会被队列接收, 否则该消息会被抛弃

Producer

package com.chou.rabbitmq.exchange.direct;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
 * exchange 类型为direct
 *
 * @author by Axel
 * @since 2024/6/9 下午10:43
 */
@Slf4j
public class DirectExchangeProducerTest {
    public static void main(String[] args) {


        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String exchangeName = "customer_direct_Exchange";
            String routing_key_Name = "direct_test_routing_key";
            String queue_Name = "direct_test_queue";
            //channel.queueDeclare(queue_Name, true, true, false, null);
            // mandatory的作用:
            //   当mandatory标志位设置为true时,如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,
            //   那么broker会调用basic.return方法将消息返还给生产者;当mandatory设置为false时,出现上述情况broker会直接将消息丢弃;
            //   通俗的讲,mandatory标志告诉broker代理服务器至少将消息route到一个队列中,否则就将消息return给发送者;
            for (int i = 1; i <= 10; i++) {
                channel.basicPublish(exchangeName, routing_key_Name, true, null, ("这是一个direct类型的Exchange交换机"+i).getBytes());
            }
            log.info("消息发送成功!");
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }

    }
}

Consumer

package com.chou.rabbitmq.exchange.direct;

import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @ClassName Consumer
 * @Description TODO
 * @Author Axel
 * @Date 2021/4/17 18:55
 * @Version 1.0
 */
@Slf4j
public class DirectExchangeConsumerTest {
    public static void main(String[] args) {
        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String exchangeName = "customer_direct_Exchange";
            String routing_key_Name = "direct_test_routing_key";
            String queue_Name = "direct_test_queue";
            channel.queueDeclare(queue_Name, true, true, false, null);
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT.getType(), true, false, null);
            channel.queueBind(queue_Name, exchangeName, routing_key_Name);
            channel.basicConsume(queue_Name, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    try {
                        log.info("处理业务逻辑开始.....");
                        TimeUnit.SECONDS.sleep(1);
                        log.info("处理业务逻辑结束需要1秒.....");
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    log.info("收到的消息是:{} ------- {}", consumerTag, new String(message.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {

                }
            });
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

TopicExchange

  • 所有发送到Topic Exchange的消息将被转发到所有关心RoutingKey中指定Topic的Queue上
  • Exchange将RoutingKey和某个Topic进行模糊匹配, 此时队列需要绑定一个Topic
    • 可以使用通配符进行模糊匹配
    • "#" : 匹配一个或多个词
    • "*" : 匹配一个词

image.png

Producer

package com.chou.rabbitmq.exchange.topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
 * exchange 类型为direct
 *
 * @author by Axel
 * @since 2024/6/9 下午10:43
 */
@Slf4j
public class TopicExchangeProducerTest {
    public static void main(String[] args) {


        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String exchangeName = "customer_topic_Exchange";
            String routing_key_Name1 = "topic.test.chou.cj";
            String routing_key_Name2 = "topic.test.zhou";
            String routing_key_Name3 = "topic.test.chou";
            //mandatory的作用:
            //当mandatory标志位设置为true时,如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,
            //那么broker会调用basic.return方法将消息返还给生产者;当mandatory设置为false时,出现上述情况broker会直接将消息丢弃;
            //通俗的讲,mandatory标志告诉broker代理服务器至少将消息route到一个队列中,否则就将消息return给发送者;

            channel.basicPublish(exchangeName, routing_key_Name1, true,
                    null, ("这是一个Topic类型的Exchange交换机routing_key:routing_key_Name1").getBytes());
            channel.basicPublish(exchangeName, routing_key_Name2, true,
                    null, ("这是一个Topic类型的Exchange交换机routing_key:routing_key_Name2").getBytes());
            channel.basicPublish(exchangeName, routing_key_Name3, true,
                    null, ("这是一个Topic类型的Exchange交换机routing_key:routing_key_Name3").getBytes());
            log.info("消息发送成功!");
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }

    }
}

Consumer

package com.chou.rabbitmq.exchange.topic;

import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @ClassName Consumer
 * @Description TODO
 * @Author Axel
 * @Date 2021/4/17 18:55
 * @Version 1.0
 */
@Slf4j
public class TopicExchangeConsumerTest {
    public static void main(String[] args) {
        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String exchangeName = "customer_topic_Exchange";
            String routing_key_Name = "topic.test.#";
            //String routing_key_Name = "topic.test.#";
            String queue_Name = "topic_test_queue";
            channel.queueDeclare(queue_Name, true, true, false, null);
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC.getType(), true, false, null);
            channel.queueBind(queue_Name, exchangeName, routing_key_Name);
            channel.basicConsume(queue_Name, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("收到的消息是:{} ------- {}", consumerTag, new String(message.getBody(), StandardCharsets.UTF_8));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {

                }
            });
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

Fanout Exchange

  • 不处理路由键, 只需要简单的将队列绑定到交换机上
  • 发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
  • Fanout交换机转发消息是最快的 image.png

Producer

package com.chou.rabbitmq.exchange.fanout;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

/**
 * exchange 类型为direct
 *
 * @author by Axel
 * @since 2024/6/9 下午10:43
 */
@Slf4j
public class FanoutExchangeProducerTest {
    public static void main(String[] args) {


        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String exchangeName = "customer_fanout_Exchange";
            String routing_key_Name = "没有什么用";
            //mandatory的作用:
            //当mandatory标志位设置为true时,如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,
            //那么broker会调用basic.return方法将消息返还给生产者;当mandatory设置为false时,出现上述情况broker会直接将消息丢弃;
            //通俗的讲,mandatory标志告诉broker代理服务器至少将消息route到一个队列中,否则就将消息return给发送者;
            for (int i = 1; i <=5 ; i++) {
                channel.basicPublish(exchangeName, routing_key_Name, true,
                        null, ("这是一个Fanout类型的Exchange交换机routing_key:routing_key_Name" + i).getBytes());
            }
            log.info("消息发送成功!");
        } catch (Exception e) {
            log.error(">>>>>>>>fanoutExchangeProducerTest occur error",e);
        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }

    }
}

Comsumer

package com.chou.rabbitmq.exchange.fanout;

import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * @author Axel
 * @since 2021/4/17 18:55
 */
@Slf4j
public class FanoutExchangeConsumerTest {
    public static void main(String[] args) {
        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String exchangeName = "customer_fanout_Exchange";
            String routing_key_Name = "";
            String queue_Name = "fanout_test_queue";
            channel.queueDeclare(queue_Name, true, true, false, null);
            channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT.getType(), true, false, null);
            channel.queueBind(queue_Name, exchangeName, routing_key_Name);
            channel.basicConsume(queue_Name, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("收到的消息是:{} ------- {}", consumerTag, new String(message.getBody(), StandardCharsets.UTF_8));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {

                }
            });
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

Headers Exchange

  • Headers Exchange不使用RoutingKey去绑定, 而是通过消息headers的键值对匹配
  • 这个Exchange很少会使用, 这里就暂时不展开

代码中对象或者属性介绍

Virtual Host-虚拟主机

  • 虚拟地址, 用于进行逻辑隔离, 最上层的消息路由
  • 一个Virtual Host里面可以有若干个Exchange和Queue
  • 同一个Virtual Host里面不能有相同名称的Exchange或Queue 例如代码中
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.0.115");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");

connectionFactory.setVirtualHost("/");给当前的程序指定分区。

Binding-绑定

  • Exchange和Exchange, Queue之间的连接关系
  • 绑定中可以包含RoutingKey或者参数

Queue-消息队列

  • 消息队列, 实际存储消息数据
  • Durability : 是否持久化

    Durable : 是
    Transient : 否

  • Auto delete : 如选yes,代表当最后一个监听被移除之后, 该Queue会自动被删除

Message消息

  • 服务和应用程序之间传送的数据
  • 本质上就是一段数据, 由Properties和Payload(Body)组成
  • 常用属性 : delivery mode, headers(自定义属性)

Message消息的其他属性

  • content_type, content_encoding, priority
  • correlation_id : 可以认为是消息的唯一id
  • replay_to : 重回队列设定
  • expiration : 消息过期时间
  • message_id : 消息id
  • timestamp, type, user_id, app_id, cluster_id

编码实例

Producer

package com.chou.rabbitmq.message;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 生产这
 *
 * @author by Axel
 * @since 2024/7/29 下午9:21
 */
@Slf4j
public class Producer {
    public static void main(String[] args) {

        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String msg = "这是一个message消息测试类";
            //mandatory的作用:
            //当mandatory标志位设置为true时,如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,
            //那么broker会调用basic.return方法将消息返还给生产者;当mandatory设置为false时,出现上述情况broker会直接将消息丢弃;
            //通俗的讲,mandatory标志告诉broker代理服务器至少将消息route到一个队列中,否则就将消息return给发送者;
            Map<String,Object> headers = new HashMap<>();
            headers.put("name", "Choucj");
            AMQP.BasicProperties basicProperties = new AMQP.BasicProperties()
                    .builder()
                    .headers(headers)
                    .contentEncoding(StandardCharsets.UTF_8.name())
                    .expiration("5000")
                    .build();
            for (int i = 1; i <= 5; i++) {
                // 使用默认的exchange,routingKey与队列名称对齐
                channel.basicPublish("","msgTest",true,basicProperties,msg.getBytes());
            }
            log.info("消息发送成功!");
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

Consumer

package com.chou.rabbitmq.message;

import com.rabbitmq.client.*;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消费者
 *
 * @author by Axel
 * @since 2024/7/29 下午9:22
 */
@Slf4j
public class Consumer {
    public static void main(String[] args) {
        Channel channel = null;
        Connection connection = null;
        try {
            // 所有的中间件技术都是基于tcp/ip协议的基础上构建新型的协议规范,只不过rabbitmq 遵循的是amqp协议
            // 设置连接属性创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("192.168.0.115");
            connectionFactory.setPort(5672);
            connectionFactory.setVirtualHost("/");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            // 获取连接对象
            connection = connectionFactory.newConnection();
            // 创建通信通道
            channel = connection.createChannel();
            String queue_Name = "msgTest";
            channel.queueDeclare(queue_Name, true, true, false, null);
            channel.basicConsume(queue_Name, true, new DeliverCallback() {
                @Override
                public void handle(String consumerTag, Delivery message) throws IOException {
                    log.info("收到的消息是:{} ------- {}", consumerTag, new String(message.getBody(), StandardCharsets.UTF_8));
                    Object name = message.getProperties().getHeaders().get("name");
                    log.info("收到的消息属性值是:{} ------- {}", consumerTag, name);
                }
            }, new CancelCallback() {
                @Override
                public void handle(String consumerTag) throws IOException {
                }
            });
            System.in.read();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}