22.5 RabbitMQ

在这里插入图片描述
在这里插入图片描述

1. RabbitMQ

1.1 概念上的组成

在这里插入图片描述

1.2 使用消息队列的好处

在这里插入图片描述
在这里插入图片描述

1.3 消息队列特性

在这里插入图片描述

1.4 RabbitMQ特点

在这里插入图片描述
在这里插入图片描述

1.5 RabbitMQ核心概念

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 安装Rabbitmq

2.1 基于Linux安装

环境配置:
前提:在一个新建的阿里云的Cent OS 7.6上安装,不要对yum换源,否则可能会安装失败。

echo "export LC_ALL=en_US.UTF-8"  >>  /etc/profile
source /etc/profile
下面两个安装方法,任选其一即可,推荐方法一:

2方法一(推荐)
第一步:执行
curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash
第二步,执行:
curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash
第三步:sudo yum install rabbitmq-server-3.8.2-1.el7.noarch
第四步,看到类似以下的画面:

在这里插入图片描述

输入y
即可完成安装。
启动RabbitMQ
systemctl start rabbitmq-server

看看端口有没有起来,查看状态

rabbitmqctl status
 
3方法二(优先推荐方法一,如果方法一失败了,尝试本方法二)
导入密钥
rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc

下载rpm安装包:
wget --content-disposition https://packagecloud.io/rabbitmq/rabbitmq-server/packages/el/7/rabbitmq-server-3.8.2-1.el7.noarch.rpm/download.rpm 
如果速度比较慢,就用:
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.2/rabbitmq-server-3.8.2-1.el7.noarch.rpm
或者下载教辅的安装包,然后scp  /Users/教辅/RabbitMQ教辅/rabbitmq-server-3.8.2-1.el7.noarch.rpm root@114.55.219.216:/root

下载完成后,安装:
yum install rabbitmq-server-3.8.2-1.el7.noarch.rpm

如果出现解压错误,说明下载了多次,用ls -la看一下有几个文件,如果有多个安装包,要把多余的删掉,把正确的改名为rabbitmq-server-3.8.2-1.el7.noarch.rpm,再执行yum install来安装
到这里RabbitMQ就安装好了

3. RabbitMQ常用命令

开启web管理界面
rabbitmq-plugins enable rabbitmq_management

停止RabbitMQ
$rabbitmqctl stop

设置开机启动
$ systemctl enable rabbitmq-server 

启动RabbitMQ
$ systemctl start rabbitmq-server

看看端口有没有起来,查看状态

$ rabbitmqctl status 

要检查RabbitMQ服务器的状态,请运行:

systemctl status rabbitmq-server

4. 后台管理

4.1 开启Rabbitmq管理后台

开启RabbitMQ管理后台
# rabbitmq-plugins enable rabbitmq_management

配置阿里云安全组,打开15672端口

添加admin用户:
rabbitmqctl add_user admin password
rabbitmqctl set_user_tags admin administrator




浏览器访问ip: 15672
用admin,密码password即可登录

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.2 admin用户配置

获取虚拟主机权限
在这里插入图片描述
在这里插入图片描述

效果:
在这里插入图片描述
在这里插入图片描述

5. 实战案例

5.1 依赖引入

    <dependency>
      <groupId>com.rabbitmq</groupId>
      <artifactId>amqp-client</artifactId>
      <version>5.8.0</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.29</version>
    </dependency>

5.2 基础应用

1. 生产者:发送消息到消息队列

package helloworld;

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

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

/**
 * 描述:     Hello World 的发送类,连接到RabbitMQ服务端,然后发送一条消息,然后退出。
 */
public class Send {

    //对列名称
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("47.97.208.162");
        factory.setUsername("root");
        factory.setPassword("abc12345");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        //声明队列:参数:(队列名称,系统重启队列保存?,当前连接独有?,自动删除?,)
        /**
         * queue: 队列名
         * durable: 是否持久化,true:重启之后,队列依然存在,false则不存在
         * exclusive: 是否独占,true:只能有一个消费者监听这个队列,一般设置为false
         * autoDelete: 是否自动删除,true:当没有消费者的时候,则自动删除这个队列
         * arguments: map类型的其他参数
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //发布消息
        String message = "Hello World! To Rabbitmq";
        //交换机,队列名, ,消息
        /**
         * exchange: 交换机的名称,简单模式下没有,所以直接设置为 ""
         * routingKey: 路由key,映射路径,如果交换机没有,则路由key和队列名保持一致
         * props: 配置参数
         * body: 消息数据
         */
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println("发送了消息:" + message);
        //关闭连接
        channel.close();
        connection.close();
    }
}

