什么是MQ
消息总线(Message Queue),是一种跨进程、异步的通信机制,用于上下游传递消息。由消息系统来确保消息的可靠传递。
MQ是干什么用的?
应用解耦、异步、流量削锋、数据分发、错峰流控、日志收集等等...
解耦举例:看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃......,所以通过发布订阅模式,谁需要谁订阅即可。
异步举例:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。 如果使用异步,A系统处理完自己的逻辑,直接返回给用户;然后发消息让BCD写库;用户收到请求只要3ms,体验流畅美滋滋。
流量削峰举例:每天 0:00 到 12:00,A 系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条。但是系统是直接基于 MySQL 的,大量的请求涌入 MySQL,每秒钟对 MySQL 执行约 5k 条 SQL。一般的 MySQL,扛到每秒 2k 个请求就差不多了,如果每秒请求到 5k 的话,可能就直接把 MySQL 给打死了,导致系统崩溃,用户也就没法再使用系统了。 这时可以通过MQ,把消息挤压在队列中,至少可以保证系统正常可用;然后按照系统正常处理速度写入数据,也不会丢失数据。
MQ的潜在缺点
- 系统可用性降低
系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用 - 系统复杂度提高
硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。 - 一致性问题
A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。
主流产品
RabbitMQ,Kafka,ActiveMQ,RocketMQ等等
综上,各种对比之后,有如下建议:
一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃;
后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;
不过现在确实越来越多的公司,会去用 RocketMQ,确实很不错(阿里出品),对自己公司技术实力有绝对自信的,推荐用 RocketMQ。
所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。
如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,何况几乎是全世界这个领域的事实性规范
RabbitMQ的优势:
可靠性(Reliablity):
使用了一些机制来保证可靠性,比如持久化、传输确认、发布确认。灵活的路由(Flexible Routing):
在消息进入队列之前,通过Exchange来路由消息。对于典型的路由功能,Rabbit已经提供了一些内置的Exchange来实现。针对更复杂的路由功能,可以将多个Exchange绑定在一起,也通过插件机制实现自己的Exchange。消息集群(Clustering):
多个RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。高可用(Highly Avaliable Queues):
队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。多种协议(Multi-protocol):
支持多种消息队列协议,如STOMP、MQTT等。多种语言客户端(Many Clients):
几乎支持所有常用语言,比如Java、.NET、Ruby等。管理界面(Management UI):
提供了易用的用户界面,使得用户可以监控和管理消息Broker的许多方面。跟踪机制(Tracing):
如果消息异常,RabbitMQ提供了消息的跟踪机制,使用者可以找出发生了什么。插件机制(Plugin System):
提供了许多插件,来从多方面进行扩展,也可以编辑自己的插件。
RabbitMQ的几种使用模式
1、生产与消费
生产端通过路由规则发送消息到不同queue,消费端根据queue名称消费消息。
RabbitMQ是向消费端推送消息,订阅关系和消费状态保存在服务端。
生产端发送一条消息通过路由投递到Queue,只有一个消费者能消费到。
举例:系统内部的点赞操作,和用户的点赞数据列表。 点赞操作执行完成后,如果还需要处理用户已点赞数据等一些列逻辑,会增加当前点赞接口耗时。所以只需要发消息出来,点赞接口先行返回点赞结果,供点赞数据收到消息后处理已点赞数据逻辑等。点赞服务可能也是集群,只要一个服务消费到这个点赞消息即可。如果存在多个服务均消费该数据,有可能会存在数据重复问题。
2、发布与订阅
当RabbitMQ需要支持多订阅时,发布者发送的消息通过路由同时写到多个Queue,不同订阅组消费此消息。
举例:棋谱负责处理feed流数据,记录feed的各种状态。小视频,创作中心都关心feed的状态,需要知道feed是否发生变化。那qipu就说 我把消息放到消息池里好了,你们谁需要新数据,通过订阅我,然后就可到池里拿了。后续新增随刻创作,也可以通过订阅的qipu的数据,来获取feed数据。
两者的主要区别:
消费模式:生产消费者是所有消费者抢占消息,订阅发布是所有订阅者共享消息。
(1)生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息;即对于每个消息只能被最多一个消费者拥有;
(2)发布者订阅者模式:发布者生产消息放到队列里,多个监听队列的消费者都会收到同一份消息;即正常情况下每个消费者收到的消息应该都是一样的。
发布订阅中EXchange有四种工作模式:fanout,direct,topic,headers(不常用)
发布与订阅要借助交换机(EXchange)的原理来实现
Exchange:消息交换机,他制定消息按什么规则,路由到哪个队列。
Queue:消息队列载体,每个消息都会被投入一个或多个队列。
Binding:绑定,他的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
1、fanout
每个发到fanout类型交换器的消息都会分到所有绑定的队列上去。
fanout交换器不处理该路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout类型转发消息是最快的。
2、direct
每个发到fanout类型交换器的消息都会分到所有绑定的队列上去。
消息中的路由键(routing key)如果和Binding中的binding key一致,交换器就将消息发到对应的队列中。路由键与队列名完全匹配。
3、topic
topic交换器通过模式匹配(可以理解按照一定规则为模糊匹配)分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。