【外行也能看懂的RabbitMQ系列(一)】—— RabbitMQ快速入门篇(内含丰富实例)

在这里插入图片描述

系列文章目录

准备篇 RabbitMQ安装文档
第一章 RabbitMQ快速入门篇
第二章 RabbitMQ的Web管理界面详解
第三章 RabbitMQ进阶篇之死信队列
第四章 RabbitMQ进阶篇之通过插件实现延迟队列



前言

恭喜所有看到本篇文章的小伙伴,成功解锁了RabbitMQ系列之快速入门的内容🎁通过本文,你将快速了解到什么是中间件?什么是RabbitMQ?RabbitMQ是如何工作的?最后小名将带大家通过一个实例来加深你对RabbitMQ的印象😁文章有些长,请大家耐心食用😄

在这里插入图片描述

问号比较少的小伙伴可以跳过前四个标题,从第五个标题开始阅读

一、什么是中间件

不知道有没有像小名一样,看到铺天盖地的定义的时候感到头昏脑胀的,所以小名就上网查了一下,发现有很多人都有同样的发问:“What is middleware exactly?”

小名在 stack overflow 上看到了一个易于理解的例子:
在这里插入图片描述
小名用红框圈住的那段话翻译一下:

“你会注意到,这与操作系统的定义基本相同。因此,举例来说,TCP/IP协议栈或缓存可以被认为是中间件。但你的操作系统也可以提供同样的功能。事实上,中间件可以被认为是操作系统的一个特殊扩展,专门用于依赖它的一组应用程序。它只是提供了一种更高层次的服务。”

小名的理解是:把中间件当作一种规则,或者说中间件是遵守TCP/IP协议的搬运工。

三大基础中间件分别为:交易中间件、消息中间件和应用服务器中间件

二、什么是消息中间件

带着灵魂提问,小名又去网上搜索了一番关于“What is message-oriented middleware?”的一些回答,发现下面这个回复,比较好理解:
在这里插入图片描述
小名用红框圈住的那段话翻译一下:

消息代理 - 是任何处理消息的系统(在MOM中),或者更准确地说,是将消息路由到特定的消费者/接收者。消息代理通常建立在MOM的基础上。MOM提供应用程序之间的基本通信,以及像消息持久性和保证交付这样的东西。"消息代理是面向消息的中间件的一个组成部分。”

简而言之,在 “生产者”(消息发送者) 与 “消费者”(消息接收者) 之间架起一座可靠的信息传递桥梁

三、什么是MQ

上文,咱们提到了“消息中间件”的概念,截图中也提到了“Rabbitmq - 一个消息代理;一个MOM实现;一个AMQP的开源实现”,接下来,咱们来了解一下什么是MQ:

MQ全称为Message Queue,消息队列是应用程序和应用程序之间“低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能”的通信方法。

四、为什么要用MQ

1. 系统解耦: 比如我们原来的需求是向购买课程者发送邮件和系统中的消息,但是需求突然变更,觉得,老师也要接到开课的提醒,其实消息主体内容是相同,只是提示的文案不太一样时,若是用代码解决,我们就需要写接口,定义一些东西告诉app端,但若是通过MQ发送消息,我们就不需要添加新的接口,把消息推送给app的老师端,app修改一下文案即可。
2. 异步调用: 比如有一个人希望注册为一位老师,但是后台需要做一些类似查询数据库他是否有资格,是不是黑名单中的用户,做完这些,开始为他插入一条老师的数据,还可能有一些需要创建的关系数据,最后再发送消息提示用户注册成功,未免太慢了一些,其实我们完全可以在确认他可以注册老师后,插入数据库之前,将数据插入mq,发送消息给用户,这样老师收到到通知就可以去做一些类似创建课程的后续操作了
3. 流量削峰: 比如秒杀场景,远远超过了平时服务器所能处理能力的上限,假设平时一次可以处理1000条数据,此时突然在极短的时间内一起涌入了100000条数据,这就很有可能使得服务器宕机。若是在服务器前,挡上一层MQ,MQ就可以延缓请求,减小服务器的压力,不至于宕机。