在这里插入图片描述
访问不通:云服务器是否开放5672端口
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 消费者:获取消息队列消息

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.
 *
   Copyright 2007-2015 Pivotal Software, Inc.
   https://github.com/rabbitmq/rabbitmq-tutorials
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       https://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package helloworld;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * 描述:     接收消息,并打印,持续运行
 */
public class Recv {
    private final static String QUEUE_NAME = "hello";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("47.97.215.215");
        factory.setUsername("root");
        factory.setPassword("abc12345");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //接收消息并消费
        /**
         * queue: 监听的队列名
         * autoAck: 是否自动确认,true:告知mq消费者已经消费的确认通知
         * callback: 回调函数,处理监听到的消息
         */
        channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel){
             /**
             * 重写消息配送方法
             * @param consumerTag 消息的标签(标识)
             * @param envelope  信封(一些信息,比如交换机路由等等信息)
             * @param properties 配置信息
             * @param body 收到的消息数据
             * @throws IOException
             */
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                    BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        });
    }

}

在这里插入图片描述

在这里插入图片描述
消费者获取消息后,不关闭消费者,服务不会关闭

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

3. 多个消费者处理

在这里插入图片描述
RabbitMQ如何将消息分发给多个消费者?默认情况下是循环调度方式分发

4. 公平派遣方式分发消息

在这里插入图片描述
关键代码

//设置消费者一次处理的消息数量
channel.basicQos(1);

//关闭自动接受,改为手动确认:basicConsume的第二个参数改为false
channel.basicConsume(TASK_QUEUE_NAME, false, new DefaultConsumer(channel) {

//确认消息已处理,等待接受新消息
channel.basicAck(envelope.getDeliveryTag(), false);
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //设置RabbitMQ地址
        factory.setHost("localhost");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        final Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
        System.out.println("开始接收消息");
        //设置消费者一次处理的消息数量
        channel.basicQos(1);
        //关闭自动确认消息,改为手动确认:basicConsume的第二个参数改为false
        channel.basicConsume(TASK_QUEUE_NAME, false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到了消息:" + message);
                try {
                    doWork(message);
                } finally {
                    System.out.println("消息处理完成");
                    //确认消息已处理,等待接受新消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        });
    }

6. 交换机工作模式

在这里插入图片描述

在这里插入图片描述

6.1 fanout广播模式

在这里插入图片描述

在这里插入图片描述
临时队列:生产者生成的消息在该队列存活时间极短,如果没有消费者立刻清除消息

1. 生产者:发送消息

//设置交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

在这里插入图片描述
对比不使用交换机

在这里插入图片描述

2. 消费者:接受消息

在这里插入图片描述

对比交换机和不使用交换机
在这里插入图片描述

3. 工作原理

消费者:
指定一个交换机(EXCHANGE_NAME)并采取广播的方式(BuiltinExchangeType.FANOUT),
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
然后创建临时队列(String queueName = channel.queueDeclare().getQueue();)绑定交换机用来接受消息.

生产者:
指定一个交换机(EXCHANGE_NAME)并采取广播的方式(BuiltinExchangeType.FANOUT),
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
不指定对列,采取默认的临时对列,即放置到该交换机的临时对列上.此时如果没有消费者存在即没有临时队列给生产者存储消息,消息会在交换机被删除
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));


生产者生成的消息放置在临时队列,消费者就能第一时间获取到,随后由于临时队列的特性消息会自动删除

6.2 direct模式

在这里插入图片描述

1.使用交换机;2.根据消息类型将消息发送到指定的消息队列.
好处:将消息合理的分发
在这里插入图片描述
日志管理示例
在这里插入图片描述

1. 生产者

    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        String message = "info:Hello World!";

        channel.basicPublish(EXCHANGE_NAME, "info", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息," + "等级为info,消息内容:" + message);

        message = "warning:Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "warning", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息," + "等级为warning,消息内容:" + message);

        message = "error:Hello World!";
        channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息," + "等级为error,消息内容:" + message);
        channel.close();
        connection.close();
    }

2. 消费者1

消费3种临时队列消息

    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //生成一个随机的临时的queue
        String queueName = channel.queueDeclare().getQueue();
        //一个交换机同时绑定3个queue
        channel.queueBind(queueName, EXCHANGE_NAME, "info");
        channel.queueBind(queueName, EXCHANGE_NAME, "warning");
        channel.queueBind(queueName, EXCHANGE_NAME, "error");

        System.out.println("开始接收消息");
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                    BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }

3. 消费者2

