七:MQ消息队列
1:介绍
消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。
2:为什么会产生消息队列?
1)不同进程(process)之间传递消息时,两个进程之间耦合程度过高,改动一个进程,引发必须修改另一个进程,为了隔离这两个进程,在两进程间抽离出一层(一个模块),所有两进程之间传递的消息,都必须通过消息队列来传递,单独修改某一个进程,不会影响另一个;
2)不同进程(process)之间传递消息时,为了实现标准化,将消息的格式规范化了,并且,某一个进程接受的消息太多,一下子无法处理完,并且也有先后顺序,必须对收到的消息进行排队,因此诞生了事实上的消息队列;
3:使用消息队列的好处?
3)系统解耦:项目开始时,无法确定最终需求,不同进程间,添加一层,实现解耦,方便今后的扩展。
4)消息缓存:系统中,不同进程处理消息速度不同,MQ,可以实现不同Process之间的缓冲,即,写入MQ的速度可以尽可能地快,而处理消息的速度可以适当调整(或快、或慢)。
5)提升系统可靠性:
冗余:Process崩溃之后,数据并不会丢失,因为MQ多采用put-get-delete模式,即,仅当确认message被完成处理 之后,才从MQ中移除message;
可恢复:MQ实现解耦,部分进程崩溃,不会拖累整个系统瘫痪,例,Process_B崩溃之后,Process_A仍可向MQ中添加 message,并等待Process_B恢复;
可伸缩:有较强的峰值处理能力,通常应用会有突发的访问流量上升情况,使用足够的硬件资源时刻待命,空闲时刻较 长,资源浪费,而消息队列却能够平滑峰值流量,缓解系统组件的峰值压力;(流量削峰)
6)提升系统可扩展性
调整模块:由于实现解耦,可以很容易调整,消息入队速率、消息处理速率、增加新的Process;
7)单次送达:保证MQ中一个message被处理一次,并且只被处理一次。本质:get获取一个message后,这一message即被预定,同一进程不会再次获取这一message;当且仅当进程处理完这一message后,MQ中会delete这个message。否则,过一段时间后,这一message自动解除被预订状态,进程能够重新预定这个message;
8)排序保证:即,满足队列的FIFO,先入先出策略;
9)异步通信:很多场景下,不会立即处理消息,这是,可以在MQ中存储message,并在某一时刻再进行处理。
10)数据流的阶段性能定位:获取用户某一操作的各个阶段(通过message来标识),捕获不同阶段的耗时,可用于定位系统瓶颈。
11)流量削峰:流量削峰一般在秒杀活动中应用广泛,秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。1.可以控制活动人数,超过此一定阀值的订单直接丢弃2.可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)
4:消息队列比较
消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致 性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
Kafka是linkedin开源的MQ系统,主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,0.8开始支持 复制,不支持事务,适合产生大量数据的互联网服务的数据收集业务。
RabbitMQ是使用Erlang语言开发的开源消息队列系统,基于AMQP协议来实现。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订 阅)、可靠性、安全。AMQP协议更多用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。
RocketMQ是阿里开源的消息中间件,它是纯Java开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点。RocketMQ思路起源于 Kafka,但并不是Kafka的一个Copy,它对消息的可靠传输及事务性做了优化,目前在阿里集团被广泛应用于交易、充值、流计算、消息推送、日志 流式处理、binglog分发等场景。
ZeroMQ只是一个网络编程的Pattern库,将常见的网络请求形式(分组管理,链接管理,发布订阅等)模式化、组件化,简而言之socket之 上、MQ之下。对于MQ来说,网络传输只是它的一部分,更多需要处理的是消息存储、路由、Broker服务发现和查找、事务、消费模式 (ack、重投等)、集群服务等。
RabbitMQ/Kafka/ZeroMQ 都能提供消息队列服务,但有很大的区别。 在面向服务架构中通过消息代理(比如 RabbitMQ / Kafka等),使用生产者-消费者模式在服务间进行异步通信是一种比较好的思想。 因为服务间依赖由强耦合变成了松耦合。消息代理都会提供持久化机制,在消费者负载高或者掉线的情况下会把消息保存起来,不会丢失。就是说生产者和消费者不需要同时在线,这是传统的请求-应答模式比较难做到的,需要一个中间件来专门做这件事。其次消息代理可 以根据消息本身做简单的路由策略,消费者可以根据这个来做负载均衡,业务分离等。缺点也有,就是需要额外搭建消息代理集群(但优点是大于缺点的 ) 。
ZeroMQ 和 RabbitMQ/Kafka 不同,它只是一个异步消息库,在套接字的基础上提供了类似于消息代理的机制。使用 ZeroMQ 的话,需要对自己的业务代码进行改造,不利于服务解耦。 RabbitMQ 支持 AMQP(二进制),STOMP(文本),MQTT(二进制),HTTP(里面包装其他协议)等协议。Kafka 使用自己的协议。 Kafka 自身服务和消费者都需要依赖 Zookeeper。 RabbitMQ 在有大量消息堆积的情况下性能会下降,Kafka不会。毕竟AMQP设计的初衷不是用来持久化海量消息的,而Kafka一开始是用来 处理海量日志的。 总的来说,RabbitMQ 和 Kafka 都是十分优秀的分布式的消息代理服务,只要合理部署,不作,基本上可以满足生产条件下的任何需求。
5:消息队列,容易丢失队列数据如何处理?
1)使用mongodb进行数据的备份,电商业务下订单时, 会将所有的订单先发送到消息队列中,供后续监听获得数据操作,为了防止订单数据丢失问题,使用mongodb数据库下订单时,发送到rabbitmq的同时,会将订单信息存入mongodb中备份,同时添加一个标识字段0:未对账,1:已对账。
2)另外设置一个springboot定时器,每月月底定时查阅mongodb数据库中一个月以前的数据信息,如果发现mongodb数据信息中有标识还为0:未对账的信息,会将该条信息重新加入到消息队列中进行处理。