RabbitMQ系列(二)——RabbitMQ的命令行操作和交换机入门

前言

在上一篇文章中,我们介绍了RabbitMQ的作用和安装方式,本篇文章将对RabbitMQ的基本命令行操作进行讲解,同时结合Springboot框架来快速实现一个生产者-mq-消费者模型的案例。希望对各位读者有所帮助

一、使用命令行来管理rabbitmq服务

管理插件
rabbitmq-plugins enable 插件名称

rabbitmq可以让我们使用插件来更好的管理我们的MQ,一般我们会使用rabbitmq自带的rabbitmq_plugins enable rabbitmq_management插件来作为视图工具管理我们的mq环境


激活插件并重启RabbitMQ服务后,我们可以通过主机名:15672来访问我们的服务,账号密码为我们在核心配置文件中所配置的账号密码,默认为guest

启动服务
rabbitmq-server start &
停止服务

需要注意,这里的停止服务并没有完全杀死进程,如果想要重启服务的话,可以使用下面的恢复服务命令

rabbitmqctl stop_app
恢复服务命令
rabbitmqctl start_app
查看节点状态
rabbitmqctl status

关于rabbitmq的命令行操作还有很多,更多命令可以参考下面的小总结:

其他常用命令
命令 作用
rabbitmqctl add_user username password 添加用户
rabbitmqctl list_users 列出所有用户
rabbitmqctl delete_user username 删除用户
rabbitmqctl clear_permissions -p vhostpath username 清除用户权限
rabbitmqctl list_user_permissions username 列出用户权限
rabbitmqctl change_password username newpassword 修改密码
rabbitmqctl set_permissions -p vhostpath username "." "." ".*" 设置用户权限
rabbitmqctl add_vhost vhostpath 创建虚拟主机
rabbitmqctl list_vhosts 列出所有虚拟主机
rabbitmqctl list_permissions -p vhostpath 列出虚拟主机上所有权限
rabbitmqctl delete_vhost vhostpath 删除虚拟主机
rabbitmqctl list_queues 查看所有队列信息
rabbitmqctl -p vhostpath purge_queue blue 清除队列里的消息
rabbitmqctl reset 移除所有数据,要在rabbitmqctl stop_app之后使用
rabbitmqctl join_cluster <clusternode> [--ram] 组成集群命令
rabbitmqctl cluster_status 查看集群状态
rabbitmqctl change_cluster_node_type disc | ram 修改集群节点的存储形式
rabbitmqctl forget_cluster_node [--offline] 忘记节点(摘除节点)
rabbitmqctl rename_cluster_node oldnode1 newnode1 [oldnode2] [newnode2 ...] (修改节点名称)

二、交换机的介绍和使用

我们在上一篇文章曾经介绍过,交换机的使用可以实现生产者和消费者之间的解耦。不仅如此,RabbitMQ中还提供了多种类型的交换机来供我们使用,让我们可以更加灵活地实现信息的转发。

(一)交换机的类型
直连交换机(Direct Exchange)

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


主题交换机(Topic Exchange)

所有发送到Topic Exchange的消息被转发到所有关心RouteKey中指定Topic的Queue上
Exchange 将RouteKey和某Topic进行模糊匹配此时队列需要绑定一个Topic
需要注意的是,我们可以使用通配符来进行模糊匹配

符号“#”匹配一个或多个词符号“”匹配不多不少一个词
例如:“log.#”能够匹配到“log.info.oa”
“log.
”只会匹配到“log.erro

输出交换机(Fanout Exchange)

Fanout Exchange的特点十分明显:
(1)不处理路由键,只需要简单的将队列绑定到交换机上
(2)发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
(3)Fanout交换机转发消息是最快的


(二)交换机的入门使用

下面,我们将使用springboot框架来编写测试用例,让读者快速上手rabbitmq的基本使用

步骤一:创建Springboot项目,在pom文件中引入rabbitmq的客户端依赖

需要注意的是,由于本篇文章演示的案例是基于rabbitmq3.6.5的版本的,所以引入的依赖版本也是3.6.5。在实际项目中这里就根据实际情况来就行。

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>3.6.5</version>
</dependency>
步骤二:编写生产者端代码
public class Producer {
    /**
     * 作为快速开始的入门用例,我们主要将在Producer这个类中实现
     * 生产者生产信息后,投递信息到Exchange交换机的过程
     * @param args
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建ConnectionFactory , 并进行初始化
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("自己的主机名");
        cf.setPort(5672);
        cf.setVirtualHost("/");

        // 2. 获取连接对象
        Connection connection = cf.newConnection();

        // 3. 创建channel
        Channel channel = connection.createChannel();

        // 4. 通过channel发送信息
        for (int i = 0; i < 5; i++) {
            String msg = "hello rabbitmq";
            // 参数分别对应 exchange, routingKey, base properties, 信息体 body
            channel.basicPublish("","testQueue1",null,msg.getBytes());
        }
        System.out.println("发送完毕...");

    }
}
步骤三: 编写消费者端代码
public class Consumer {

    /**
     * 作为快速开始的入门用例,我们主要将在Consumer这个类中实现
     * 创建队列和消费者,并进行消费的操作
     */
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1. 创建和初始化ConnectFactory
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("自己的主机名");
        cf.setPort(5672);
        cf.setVirtualHost("/");

