RabbitMQ 4.1.1初体验-队列和交换机

接上一篇博文

启动RabbitMQ

注意:根据版本,要提前安装Erlang的环境

这里用的版本是 RabbitMQ 4.0.9 Erlang 28.0.1

  • Windows
bash 复制代码
# 启动rabbitmq-server
D:\rabbitmq-server-windows-4.0.9\rabbitmq_server-4.0.9>sbin\rabbitmq-server.bat
  • Linux
    切换到安装目录的sbin目录下:
bash 复制代码
#启动
./rabbitmq-server  -detached

说明:

-detached 选项为后台启动运行rabbitmq;不加该参数表示前台启动;detach单词是"分离"的意思

rabbitmq的运行日志存放在rabbitmq安装目录的var目录下;

现在的目录是:/usr/local/rabbitmq_server-4.0.9/var/log/rabbitmq

detached单词 :独立的;超然的;单独的

查看RabbitMQ的状态

切换到sbin目录下执行:

bash 复制代码
./rabbitmqctl -n rabbit status 
# 或者
./rabbitmqctl  status

说明:-n rabbit 是指定节点名称为rabbit,目前只有一个节点,节点名默认为rabbit。 一个节点就是RabbitMQ服务器。

注意:此处-n rabbit 也可以省略

停止RabbitMQ

bash 复制代码
# 切换到sbin目录下执行
./rabbitmqctl shutdown

RabbitMQ管理命令

rabbitmqctl 是一个管理命令,可以管理rabbitmq的很多操作。

rabbitmqctl help可以查看一下有哪些操作

查看具体子命令 可以使用 rabbitmqctl help 子命令名称

用户管理

RabbitMQ Web控制台 需要身份验证和授权才能访问, 在控制台能够管理消息。创建用户,赋予用户角色(tag), 角色代表对Web控制台的使用权限

用户管理包括增加用户,删除用户,查看用户列表,修改用户密码。

这些操作都是通过rabbitmqctl管理命令来实现完成。

bash 复制代码
# 查看帮助
./rabbitmqctl help add_user

# 查看当前用户列表
./rabbitmqctl list_users

# 新增一个用户
语法:rabbitmqctl add_user Username  Password
示例: ./rabbitmqctl add_user admin 123456

# 设置用户角色 说明:此处设置admin用户的角色为管理员角色
rabbitmqctl set_user_tags  User  Tag
示例:./rabbitmqctl set_user_tags admin administrator

#查看用户权限
./rabbitmqctl list_permissions

#设置用户权限
./rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

-p : 要设置虚拟主机
/  : 虚拟主机名称, / 根主机,默认有这个主机
后面的 ".*"是config , writer ,read 权限
# 说明:此操作是设置admin用户拥有操作虚拟主机"/"下的所有权限

虚拟主机(virtual host)?

RabbitMQ中的虚拟主机(virtual host)是一个逻辑隔离的概念,它能够允许多个用户、交换器、队列和绑定共存。每个虚拟主机在RabbitMQ中都有自己的权限系统,使得不同的团队或项目可以在同一个RabbitMQ服务器上工作,彼此之间不会有太大的干扰。

权限参考页面:https://www.rabbitmq.com/access-control.html,可以后面再看这个列表

通过web页面新建虚拟主机

新建虚拟主机my-virtual-host(tags:administrator),参见下图

建完后如下

Default Queue Type:

  • classic:经典队列,RabbitMQ默认的队列类型是"classic",也就是直接的、传统的队列。在这种类型的队列中,消息被按照入队的顺序处理,每个消息都会被分发给一个消费者。
  • quorum:仲裁队列,是RabbitMQ从3.8.0版本,引入的一个新的队列类型,整个3.8.X版本,也都是在围绕仲裁队列进行完善和优化。仲裁队列相比Classic经典队列,在分布式环境下对消息的可靠性保障更高。官方文档中表示,未来会使用Quorum仲裁队列代替传统Classic队列。Quorum队列更适合于长期存在的队列,并且在对容错、数据安全方面有更严格要求的场景。相对于追求低延迟、非持久化等高级队列,Quorum队列提供了更可靠的数据复制机制,以满足对数据一致性和高可用性的要求。
  • Stream流式队列: Stream队列是RabbitMQ自3.9.0版本开始引入的一种新的数据队列类型。这种队列类型的消息是持久化到磁盘并且具备分布式备份的,更适合于消费者多,读消息非常频繁的场景。

