今天给大家带来的的是关于RabbitMQ的养成攻略,RabbitMQ
中的 Rabbit
是兔子的意思,就是形容跑的和兔子一样快。是一款轻量级的,支持多种消息传递协议的高可用的消息队列。RabbitMQ 是由 Erlang
语言编写的,而 Erlang 语言就是一款天生适合高并发的语言。
是不是都对小兔很喜欢呢,可爱的小兔在我们工作中可是扮演者大哥大的角色,说它是阿里现在的宠儿一点不为过了。RabbitMQ 前面小兔我介绍过了,那么MQ代表的是什么意思呢?其实了解的都知道,Message Queue
的简写,用官方的话说 RabbitMQ 是一款开源的消息队列系统。下面跟着小羽一起来看看这只小兔是如何养成的吧。
前言
现在市场上主流的MQ有很多,比如 ActiveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ
等。
阿里巴巴最初也是使用 ActiveMQ ,不过随着业务的不断发展,ActiveMQ IO 模块出现瓶颈,后来阿里巴巴通过一系列优化但是还是不能很好的解决,之后阿里巴巴把注意力放到了主流消息中间件 kafka
上面,但是 kafka 并不能满足他们的要求,尤其是低延迟和高可靠性。
所以 RocketMQ 是站在巨人的肩膀上(kafka),又对其进行了优化让其更满足互联网公司的特点。
RabbitMQ 作为一款非常流行的消息中间件,其有着非常丰富的特性和优势:高可靠性、路由灵活、集群扩张性高、高可用、支持多种协议、支持多种客户端和有着丰富的插件系统。
RocketMQ目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志流式处理、binglog分发等场景。
概念
RabbitMQ 是一个由 Erlang 语言开发的 AMQP
的开源实现。
AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。
RabbitMQ 最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
使用场景
应用解耦
拿我们经常说的订单系统来举例,一般都会订单系统调用库存系统的接口。如下:
这种处理方案会引发很多问题,当库存系统无法访问,我们订单系统的减库存就会失败。
当我们使用了消息队列后,用户下单后,订单系统进行持久化处理,将消息写入消息队列,返回用户订单下单成功;订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
这样的即使下单时库存系统出现问题,也不影响正常下单,因为下单后,订单系统已经写入消息队列,其他操作就不关心了,实现订单系统与库存系统的应用解耦。
异步处理
拿我们的用户注册来举例,用户注册之后需要发注册邮件和注册短信。如下:
串行模式:
并行模式:
当我们使用了消息队列,就不再是必须的业务逻辑,只需进行异步处理。如下:
流量削峰
拿我们经常提到的秒杀来举例,想必大家都经历过天猫淘宝的双11吧,当这个时候,我们会在特定的那个时间,比如晚上凌晨0点的每秒需求会突然增大,如果不进行系统结构升级,是经不起这么多的请求的,直接会系统崩溃。
当我们使用了消息队列之后,在这个高峰期的时候,很多用户进来,比如每秒有 5
千个请求,我们只需要将这 5
千个请求放在消息队列里,系统每秒处理 2
千个请求,会从消息队列里拉取出对应的数量,每秒只会处理这 2
千个请求。这样在秒杀持续的这个时间段内,会有几十万或者更多的请求都放在消息队列里。因为毕竟秒杀只是会在短暂的那一段时间,等它过去之后,每秒可能就只有几十,几百个请求进入消息队列。但是系统还会按照每秒 2
千个请求的速度去处理。所以,秒杀结束,系统会把那些剩下的消息都消费掉。
主要特点
可靠性(Reliability):RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
灵活的路由(Flexible Routing):在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange
来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
消息集群(Clustering):多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker
。
高可用(Highly Available Queues):队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
多种协议(Multi-protocol):RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT
等等。
多语言客户端(Many Clients):RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby
等等。
管理界面(Management UI):RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面。
跟踪机制(Tracing):如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
插件机制(Plugin System):RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
架构模型
Message
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括 routing-key
(路由键)、priority
(相对于其他消息的优先权)、delivery-mode
(指出该消息可能需要持久性存储)等。
Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。
Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
Queue
队列,是RabbitMQ的内部对象,用于存储消息消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
Connection
网络连接,比如一个 TCP
连接。
Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的 TCP
连接内地虚拟连接,AMQP
命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP
都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP
连接。
Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。
Broker
表示消息队列服务器实体。
Exchange 类型
Exchange 分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers
。headers 匹配 AMQP
消息的 header
而不是路由键,此外 headers 交换器和direct 交换器完全一致,但性能差很多,目前几乎用不到了,所以直接看另外三种类型:
Direct 键分布
Direct:消息中的路由键(routing key)如果和 Binding 中的 binding key
一致,交换器就将消息发到对应的队列中。它是完全匹配、单播的模式。
我们以 routingKey=”error” 发送消息到 Exchange ,则消息会路由到 Queue1
(amqp.gen-S9b…,这是由RabbitMQ自动生成的Queue名称)和 Queue2(amqp.gen-Agl…);如果我们以 routingKey=”info”或routingKey=”warning” 来发送消息,则消息只会路由到 Queue2。如果我们以其他 routingKey 发送消息,则消息不会路由到这两个 Queue 中。
Fanout
Fanout:每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。
生产者 Publisher 发送到 Exchange 的所有消息都会路由到图中的两个 Queue
,并最终被两个消费者(Consumer1与Comsumer2)消费。
topic 交换器
topic 交换器:topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号 “#
” 和符号 “” 。# 匹配 0
个或多个单词,匹配不多不少一个单词。
我们 routingKey=”quick.orange.rabbit” 的消息会同时路由到Q1
与Q2
,routingKey=”lazy.orange.fox” 的消息会路由到Q1
,routingKey=”lazy.brown.fox” 的消息会路由到Q2
,routingKey=”lazy.pink.rabbit” 的消息会路由到Q2
(只会投递给Q2一次,虽然这个 routingKey 与Q2
的两个 bindingKey 都匹配);routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit” 的消息将会被丢弃,因为它们没有匹配任何 bindingKey
。
安装步骤
一般来说安装 RabbitMQ 之前要安装 Erlang
,可以去 Erlang 官网下载。接着去 RabbitMQ 官网下载安装包,之后解压缩即可。根据操作系统不同官网提供了相应的安装说明:Windows、Debian / Ubuntu、RPM-based Linux、Mac
下载地址
https://www.rabbitmq.com/releases/rabbitmq-server/
下载
wget https://www.rabbitmq.com/releases/rabbitmq-server/v3.6.15/rabbitmq-server-generic-unix-3.6.15.tar.xz
安装
tar -xvf rabbitmq-server-generic-unix-3.6.15.tar.xz
配置
vim /etc/profile
最后加上 export PATH=$PATH:/opt/rabbitmq/rabbitmq_server-3.6.15/sbin 根据自己的安装路径来
使修改生效
source /etc/profile
修改 hosts
vim/etc/hosts
启动
cd /opt/rabbitmq/rabbitmq_server-3.6.15/sbin/
./rabbitmq-server -detached 注意需root用户启动
查看是否启动成功
./rabbitmqctl status
出现这个就成功了,如果想远程访问
./rabbitmq-plugins enable rabbitmq_management
就可以访问了(非本机访问请关闭防火墙)
访问
Springboot 项目演示
springboot 集成 RabbitMQ 非常简单,如果只是简单的使用配置非常少, springboot 提供了 spring-boot-starter-amqp
对消息各种支持。
配置 pom
文件,主要是添加 spring-boot-starter-amqp 的支持
代码演示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置 application.properties
文件,配置 rabbitmq 的安装地址、端口以及账户信息
代码演示:
spring.application.name=spirng-boot-rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
配置队列
代码演示:
package com.zpc.rabbitmq
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration
@Configuration
public class RabbitConfig {
@Bean
public Queue queue() {
return new Queue("q_hello");
}
}
发送者
代码演示:
package com.zpc.rabbitmq
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component
import java.text.SimpleDateFormat;
import java.util.Date
@Component
public class HelloSender {
@Autowired
private AmqpTemplate rabbitTemplate
public void send() {
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());//24小时制
String context = "hello " + date;
System.out.println("Sender : " + context);
//简单对列的情况下routingKey即为Q名
this.rabbitTemplate.convertAndSend("q_hello", context);
}
}
接收者
代码演示:
package com.zpc.rabbitmq
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component
@Component
@RabbitListener(queues = "q_hello")
public class HelloReceiver
@RabbitHandler
public void process(String hello) {
System.out.println("Receiver : " + hello);
}
}
测试
代码演示:
package com.zpc.rabbitmq
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner
@RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMqHelloTest
@Autowired
private HelloSender helloSender
@Test
public void hello() throws Exception {
helloSender.send();
}
}
内建集群
RabbitMQ 最为出色的功能之一就是内建集群,这个功能设计的目的是允许消费者和生产者在节点崩溃的情况下继续运行,以及通过添加更多的节点来线性扩展消息通信吞吐量。RabbitMQ 内部利用 Erlang 提供的分布式通信框架 OTP
来满足上述需求,使客户端在失去一个 RabbitMQ 节点连接的情况下,还是能够重新连接到集群中的任何其他节点继续生产、消费消息。
RabbitMQ 集群中常用概念:
队列元数据:包括队列名称和它们的属性,比如是否可持久化,是否自动删除
交换器元数据:交换器名称、类型、属性
绑定元数据:内部是一张表格记录如何将消息路由到队列
vhost 元数据:为 vhost 内部的队列、交换器、绑定提供命名空间和安全属性
技术选型
在我们平时开发中对各种消息队列 MQ 选型的思考:
如果用户访问量在 ActiveMQ 的可承受范围内,而且确实主要是基于解耦和异步来用的,可以考虑 ActiveMQ
,也比较贴近 Java 工程师的使用习惯,但是ActiveMQ 现在停止维护了,同时ActiveMQ 并发不高,所以业务量一定的情况下可以考虑使用。
RabbitMQ 作为一个纯正血统的消息中间件,有着高级消息协议 AMQP
的完美结合,在消息中间件中地位无可取代,但是 erlang 语言阻止了我们去深入研究和掌控,对公司而言,底层技术无法控制,但是确实是开源的,有比较稳定的支持,活跃度也高。
对自己公司技术实力有绝对自信的,可以用 RocketMQ
,但是 RocketMQ 诞生比较晚,并且更新迭代很快,这个意味着在使用过程中有可能会遇到很多坑,所以如果你们公司 Java 技术不是很强,不推荐使用。
中小型公司,技术实力较为一般,技术挑战不是特别高,用 ActiveMQ、RabbitMQ
是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ
是很好的选择
如果是大数据领域的实时计算、日志采集等场景,用 Kafka
是业内标准的,绝对没问题,社区活跃度很高,几乎是全世界这个领域的事实性规范。
从性能上来看,使用文件系统的消息中间件(Kafka、RokcetMq)性能是最好的,所以基于文件系统存储的消息中间件是发展趋势。(从存储方式和效率来看文件系统>KV存储>关系型数据库)
最后
消息队列呢,其实都大同小异,用法基本都是一样的,只是各个开源消息队列的侧重点稍有不同,我们应该根据我们自己的项目需求来决定我们应该选取什么样的消息队列来为我们的项目服务,这个项目选型的工作一般都是研发前期,组长就已经决定好了用哪个来做,一般是轮不到我们来做的,但是面试的时候可能会考察相关知识,所以这几种消息队列我们都应该对其了解并能灵活使用。