消费1种临时队列消息

    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //生成一个随机的临时的queue
        String queueName = channel.queueDeclare().getQueue();
        //一个交换机绑定1个queue
        channel.queueBind(queueName, EXCHANGE_NAME, "error");

        System.out.println("开始接收消息");
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                    BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }

6.3 topic模式

在这里插入图片描述
重点:模糊匹配4字

匹配规则:
在这里插入图片描述

在这里插入图片描述

1. 生产者

    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);

        String message = "Animal World";

        String[] routingKeys = new String[9];
        //定义对列
        routingKeys[0] = "quick.orange.rabbit";
        routingKeys[1] = "lazy.orange.elephant";
        routingKeys[2] = "quick.orange.fox";
        routingKeys[3] = "lazy.brown.fox";
        routingKeys[4] = "lazy.pink.rabbit";
        routingKeys[5] = "quick.brown.fox";
        routingKeys[6] = "orange";
        routingKeys[7] = "quick.orange.male.rabbit";
        routingKeys[8] = "lazy.orange.male.rabbit";

        for (int i = 0; i < routingKeys.length; i++) {
            channel.basicPublish(EXCHANGE_NAME, routingKeys[i], null, message.getBytes("UTF-8"));
            System.out.println("发送了:" + message+" routingKey:"+routingKeys[i]);
        }

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

在这里插入图片描述

2. 消费者1

    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //生成一个随机的临时的queue
        String queueName = channel.queueDeclare().getQueue();
        String routingKey = "*.orange.*";
        channel.queueBind(queueName, EXCHANGE_NAME, routingKey);

        System.out.println("开始接收消息");
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                    BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message + " routingKey: " + envelope.getRoutingKey());
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }

在这里插入图片描述

3. 消费者2

    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //生成一个随机的临时的queue
        String queueName = channel.queueDeclare().getQueue();
        String routingKey = "*.*.rabbit";
        channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
        String routingKey2 = "lazy.#";
        channel.queueBind(queueName, EXCHANGE_NAME, routingKey2);

        System.out.println("开始接收消息");
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                    BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message + " routingKey: " + envelope.getRoutingKey());
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }

在这里插入图片描述

7. SpringBoot整合RabbitMQ

7.1 依赖引入

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

7.2 application.properties配置文件

#RabbitMQ配置信息
spring.rabbitmq.addresses=127.0.0.1:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000

7.3 RabbitMQ配置类

package com.imooc.springbootrabbitmqproducer;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 描述:     rabbitmq配置类
 */
@Configuration
public class TopicRabbitConfig {
    //定义队列1
    @Bean
    public Queue queue1() {
        return new Queue("queue1");
    }
    //定义队列2
    @Bean
    public Queue queue2() {
        return new Queue("queue2");
    }
    //配置交换机
    @Bean
    TopicExchange exchange() {
        return new TopicExchange("bootExchange");
    }
    
    //交换机与队列1绑定,设置该队列接受dog.red描述的消息
    @Bean
    Binding bingdingExchangeMessage1(Queue queue1, TopicExchange exchange) {
        return BindingBuilder.bind(queue1).to(exchange).with("dog.red");
    }
    //交换机与队列2绑定,设置该队列接受dog.#描述的消息
    @Bean
    Binding bingdingExchangeMessage2(Queue queue2, TopicExchange exchange) {
        return BindingBuilder.bind(queue2).to(exchange).with("dog.#");
    }
}

7.4 生产者

package com.imooc.springbootrabbitmqproducer;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 描述:     发送消息
 */
@Component
public class MsgSender {

    @Autowired
    private AmqpTemplate rabbitmqTemplate;

    public void send1() {
        String message = "This is message 1, routing key is dog.red";
        System.out.println("发送了:"+message);
        this.rabbitmqTemplate.convertAndSend("bootExchange", "dog.red", message);
    }

    public void send2() {
        String message = "This is message 2, routing key is dog.black";
        System.out.println("发送了:"+message);
        this.rabbitmqTemplate.convertAndSend("bootExchange", "dog.black", message);
    }
}

7.5 消费者

package com.imooc.springbootrabbitmqconsumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 描述:     消费者1
 * RabbitListener监听注解
 */
@Component
@RabbitListener(queues = "queue1")
public class Receiver1 {

    @RabbitHandler
    public void process(String message) {
        System.out.println("Receiver1: " + message);
    }
}

package com.imooc.springbootrabbitmqconsumer;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * 描述:     消费者2
 */
@Component
@RabbitListener(queues = "queue2")
public class Receiver2 {

    @RabbitHandler
    public void process(String message) {
        System.out.println("Receiver2: " + message);
    }
}

*********************************************************************************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与海boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值