交换机 (Exchange)

  • 交换器 (Exchange)类型

    • 1、Fanout Exchange(扇形)
    • 2、Direct Exchange(直连)
    • 3、Topic Exchange(主题)
    • 4、Headers Exchange(头部)
  • Fanout Exchange

    Fanout 扇形的,散开的; 扇形交换机

    投递到所有绑定的队列,不需要路由键,不需要进行路由键的匹配,相当于广播、群发;

  • 关键代码
    application.yml

yaml 复制代码
#定义要使用的交换机和队列名称
exchange:
  name: exchange.fanout

queueA: queue.a
queueB: queue.b

spring:
  application:
    name: fanout-exchange
  #配置连接 rabbitmq服务器
  rabbitmq:
    #mq服务器的ip
    host: 127.0.0.1
    #访问端口号
    port: 5672
    #用户名称
    username: admin
    #密码
    password: 123456
    #虚拟主机
    virtual-host: my-virtual-host
java 复制代码
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 创建Exchange , Queue等对象
 * 1.创建Exchange
 * 2.创建Queue
 * 3.将Exchange和Queue绑定
 */
@Configuration
public class RabbitConfig {
    @Value("${exchange.name}")
    private String exchangeName;

    @Value("${queueA}")
    private String queueA;

    @Value("${queueB}")
    private String queueB;


    //1.创建Exchange( 构造方法和Builder)

    /**
     * @Bean: 将方法的返回值对象放入到spring容器。
     *        这个方法返回值必须是对象
     *        默认这个bean在容器中的名称是方法名称
     *   name: 属性,指定bean的名称(id)

     */
    @Bean
    public FanoutExchange fanoutExchange(){
        //参数: 交换机名称
        return new FanoutExchange(exchangeName);
    }

    //2.创建Queue(构造方法和Builder)
    @Bean
    public Queue queueA(){
        //构造方法
        return new Queue(queueA);
    }
    @Bean
    public Queue queueB(){
        //构造方法
        return new Queue(queueB);
    }

    //将Exchange和Queue绑定, 因为是fanout exchange无需routingkey参数
    @Bean
    public Binding bindingA(FanoutExchange fanoutExchange,Queue queueA){
        //绑定队列queueA和交换机fanoutExchange
        return BindingBuilder.bind(queueA).to(fanoutExchange);
    }

    @Bean
    public Binding bindingB(FanoutExchange fanoutExchange,Queue queueB){
        //绑定队列queueA和交换机fanoutExchange
        return BindingBuilder.bind(queueB).to(fanoutExchange);
    }

}
java 复制代码
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class ProductMessageService {
    @Value("${exchange.name}")
    private String exchangeName;

    /**
     * RabbitTemplate:发送消息的对象(RedisTemplate)
     */
    @Resource
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {
        //发送一个文本消息, mq中消息由: 消息体和消息属性两个部分组成
        String text = DateUtil.now()+ ":欢迎使用扇形交换机FanoutExchange";
        Message message = new Message(text.getBytes());

        //发送消息 1:交换机名称 2:routingKey(路由键) 3.消息对象
        //FanoutExchange不需要routingKey
        rabbitTemplate.send(exchangeName,"",message);

        System.out.println("---------------->发送消息完成----------------");
    }
}

运行结果:

  • MQ的消息包含两部分
    • 消息体body
    • 消息属性MessageProperties

Direct Exchange

根据路由键精确匹配(一模一样)进行路由消息队列;

实操如下
application.yml

yaml 复制代码
#定义要使用的交换机和队列名称
exchange:
  name: exchange.direct