        // 2. 获取工厂连接
        Connection connection = cf.newConnection();

        // 3. 创建channel
        Channel channel = connection.createChannel();

        // 4. 创建队列
        // 对应的参数分别为 queueName, durable,  exclusive, autoDelete , 其他
        channel.queueDeclare("testQueue1",true,false,false,null);

        // 5. 创建一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);

        // 6. 定义绑定关系
        channel.basicConsume("testQueue1",true,consumer);

        // 7. 在循环中接收信息
        while (true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String receiveMsg = new String(delivery.getBody());
            System.out.println("接收到的信息为:" + receiveMsg);
        }
    }
}
步骤四: 分别执行消费端、生产端的代码

注意,需要先启动消费端的代码初始化队列



从结果来看,消费端确实可以收到生产者发送的信息,同时我们也可以注意到,我们并没有在代码中将生产者和队列进行强绑定,只是将信息发送给了交换机(上面的交换机设置为空字符串,默认表示AMQP default这个交换机)但最终信息还是可以成功转发到对应的队列上。

这是怎么实现的呢?

答案是,rabbitmq默认的交换机AMQP default是direct类型的交换机,该交换机会把接收到的信息转发给同名的queue。如果匹配不到,则丢弃信息。

(三)三种类型交换机的使用

在本小节中将对第一小节描述的三种交换机的使用和特点进行演示

direct exchange代码演示
消费者端代码

这里需要注意的是,为了方便演示,我们在消费端进行了exchange和queue的初始化,同时对exchange和queue进行了绑定。

