消息队列RabbitMQ介绍和使用

目录​​​​​​​

一、RabbitMQ初识

1.1 介绍

1.2 初识消息队列

核心思想:

消息队列:

1.3 暂无

1.4 暂无 

1.5 RabbitMQ 的特点和核心概念

AMQP协议

RabbitMQ核心概念

消息流转过程

二、RabbitMQ的安装

2.1 RabbitMQ在CentOS下的安装 part1

2.2 RabbitMQ在CentOS下的安装 part2

RabbitMQ常用命令

2.3 RabbitMQ在macOS 和 Windows下的安装

三、RabbitMQ的应用

3.1 RabbitMQ的管理后台

3.2 第一个生产者

3.3 第一个消费者

3.4 暂无

3.5 根据消息内容作处理

3.6 多个消费者平均压力

四、交换机类型

4.1 交换机类型 - fanout

交换机工作模式:

fanout模式代码:

4.2 暂无

4.3 交换机类型 - direct

​编辑

 4.4 交换机类型 - topic生产者

topic模式:

4.5 交换机类型 - topic 消费者

五、SpringBoot与RabbitMQ的整合

5.1 SpringBoot整合RabbitMQ

六、总结


一、RabbitMQ初识

1.1 介绍

        1. 初识 RabbitMQ

        2. RabbitMQ 的安装和启动

        3. RabbitMQ 管理后台

        4. 实战案例演示

        5. 交换机工作模式(类似于计算机网络中 路由器下一层级硬件)

        6. Spring Boot 整合 RabbitMQ

1.2 初识消息队列

核心思想:

        接收并转发消息。你可以把它想象成一个邮局,邮筒、信件,我们把信件放进邮筒就可以走了,我们非常确定邮筒会经过邮递员叔叔派送,送到收件人手中。

        RabbitMQ就包含邮局、邮筒、邮递员。我们只要把消息给到RabbitMQ,剩下的就不必操心。我们就可以认为,消息最终会交到我们希望的收件人手中。

producer:

        生产者、消息发送者

queue:

        邮筒,由RabbitMQ管理。

consumer:

        消费者、收件人

三者可以不在一台机器上,实现解耦。

消息队列:

Message Queue:

        enqueue、dequeue

特性:

        容灾(消息可以持久化)

        性能

为什么要用消息队列:

        系统解耦(避免子系统之间相互调用接口)

       异步调用

       流量削峰(把海量请求先存储在队列中,再以我们能接受的频率发送给我们)

1.3 暂无

1.4 暂无 

1.5 RabbitMQ 的特点和核心概念

开源、跨语言

Erlang语言编写:

        交换机上常用语言、性能很好、通信方面有优势,和原生Socket有一样低的延迟。节点与节点之间数据传输和数据复制,性能很好。MQ技术选型指标,数据复制延迟低不低。数据复制延迟低,可以承受流量高峰期压力。

应用广泛

社区活跃、API丰富

AMQP协议

advanced message queuing protocol

        专门面向消息中间件的一种开放式标准应用层协议

RabbitMQ核心概念

Server: 服务 

connection: 与Server建立连接

channel: 信道。connection里面有多个channel。几乎所有的操作都在信道上进行,客户端可以建立多个信道

message: 消息。由properties(对消息进行额外修饰,优先级、延迟、自定义属性)和body组成

virtual host: 虚拟主机,顶层隔离。同一个虚拟主机下,不能有重复的交换机和queue 

Exchange: 交换机,接收生产者的消息的。然后根据指定的路由器去把消息转发到所绑定的队列上。

binding: 绑定交换与队列 

routing key: 路由键,路由规则,虚拟机可以用它来确定这个消息如何进行一个路由

queue: 队列,消费者只需要监听队列来消费 信息,不需要关注消息来自于哪个Exchange

Exchange和message queue存在着绑定的关系,一个exchange可以绑定多个消息队列

消息流转过程

二、RabbitMQ的安装

2.1 RabbitMQ在CentOS下的安装 part1

安装Erlang

         安装erlang-rpm包,该包经过RabbitMQ官方处理

        RabbitMQ版本与Erlang版本有对应关系

vim /etc/yum.repos.d/rabbitmq_erlang.repo

[rabbitmq-erlang]
name=rabbitmq-erlang
baseurl=https://dl.bintray.com/rabbitmq-erlang/rpm/erlang/22/el/7
gpgcheck=1
gpgkey=https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc
repo_gpgcheck=0
enabled=1



yum clear all
yum makecache
yum install erlang
要确认源是“rabbitmq_erlang”!!

        下载的包名类似这样:erlang-22.3-1.e17.x86_64.rpm

        检测erlang语言是否安装成功:erl -version

2.2 RabbitMQ在CentOS下的安装 part2

//导入密钥
rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc

//下载安装包
wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.8.2/rabbitmq-server-3.8.2-1.el7.noarch.rpm
//如果速度比较慢,就用:
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.2/rabbitmq-server-3.8.2-1.el7.noarch.rpm

//下载完成后,安装:
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就安装好了

RabbitMQ常用命令

启动RabbitMQ:

        systemctl start rabbitmq-server

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

        rabbitmqctl status 

开启web管理界面

        rabbitmq-plugins enable rabbitmq_management

停止RabbitMQ

        rabbitmqctl stop

设置开机启动

        systemctl enable rabbitmq-server 

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

        systemctl status rabbitmq-server

2.3 RabbitMQ在macOS 和 Windows下的安装

        上传在CSDN资源中。

三、RabbitMQ的应用

3.1 RabbitMQ的管理后台

开启web管理界面

        rabbitmq-plugins enable rabbitmq_management

创建用户名和密码

        rabbitmqctl add_user admin password

给用户设置权限

        rabbitmqctl set_user_tags admin administractor

访问浏览器进入管理页面

        http://127.0.0.1:15672

管理后台功能

        概览页面

        添加用户

        创建虚拟主机(Virtual Hosts)

3.2 第一个生产者

        1. IDEA新建Maven项目

        2. 引入依赖

  <dependencies>
    <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>
  </dependencies>

        3. 给RabbitMQ新建admin用户,把“/” Hosts的权限给admin用户一个。远端/代码 就可以用admin账户登录了。

        4. 代码如下:

/**
 * 描述:     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("114.55.219.216");
        factory.setUsername("admin");
        factory.setPassword("password");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //发布消息
        String message = "Hello World! 2";
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
        System.out.println("发送了消息:" + message);
        //关闭连接
        channel.close();
        connection.close();
    }
}

3.3 第一个消费者

        1. 代码如下:

/**
 * 描述:     接收消息,并打印,持续运行
 */
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("114.55.219.216");
        factory.setUsername("admin");
        factory.setPassword("password");
        //建立连接
        Connection connection = factory.newConnection();
        //获得信道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //接收消息并消费
        channel.basicConsume(QUEUE_NAME, true, 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);
            }
        });
    }
}

3.4 暂无

3.5 根据消息内容作处理

/**
 * 描述:     消费者,接收前面的批量消息
 */
public class Worker {

    private final static String TASK_QUEUE_NAME = "task_queue";

    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);

        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);
                }
            }
        });
    }

    private static void doWork(String task) {
        char[] chars = task.toCharArray();
        for (char ch : chars) {
            if (ch == '.') {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

3.6 多个消费者平均压力

多个消费者:

        循环调度(按顺序你一个他一个,一次分完。未考虑各个任务压力大小区别)

        公平派遣(根据消费者压力决定是不是派给你)

        消息确认(消费者告诉MQ我处理完了,来下一个吧)

        代码 加在上面代码块中

四、交换机类型

4.1 交换机类型 - fanout

交换机工作模式:

        fanout: 广播,这种模式只需要将队列绑定到交换机上即可,是不需要设置路由键的。只要是和此交换机做关联的queue,一律都会收到消息。

        direct: 根据RoutingKey匹配 消息 路由到指定的队列。

        topic: 生产者指定RoutingKey,消息 根据消费端指定的队列 通过模糊匹配的方式 进行相应转发。

        headers: 根据发送消息内容中的headers属性来匹配。

fanout模式代码:

/**
 * 描述:     发送日志信息
 */
public class EmitLog {

    private static final String EXCHANGE_NAME = "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.FANOUT);

        String message = "info: Hello World!";

        channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
        System.out.println("发送了消息:" + message);
        channel.close();
        connection.close();
    }
}


/**
 * 描述:     接收日志消息
 */
public class ReceiveLogs {
    private static final String EXCHANGE_NAME = "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.FANOUT);

        //非持久 会自动删除的队列
        String queueName = channel.queueDeclare().getQueue();
        channel.queueBind(queueName, EXCHANGE_NAME, "");

        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);
    }
}

4.2 暂无

4.3 交换机类型 - direct

/**
 * 描述:     direct类型的交换机,发送消息
 */
public class EmitLogDirect {

    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();
    }
}


/**
 * 描述:     接收3个等级的日志
 */
public class ReceiveLogsDirect1 {
    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);
    }
}


/**
 * 描述:     接收1个等级的日志
 */
public class ReceiveLogsDirect2 {
    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);
    }
}

 4.4 交换机类型 - topic生产者

topic模式:

        * 可以代替一个单词

        # 可以代替零个或多个单词

/**
 * 描述:     topic模式交换机,发送消息
 */
public class EmitLogTopic {

    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();
    }
}

4.5 交换机类型 - topic 消费者

/**
 * 描述:     特定路由键
 */
public class ReceiveLogsTopic1 {

    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();
        //重点 交换机给满足 类似正则规则的queue 发送消息,消费者再消费此queue中的消息
        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);
    }
}


/**
 * 描述:     特定路由键
 */
public class ReceiveLogsTopic2 {

    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();
        //重点 交换机给满足 类似正则规则的queue 发送消息,消费者再消费此queue中的消息
        String routingKey = "*.*.rabbit";
        channel.queueBind(queueName, EXCHANGE_NAME, routingKey);
        //重点 交换机给满足 类似正则规则的queue 发送消息,消费者再消费此queue中的消息
        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);
    }
}

五、SpringBoot与RabbitMQ的整合

5.1 SpringBoot整合RabbitMQ

Spring initiallzr -> 可选依赖 -> finished -> add as maven project

    //修改版本号为2.2.1
    <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

        //加入依赖
        <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
/**
 * 描述:     rabbitmq配置类
 */
@Configuration
public class TopicRabbitConfig {

    @Bean
    public Queue queue1() {
        return new Queue("queue1");
    }

    @Bean
    public Queue queue2() {
        return new Queue("queue2");
    }

    @Bean
    TopicExchange exchange() {
        return new TopicExchange("bootExchange");
    }

    @Bean
    Binding bingdingExchangeMessage1(Queue queue1, TopicExchange exchange) {
        return BindingBuilder.bind(queue1).to(exchange).with("dog.red");
    }

    @Bean
    Binding bingdingExchangeMessage2(Queue queue2, TopicExchange exchange) {
        return BindingBuilder.bind(queue2).to(exchange).with("dog.#");
    }
}


/**
 * 描述:     发送消息
 */
@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);
    }
}


@SpringBootTest
public class SpringBootRabbitmqProducerApplicationTests {

    @Autowired
    MsgSender msgSender;

    @Test
    public void send1() {
        msgSender.send1();
    }
    @Test
    public void send2(){
        msgSender.send2();
    }

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

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

六、总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

chengbo_eva

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

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

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

打赏作者

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

抵扣说明:

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

余额充值