queueA: queue.direct.a
queueB: queue.direct.b

spring:
  application:
    name: fanout-exchange
  #配置连接 rabbitmq服务器
  rabbitmq:
    #mq服务器的ip
    host: 127.0.0.1
    #访问端口号
    port: 5672
    #用户名称
    username: admin
    #密码
    password: 123456
    #虚拟主机
    virtual-host: my-virtual-host
java 复制代码
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 创建Exchange , Queue等对象
 * 1.创建Exchange
 * 2.创建Queue
 * 3.将Exchange和Queue绑定
 */
@Configuration
public class RabbitConfig {
    @Value("${exchange.name}")
    private String exchangeName;

    @Value("${queueA}")
    private String queueA;

    @Value("${queueB}")
    private String queueB;


    //1.创建Exchange( 构造方法和Builder)

    /**
     * @Bean: 将方法的返回值对象放入到spring容器。
     *        这个方法返回值必须是对象
     *        默认这个bean在容器中的名称是方法名称
     *   name: 属性,指定bean的名称(id)

     */
    @Bean
    public DirectExchange directExchange(){
        //参数: 交换机名称   构建器模式(buidler)
        return ExchangeBuilder.directExchange(exchangeName).build();
    }

    //2.创建Queue(构造方法和Builder)
    @Bean
    public Queue queueA(){
        // buidler模式, durable:表示持久化队列
        return QueueBuilder.durable(queueA).build();
    }
    @Bean
    public Queue queueB(){
        // buidler模式, durable:表示持久化队列
        return QueueBuilder.durable(queueB).build();
    }

    //将Exchange和Queue绑定
    @Bean
    public Binding bindingA(DirectExchange directExchange,Queue queueA){
        //绑定队列queueA和交换机directExchange, 需要routingkey( info )
        return BindingBuilder.bind(queueA).to(directExchange).with("info");
    }

    @Bean
    public Binding bindingB(DirectExchange directExchange,Queue queueB){
        //绑定队列queueA和交换机fanoutExchange
        return BindingBuilder.bind(queueB).to(directExchange).with("error");
    }

}
java 复制代码
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class ProductMessageService {
    @Value("${exchange.name}")
    private String exchangeName;

    /**
     * RabbitTemplate:发送消息的对象(RedisTemplate)
     */
    @Resource
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {
        //发送一个文本消息, mq中消息由: 消息体和消息属性两个部分组成
        String text = DateUtil.now()+ ":欢迎使用直连交换机DirectExchange====";
        Message message = new Message(text.getBytes());

        //发送消息 1:交换机名称 2:routingKey(路由键) 3.消息对象
        //DirectExchange需要routingKey
        rabbitTemplate.send(exchangeName,"info",message);
        rabbitTemplate.send(exchangeName,"error",message);

        System.out.println("---------------->发送消息完成----------------");
    }
}

运行结果


  • Topic Exchange
    通配符匹配,相当于模糊匹配;
    • 匹配多个单词,用来表示任意数量(零个或多个)单词

    • * 匹配一个单词(必须有一个,而且只有一个),用.隔开的为一个单词:
      beijing.# == beijing.queue.abc, beijing.queue.xyz.xxx,beijing.queue.a.b
      beijing.* == beijing.queue, beijing.xyz

发送时指定的路由键:lazy.orange.rabbit

示例代码
application.yml

yaml 复制代码
#定义要使用的交换机和队列名称
exchange:
  name: exchange.topic
queueA: queue.topic.a
queueB: queue.topic.b
spring:
  application:
    name: topic-exchange
  #配置连接 rabbitmq服务器
  rabbitmq:
    #mq服务器的ip
    host: 127.0.0.1
    #访问端口号
    port: 5672
    #用户名称
    username: admin
    #密码
    password: 123456
    #虚拟主机
    virtual-host: my-virtual-host
java 复制代码
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 创建Exchange , Queue等对象
 * 1.创建Exchange
 * 2.创建Queue
 * 3.将Exchange和Queue绑定
 */