五、MQ中的四个关键词

  • 生产者(Producer): 业务的发起者,创建消息并发送给消息服务器(Broker)
  • 消费者(Consumer): 业务的处理者,从消息服务器(Broker)获取消息,并进行后续的业务逻辑处理
  • 交换机(Exchange): 根据路由键将消息绑定到对应的队列上
  • 队列(Queue): 保存消息,并将消息转发给消费者

六、什么是AMQP

AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是一个进程间可靠、通用的传递异步消息的网络协议,就类似于SMTP、HTTP、FTP等是一种协议,它是专门用来统一消息服务的一种协议,为消息中间件设计的,且不受开发语言的限制。

七、 RabbitMQ工作原理

在这里插入图片描述

  1. Producer: 消息生产者,投递消息。
  2. Connection: 每次访问 RabbitMQ 都建立一个 Connection,将 publisher、consumer 和 broker 连接起来
  3. Channel: Channel 在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的 channel 进行通讯,且 channel 之间是完全隔离的(Channel 作为轻量级的Connection 极大减少了操作系统建立 TCP connection 的开销)
  4. Broker: 接收和分发消息的应用。
  5. Exchange: message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到 queue 中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout(multicast)。

八、RabbitMQ三种Exchange重要模式

  1. Fanout模式
    官方介绍:
    Publish/Subscribe:Sending messages to many consumers at once.
    在这里插入图片描述
    Fanout—发布与订阅模式,是一种广播机制,它是没有路由key的模式,只要将队列绑定到交换机上,则该交换机接收到的消息都会被转发到与该交换机绑定的所有队列上。所以,Fanout交换机转发消息是最快的。

  2. Direct模式
    官方介绍:
    Routing:Receiving messages selectively
    在这里插入图片描述
    Direct—路由模式,是基于fanout模式上的一种叠加,增加了路由routingKey,即:所有发送到 Direct Exchange 的消息会被转发到 routingKey 中指定的 Queue,所以在消息传递时需要注意的是,routingKey必须完全匹配才会被队列接收,否则该消息会被抛弃。

  3. Topics模式
    官方介绍:
    Topics:Receiving messages based on a pattern (topics)
    在这里插入图片描述
    Topic模式是基于direct模式上的一种叠加,增加了模糊路由routingKey,即:可以使用通配符进行模糊匹配。
    ×××.# : 匹配0或1级又或多级
    ×××.* : 匹配有且只能有1级
    这里没有看懂这个通配符的使用方式的小伙伴,请继续向下看“十一. RabbitMQ的管理界面详解”

九、消息生产者

  1. 如下图创建项目
    在这里插入图片描述

  2. 在pom.xml中引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 在application.yml进行配置
# 服务端口
server:
  port: 9000
# 配置rabbitmq服务
spring:
  rabbitmq:
    host: 你的rabbitmq公网地址
    port: 5672
    username: 用户名
   password: 密码
   publisher-confirms: true
   publisher-returns: true
  1. 定义生产者
  • Order.java
public class Order {
    private String orderId;
    private Long uid;
    private String name;

    public Order() {

    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Order(String orderId, Long uid, String name) {
        this.orderId = orderId;
        this.uid = uid;
        this.name = name;
    }

    public Long getUid() {
        return uid;
    }

    public void setUid(Long uid) {
        this.uid = uid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
  • OrderService.java
@Component
public class OrderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void makeOrderFanout(Long userId, Long productId, int num) {
        // 1. 模拟用户下单
        String orderNumer = UUID.randomUUID().toString();

        // 2. 下单完成以后
        System.out.println("用户 " + userId + ",订单编号是:" + orderNumer);

        // 3. 通过MQ来完成消息的分发
        //参数1: 交换机; 参数2:路由Key/queue队列名称; 参数3:消息内容
        String exchangeName = "fanout_order_exchange";
        String routeKey = "";

        // 发送订单信息给RabbitMQ 
        rabbitTemplate.convertAndSend(exchangeName, routeKey, orderNumer);
    }

    public void makeOrderDirect(Long userId, Long productId, int num) {
        // 1. 模拟用户下单
        String orderNumer = UUID.randomUUID().toString();

        // 2. 下单完成以后
        System.out.println("用户 " + userId + ",订单编号是:" + orderNumer);

        // 3. 通过MQ来完成消息的分发
        //参数1: 交换机; 参数2:路由Key/queue队列名称; 参数3:消息内容
        String exchangeName = "direct_order_exchange";
        String routeKey1 = "email";
        String routeKey2 = "sms";

        // 发送订单信息给RabbitMQ
        rabbitTemplate.convertAndSend(exchangeName, routeKey1, orderNumer);
        rabbitTemplate.convertAndSend(exchangeName, routeKey2, orderNumer);
    }

    public void makeOrderTopic(Long userId, Long productId, int num) {
        // 1. 模拟用户下单
        String orderNumer = UUID.randomUUID().toString();

        // 2. 下单完成以后
        System.out.println("用户 " + userId + ",订单编号是:" + orderNumer);

        // 3. 通过MQ来完成消息的分发
        //参数1: 交换机; 参数2:路由Key/queue队列名称; 参数3:消息内容
        String exchangeName = "topic_order_exchange";
        String routeKey = "sms.email.duanxin";

        // 发送订单信息给RabbitMQ topic
        rabbitTemplate.convertAndSend(exchangeName, routeKey, orderNumer);
    }
 }   

其中:

public void convertAndSend(String exchange, String routingKey, Object object) throws AmqpException {
    this.convertAndSend(exchange, routingKey, object, (CorrelationData)null);
}

通过 convertAndSend 方法指定发送的交换机、路由Key以及消息体,当然,fanout模式就无需设置路由Key了。

  1. 编写测试类
  • ProducerApplicationTests.java
@SpringBootTest
class ProducerApplicationTests {
    @Autowired
    private OrderService orderService;

    @Test
    void contextLoads() {
        orderService.makeOrderFanout(1l, 1l, 12);
    }

    @Test
    void directTest() {
        orderService.makeOrderDirect(1l, 1l, 12);
    }

    @Test
    void topicTest() {
        orderService.makeOrderTopic(1l, 1l, 12);
    }
 }   

十、消息消费者

  1. 如下图创建项目
    在这里插入图片描述

  2. 在pom.xml中引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 在application.yml进行配置
# 服务端口
server:
  port: 9001
# 配置rabbitmq服务
spring:
  rabbitmq:
    host: 你的rabbitmq公网地址
    port: 5672
    username: 用户名
   password: 密码
   publisher-confirms: true
   publisher-returns: true
  1. 编写绑定关系相关代码
    注:小名将定关系定义在消费者,可以避免消费者运行报错
10.4.1.Direct模式:

DirectRabbitMqConfiguration.java

@Configuration
public class DirectRabbitMqConfiguration {
    //1. 声明注册direct模式的虚拟机
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange("direct_order_exchange", true, false);
    }

    //2. 声明队列
    @Bean
    public Queue directSmsQueue() {
        return new Queue("sms.direct.queue", true);
    }

    @Bean
    public Queue directEmailQueue() {
        return new Queue("email.direct.queue", true);
    }

    @Bean
    public Queue directDuanxinQueue() {
        return new Queue("duanxin.direct.queue", true);
    }

    //3. 完成绑定关系
    @Bean
    public Binding directSmsBinding() {
        return BindingBuilder.bind(directSmsQueue()).to(directExchange()).with("sms");
    }

    @Bean
    public Binding directEmailBinding() {
        return BindingBuilder.bind(directEmailQueue()).to(directExchange()).with("email");
    }

    @Bean
    public Binding directDuanxinBinding() {
        return BindingBuilder.bind(directDuanxinQueue()).to(directExchange()).with("duanxin");
    }
}
附加知识点1:

1).