public class Direct_Consumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1. 创建和初始化 ConnectionFactory
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("自己的主机名");
        cf.setPort(5672);
        cf.setVirtualHost("/");
        cf.setAutomaticRecoveryEnabled(true); // 开启集群模式下的自动恢复连接
        cf.setNetworkRecoveryInterval(3000);  // 设置网络异常重试时间间隔
        // 2. 获取connection对象
        Connection connection = cf.newConnection();
        // 3. 创建channel
        Channel channel = connection.createChannel();
        // 4. 定义参数
        String exchangeName = "xiaoming_direct_exchange";
        String exchangeType = "direct";
        String queueName = "xiaoming_direct_queue";
        String routingKey = "xiaoming";
        // 5. 声明组件
            // 声明交换机  对应的参数名是 exchange name, exchange type, durable 是否持久化,  autoDelete ,internal,arguments
        channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);
            // 声明一个队列  对应参数名是queue name, durable, exclusive, autoDelete,arguments后面都会再具体细讲
        channel.queueDeclare(queueName,false,false,false,null);
        // 6. 建立绑定关系
        channel.queueBind(queueName,exchangeName,routingKey);
        // 7. 定义一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 参数:队列名称、是否自动ACK、Consumer
        channel.basicConsume(queueName,true,consumer);
        // 死循环读取信息
        while (true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            System.out.println("接收到的信息是:" + new String(delivery.getBody()));
        }
    }
}
生产者的代码
public class Direct_Producer {
    /*
       生产者使用直连交换机进行信息传递
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建和初始化 ConnectionFactory
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("自己的主机名");
        cf.setPort(5672);
        cf.setVirtualHost("/");
        // 2. 获取connection对象
        Connection connection = cf.newConnection();
        // 3. 创建channel
        Channel channel = connection.createChannel();
        // 4. 定义参数
        String exchangeName = "xiaoming_direct_exchange";
        String routingKey = "xiaoming";
        String msg = "hello , rabbit mq";
        // 5. 推送
        channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());
        System.out.println("生产者发送成功....");
    }
}

依次执行消费端,生产端的代码,我们可以得到如下结果:



需要注意的是,direct exchange的匹配规则是,将生产端信息的routingKey和所绑定队列的routingKey进行对比,只有routingKey完全一致的情况下,才可以成功转发信息到队列上。所以生产端在设置routingKey时一定要注意不要设置无效的routingKey,否则会出现queque接收不到信息的情况。

topic exchange代码演示
消费端代码
public class Topic_Consumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1. 创建和初始化 ConnectionFactory
        ConnectionFactory cf = new ConnectionFactory();
       ...
        // 2. 获取connection对象
        Connection connection = cf.newConnection();
        // 3. 创建channel
        Channel channel = connection.createChannel();
        // 4. 定义参数
        String exchangeName = "xiaoming_topic_exchange";
        String exchangeType = "topic";
        String queueName = "xiaoming_topic_queue";
        String routingKey = "xiaoming.#";
        // 5. 声明组件
        // 声明交换机  对应的参数名是 exchange name, exchange type, durable 是否持久化,  autoDelete ,internal,arguments
        channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);
        // 声明一个队列  对应参数名是queue name, durable, exclusive, autoDelete,arguments后面都会再具体细讲
        channel.queueDeclare(queueName,false,false,false,null);
        // 6. 建立绑定关系
        channel.queueBind(queueName,exchangeName,routingKey);
        // 7. 定义一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 参数:队列名称、是否自动ACK、Consumer
        channel.basicConsume(queueName,true,consumer);
        System.out.println("队列和交换机初始化成功...");
        // 死循环读取信息
        while (true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            System.out.println("消费端接收到的信息是:" + new String(delivery.getBody()));
        }
    }
}
生产端代码
public class Topic_Producer {
    /*
       生产者使用topic交换机进行信息传递
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建和初始化 ConnectionFactory
        ConnectionFactory cf = new ConnectionFactory();
        ...
        // 2. 获取connection对象
        Connection connection = cf.newConnection();
        // 3. 创建channel
        Channel channel = connection.createChannel();
        // 4. 定义参数
        String exchangeName = "xiaoming_topic_exchange";
        String routingKey = "xiaoming.store";
        String msg = "hello , rabbit mq";
        // 5. 推送
        channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());
        System.out.println("生产者发送成功....");
    }
}

依次启动消费端、生产端代码,结果如下:



fanout exchange代码演示

由上面的讲解我们可以知道,fanout exchange是一种较为特别的交换机,他不需要指定routingKey作为匹配规则,而是将接收到的信息直接转发到

消费端代码
public class Fanout_Consumer {
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        // 1. 创建和初始化 ConnectionFactory
        ConnectionFactory cf = new ConnectionFactory();
        ...
        // 2. 获取connection对象
        Connection connection = cf.newConnection();
        // 3. 创建channel
        Channel channel = connection.createChannel();
        // 4. 定义参数
        String exchangeName = "xiaoming_fanout_exchange";
        String exchangeType = "fanout";
        String queueName = "xiaoming_fanout_queue";
        String routingKey = ""; // 不设置路由键
        // 5. 声明组件
        // 声明交换机  对应的参数名是 exchange name, exchange type, durable 是否持久化,  autoDelete ,internal,arguments
        channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);
        // 声明一个队列  对应参数名是queue name, durable, exclusive, autoDelete,arguments后面都会再具体细讲
        channel.queueDeclare(queueName,false,false,false,null);
        // 6. 建立绑定关系
        channel.queueBind(queueName,exchangeName,routingKey);
        // 7. 定义一个消费者
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 参数:队列名称、是否自动ACK、Consumer
        channel.basicConsume(queueName,true,consumer);
        System.out.println("队列和交换机初始化成功...");
        System.out.println("消费端开始接受信息...");
        // 死循环读取信息
        while (true){
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            System.out.println("消费端接收到的信息是:" + new String(delivery.getBody()));
        }
    }
}
生产者端代码
public class Fanout_Producer {
    /*
       生产者使用fanout交换机进行信息传递
     */
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1. 创建和初始化 ConnectionFactory
        ConnectionFactory cf = new ConnectionFactory();
        ...
        // 2. 获取connection对象
        Connection connection = cf.newConnection();
        // 3. 创建channel
        Channel channel = connection.createChannel();
        // 4. 定义参数
        String exchangeName = "xiaoming_fanout_exchange";
        String msg = "hello , rabbit mq";
        // 5. 推送
        channel.basicPublish(exchangeName,"",null,msg.getBytes());
        System.out.println("生产者发送成功....");
        channel.close();
        connection.close();
    }
}

依次执行消费端、生产端的代码,得到的结果如下:


以上,即为本篇文章对交换机类型的介绍以及代码演示,在实际应用中我们可以根据项目需要来选择合适的交换机进行信息的转发。关于rabbitmq的更多知识点,可以参数本系列其他文章。

posted @ 2021-09-12 14:35  moutory  阅读(252)  评论(0编辑  收藏  举报  来源