@Configuration
public class RabbitConfig {
    @Value("${exchange.name}")
    private String exchangeName;

    @Value("${queueA}")
    private String queueA;

    @Value("${queueB}")
    private String queueB;


    //1.创建Exchange( 构造方法和Builder)

    /**
     * @Bean: 将方法的返回值对象放入到spring容器。
     *        这个方法返回值必须是对象
     *        默认这个bean在容器中的名称是方法名称
     *   name: 属性,指定bean的名称(id)

     */
    @Bean
    public TopicExchange topicExchange(){
        //参数: 交换机名称   构建器模式(buidler)
        return ExchangeBuilder.topicExchange(exchangeName).build();
    }

    //2.创建Queue(构造方法和Builder)
    @Bean
    public Queue queueA(){
        // buidler模式, durable:表示持久化队列
        return QueueBuilder.durable(queueA).build();
    }
    @Bean
    public Queue queueB(){
        // buidler模式, durable:表示持久化队列
        return QueueBuilder.durable(queueB).build();
    }

    //将Exchange和Queue绑定, 因为是fanout exchange无需routingkey参数
    @Bean
    public Binding bindingA(TopicExchange topicExchange,Queue queueA){
        //绑定队列queueA和交换机TopicExchange, 需要routingkey( info )
        return BindingBuilder.bind(queueA).to(topicExchange).with("*.orange.*");
    }

    @Bean
    public Binding bindingB(TopicExchange topicExchange,Queue queueB){
        //绑定队列queueA和交换机fanoutExchange
        return BindingBuilder.bind(queueB).to(topicExchange).with("lazy.#");
    }

}
java 复制代码
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class ProductMessageService {
    @Value("${exchange.name}")
    private String exchangeName;

    /**
     * RabbitTemplate:发送消息的对象(RedisTemplate)
     */
    @Resource
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {
        //发送一个文本消息, mq中消息由: 消息体和消息属性两个部分组成
        String text = DateUtil.now()+ ":欢迎使用主题交换机TopicExchange====";
        Message message = new Message(text.getBytes());

        //发送消息 1:交换机名称 2:routingKey(路由键) 3.消息对象
        //DirectExchange需要routingKey
        rabbitTemplate.send(exchangeName,"lazy.orange.rabbit",message);
        rabbitTemplate.send(exchangeName,"lazy.orange.rabbit",message);

        System.out.println("---------------->发送消息完成----------------");
    }
}

运行结果


  • Headers Exchange(用的比较少)
    基于每条消息属性中的headers属性进行匹配;

    示例代码
yaml 复制代码
#定义要使用的交换机和队列名称
exchange:
  name: exchange.header

queueA: queue.header.a
queueB: queue.header.b

spring:
  application:
    name: header-exchange
  #配置连接 rabbitmq服务器
  rabbitmq:
    #mq服务器的ip
    host: 127.0.0.1
    #访问端口号
    port: 5672
    #用户名称
    username: admin
    #密码
    password: 123456
    #虚拟主机
    virtual-host: my-virtual-host
java 复制代码
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * 创建Exchange , Queue等对象
 * 1.创建Exchange
 * 2.创建Queue
 * 3.将Exchange和Queue绑定
 */
@Configuration
public class RabbitConfig {
    @Value("${exchange.name}")
    private String exchangeName;

    @Value("${queueA}")
    private String queueA;

    @Value("${queueB}")
    private String queueB;


    //1.创建Exchange( 构造方法和Builder)

    /**
     * @Bean: 将方法的返回值对象放入到spring容器。
     *        这个方法返回值必须是对象
     *        默认这个bean在容器中的名称是方法名称
     *   name: 属性,指定bean的名称(id)

     */
    @Bean
    public HeadersExchange headersExchange(){
        //参数: 交换机名称   构建器模式(buidler)
        return ExchangeBuilder.headersExchange(exchangeName).build();
    }