new DirectExchange((String name, boolean durable, boolean autoDelete);

name: 交换器名字

durable: 是否持久化
rabbitMQ默认将消息存储在内存中,若rabbitMQ宕机,重启rabbitmq,消息会丢失,如果想重启之后还存在就要使队列持久化(设置durable属性为true),保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库。

autoDelete: 是否自动删除
当autoDelete 属性被设置为true时,那么,当消息接收者宕机,关闭后,消息队列则会删除,消息发送者一直发送消息,当消息接收者重新启动恢复正常后,会接收最新的消息,而宕机期间的消息则会丢弃。

当autoDelete 属性被设置为false时,那么,当消息接收者宕机,关闭后,消息队列不会删除,消息发送者一直发送消息,当消息接收者重新启动恢复正常后,宕机期间的消息和新消息都会接收。

2).

new Queue(String name, boolean durable)

name: 队列名字
durable: 持久化,同上
3).

BindingBuilder.bind(directSmsQueue()).to(directExchange()).with("sms")

将RoutingExChange与队列”sms.direct.queue“绑定,设定routingKey为sms

10.4.2.Fanout模式:

FanoutRabbitMqConfiguration.java

@Configuration
public class FanoutRabbitMqConfiguration {
    //1. 声明注册fanout模式的虚拟机
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanout_order_exchange", true, false);
    }

    //2. 声明队列
    @Bean
    public Queue smsQueue() {
        return new Queue("sms.fanout.queue", true);
    }

    @Bean
    public Queue emailQueue() {
        return new Queue("email.fanout.queue", true);
    }

    @Bean
    public Queue duanxinQueue() {
        return new Queue("duanxin.fanout.queue", true);
    }

    //3. 完成绑定关系
    @Bean
    public Binding smsBinding() {
        return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
    }

    @Bean
    public Binding emailBinding() {
        return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
    }

    @Bean
    public Binding duanxinBinding() {
        return BindingBuilder.bind(duanxinQueue()).to(fanoutExchange());
    }
}
附加知识点2:
BindingBuilder.bind(duanxinQueue()).to(fanoutExchange())

细心的小伙伴会发现,fount与direct模式在绑定处有所不同,fount模式缺少了”.with(“sms”)“,这是因为,咱们上面说到过:发布与订阅模式,是一种广播机制,它是没有路由key的模式,只要将队列绑定到交换机上,则该交换机接收到的消息会被转发到所有与之绑定的队列上。

10.4.3.Topic模式:

TopicRabbitMqConfiguration.java

@Configuration
public class TopicRabbitMqConfiguration {
    //1. 声明注册topic模式的虚拟机
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange("topic_order_exchange", true, false);
    }

    //2. 声明队列 sms.topic.queue email.topic.queue duanxin.topic.queue
    @Bean
    public Queue topicSmsQueue() {
        return new Queue("sms.topic.queue", true);
    }

    @Bean
    public Queue topicEmailQueue() {
        return new Queue("email.topic.queue", true);
    }

    @Bean
    public Queue topicDuanxinQueue() {
        return new Queue("duanxin.topic.queue", true);
    }

    //3. 完成绑定关系
    @Bean
    public Binding topicSmsBinding() {
        return BindingBuilder.bind(topicSmsQueue()).to(topicExchange()).with("sms.#");
    }

    @Bean
    public Binding topicEmailBinding() {
        return BindingBuilder.bind(topicEmailQueue()).to(topicExchange()).with("*.email.#");
    }

    @Bean
    public Binding topicDuanxinBinding() {
        return BindingBuilder.bind(topicDuanxinQueue()).to(topicExchange()).with("#.duanxin.#");
    }
附加知识点3:
BindingBuilder.bind(topicEmailQueue()).to(topicExchange()).with("*.email.#");

细心的小伙伴会发现,topic模式,在绑定路由key的时候,咱们需要设定相应的通配符。
×××.# : 匹配0或1级又或多级
×××. * : 匹配有且只能有1级
拿上面的例子简单的解释一下:
若路由Key设定为:" *.email.# "
那么,当生产者发送消息给以下队列时,它们可以接收到消息
test.email.ittest.email.it.computertest.email
但下述队列,就不能正常接收到消息了
test1.test2.email.ittest1.test2.email.it.computertest1.test2.email
这里只是简单的举个例子,不懂的小伙伴请继续向下看“十一. RabbitMQ的管理界面详解”

  1. 三种模式的消费者代码实现
10.5.1. Direct模式消费者代码:

DirectDuanxinConsumer.java

@Service
public class DirectDuanxinConsumer {
    @RabbitListener(queues = {"duanxin.direct.queue"})
    public void reviceMessage(String message) {
        System.out.println("duanxin direct---接收到了订单信息是:->" + message);
    }
}

DirectEmailConsumer.java

@Service
public class DirectEmailConsumer {
    @RabbitListener(queues = {"email.direct.queue"})
    public void reviceMessage(String message) {
        System.out.println("email direct---接收到了订单信息是:->" + message);
    }
}

DirectSmsConsumer.java

@Service
public class DirectSmsConsumer {
    @RabbitListener(queues = {"sms.direct.queue"})
    public void reviceMessage(String message) {
        System.out.println("sms direct---接收到了订单信息是:->" + message);
    }
}
10.5.2. Fanout模式消费者代码:

FanoutDuanxinConsumer.java

@Service
public class FanoutDuanxinConsumer {
    @RabbitListener(queues = {"duanxin.fanout.queue"})
    public void reviceMessage(String message) {
        System.out.println("duanxin fanout---接收到了订单信息是:->" + message);
    }
}

FanoutEmailConsumer.java

@Service
public class FanoutEmailConsumer {
    @RabbitListener(queues = {"email.fanout.queue"})
    public void reviceMessage(String message) {
        System.out.println("email fanout---接收到了订单信息是:->" + message);
    }
}

FanoutSmsConsumer.java

@Service
public class FanoutSmsConsumer {
    @RabbitListener(queues = {"sms.fanout.queue"})
    public void reviceMessage(String message) {
        System.out.println("sms fanout---接收到了订单信息是:->" + message);
    }
}
10.5.3. Topic模式消费者代码:

TopicDuanxinConsumer.java

@Service
public class TopicDuanxinConsumer {
    @RabbitListener(queues = {"duanxin.topic.queue"})
    public void reviceMessage(String message) {
        System.out.println("duanxin topic---接收到了订单信息是:->" + message);
    }
}

TopicEmailConsumer.java

@Service
public class TopicEmailConsumer {
    @RabbitListener(queues = {"email.topic.queue"})
    public void reviceMessage(String message) {
        System.out.println("email topic---接收到了订单信息是:->" + message);
    }
}

TopicSmsConsumer.java

@Service
public class TopicSmsConsumer {
    @RabbitListener(queues = {"sms.topic.queue"})
    public void reviceMessage(String message) {
        System.out.println("sms topic---接收到了订单信息是:->" + message);
    }
}
  1. 三种模式的运行效果
    1.分别运行我们在”生产者“中三个模式的测试类的测试代码(上文 ”9.5 编写测试类“)
    2.查看消费者的控制台输出消息
- Fount模式运行结果:

生产者:
在这里插入图片描述

消费者:
在这里插入图片描述

- Direct模式运行结果:

生产者:
在这里插入图片描述

消费者:
在这里插入图片描述

- Topic模式运行结果:

生产者:
在这里插入图片描述
消费者:
在这里插入图片描述

十一、RabbitMQ的管理界面详解

非常感谢小伙伴们耐心的读到这里,由于篇幅较长读起来可能有些疲惫,下面的内容,小名会在下一篇文章中继续与大家分享。🎉感谢大家的支持🎉

11.1 进入登陆界面

11.2 概要界面

11.3 连接界面

11.4 通道界面

11.5 交换机页面

11.6 队列界面

11.7 Admin界面

11.8 Topic模式实例实操


如若您在文章中发现任何错误的地方,希望您可以在评论区给予小名批评指正🤝 如果觉得小名的文章帮助到了您,请关注小名的专栏【RabbitMQ】,支持一下小名😄,给小名的文章点赞👍、评论✍、收藏🤞谢谢大家啦~♥♥♥
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

进阶的小名

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

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

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

打赏作者

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

抵扣说明:

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

余额充值