    //2.创建Queue(构造方法和Builder)
    @Bean
    public Queue queueA(){
        // buidler模式, durable:表示持久化队列
        return QueueBuilder.durable(queueA).build();
    }
    @Bean
    public Queue queueB(){
        // buidler模式, durable:表示持久化队列
        return QueueBuilder.durable(queueB).build();
    }

    //将Exchange和Queue绑定, 因为是fanout exchange无需routingkey参数
    @Bean
    public Binding bindingA(HeadersExchange headersExchange,Queue queueA){
        //匹配条件
        Map<String, Object> headerValues = new HashMap<>();
        headerValues.put("type",1);
        headerValues.put("status","m");
        //绑定队列queueA和交换机HeadersExchange
        return BindingBuilder.bind(queueA).to(headersExchange).whereAll(headerValues).match();
    }

    @Bean
    public Binding bindingB(HeadersExchange headersExchange,Queue queueB){
        //匹配条件
        Map<String, Object> headerValues = new HashMap<>();
        headerValues.put("type",2);
        headerValues.put("status","f");
        //绑定队列queueA和交换机HeadersExchange
        return BindingBuilder.bind(queueB).to(headersExchange).whereAll(headerValues).match();
    }

}
java 复制代码
import cn.hutool.core.date.DateUtil;
import jakarta.annotation.Resource;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ProductMessageService {
    @Value("${exchange.name}")
    private String exchangeName;

    /**
     * RabbitTemplate:发送消息的对象(RedisTemplate)
     */
    @Resource
    private RabbitTemplate rabbitTemplate;

    public void sendMessage() {


        //指定匹配条件,需要使用 Message的属性, 在header中增加条件
        Map<String, Object> headerValues = new HashMap<>();
        headerValues.put("type",1);
        headerValues.put("status","m");

        MessageProperties prop  = new MessageProperties();
        prop.setHeaders(headerValues);

        //使用 MessageBuilder创建消息
        //发送一个文本消息, mq中消息由: 消息体和消息属性两个部分组成
        String text = DateUtil.now()+ ":欢迎使用Header交换机HeaderExchange====";
        Message message = MessageBuilder.withBody(text.getBytes()).andProperties(prop).build();

        //发送消息 1:交换机名称 2:routingKey(路由键) 3.消息对象
        //HeaderExchange 不需要routingKey
        rabbitTemplate.send(exchangeName,"",message);

        System.out.println("---------------->发送消息完成----------------");
    }
}

运行结果

默认交换机

  • 创建虚拟主机的时候就会创建默认交换机,默认交换机的名字是空字符串,默认交换机是直连交换机(Direct)。
  • 每新建一个队列,都会自动和默认交换机绑定,绑定的路由key是该队列的名字
  • 向默认交换机发送消息,交换机名指定为空字符串,路由key为队列的名字
相关推荐
掘金-我是哪吒34 分钟前
分布式微服务系统架构第158集:JavaPlus技术文档平台日更-JVM基础知识
jvm·分布式·微服务·架构·系统架构
东窗西篱梦1 小时前
Redis集群部署指南:高可用与分布式实践
数据库·redis·分布式
Acrel_Fanny1 小时前
Acrel-1000系列分布式光伏监控系统在湖北荆门一马光彩大市场屋顶光伏发电项目中应用
分布式
xufwind1 小时前
spark standlone 集群离线安装
大数据·分布式·spark
半新半旧2 小时前
Redis集群和 zookeeper 实现分布式锁的优势和劣势
redis·分布式·zookeeper
亲爱的非洲野猪3 小时前
Kafka “假死“现象深度解析与解决方案
分布式·kafka
CodeWithMe3 小时前
【Note】《Kafka: The Definitive Guide》第三章: Kafka 生产者深入解析:如何高效写入 Kafka 消息队列
分布式·kafka
虾条_花吹雪3 小时前
2、Connecting to Kafka
分布式·ai·kafka
Edingbrugh.南空4 小时前
Hadoop高可用集群搭建
大数据·hadoop·分布式
Bug退退退1235 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq