修改时间:2020年3月28日
作者:pp_x
邮箱:pp_x12138@163.com
文章目录
消息队列MQ(MessageQueue)
什么是消息队列
-
消息队列,一般我们会简称它为MQ(Message Queue),消息队列是一种帮助开发人员解决系统间异步通信的中间件,常用于解决系统解耦和请求的削峰平谷的问题。
-
队列(Queue):
- 是一种先进先出的容器
- 是一种先进先出的容器
-
消息(Message)
- 不同应用之间传输的数据。
-
消息队列:我们可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用。消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系统性能和削峰、降低系统耦合性。队列 Queue 是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的。
-
传统方式
-
MQ方式
消息队列的应用场景
- 应用耦合:多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败
- 异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间
- 限流削峰:广泛应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况
- 消息驱动的系统:系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息,消费者(可能有多个)负责对消息进行处理
异步处理
- 具体场景:用户为了使用某个应用,进行注册,系统需要发送注册邮件并验证短信。对这两个子系统操作的处理方式有两种:串行及并行。涉及到三个子系统:注册系统、邮件系统、短信系统。
- 串行方式:需要新注册信息生成后,先发送注册邮件,再发送验证短信;在这种方式下,需要最终发送验证短信后再返回给客户端(50+50+50)
- 并行处理:新注册信息写入后,由发短信和发邮件并行处处理;在这种方式下,发短信和发邮件 需处理完成后再返回给客户端。(50+50)
- 消息队列方式:用户注册信息写入后将消息发送给消息队列,由于写入消息队列的时间很短可以忽略,所以主要事件在于信息写入。(50s)
应用解耦
- 具体场景
- 用户使用QQ相册上传一张图片,人脸识别系统会对该图片进行人脸识别
- 一般的做法是,服务器接收到图片后,图片上传系统立即调用人脸识别系统,调用完成后再返回成功,如下图所示:
- 调用方式:webService、Http协议(HttpClient、RestTemplate)、Tcp协议(Dubbo)
- 该方法有如下缺点:
- 人脸识别系统被调失败,导致图片上传失败;
- 延迟高,需要人脸识别系统处理完成后,再返回给客户端,即使用户并不需要立即知道结果
- 图片上传系统与人脸识别系统之间互相调用,需要做耦合;
- 若用消息队列
- 客户端上传图片后,图片上传系统将图片信息批次写入消息队列,直接返回成功;
- 人脸识别系统则定时从消息队列中取数据,完成对新增图片的识别
- 图片上传系统并不需要关心人脸识别系统是否对这些图片信息的处理、以及何时对这些图片信息进行处理
- 事实上,由于用户并不需要立即知道人脸识别结果,人脸识别系统可以选择不同的调度策略,照闲时、忙时、正常时间,对队列中的图片信息进行处理
限流削峰
- 具体场景:
- 购物网站开展秒杀活动,一般由于瞬时访问量过大,服务器接收过大,会导致流量暴增,相关系统无法处理请求甚至崩溃
- 而加入消息队列后,系统可以从消息队列中取数据,相当于消息队列做了一次缓冲
- 该方法如下优点:
- 请求先进入消息队列,而不是由业务直接处理,做了一次缓冲,极大的减少了业务的处理系统压力
- 如果限额,完全可以在队列中设置最大消息数,达到规定数量后,后续的请求直接按秒杀失败处理
消息事件驱动的系统
- 具体场景:
- 用户新上传了一批照片 ---->人脸识别系统需要对这个用户的所有照片进行聚类 -------> 由对账系统重新生成用户的人脸索引(加快查询)
- 这三个子系统间由消息队列连接起来,前一个阶段的处理结果放入队列中,后一个阶段从队列获取消息继续处理
- 该方法有如下优点
- 避免了直接调用下一个系统导致当前系统失败
- 每个子系统对于消息的处理方式可以更为灵活,可以选择收到消息时就处理,可以选择定时处理,也可以划分时间段按不同处理速度处理;
消息队列的两种模式
- 消息队列包括两种模式,点对点模式(point to point, queue)和发布/订阅模式(publish/subscribe,topic)
点对点模式
- 点对点模式下包括三个角色:
消息队列
,发送者 (生产者)
,接收者(消费者)
- 每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,可以放在内存中也可以持久化,直到他们被消费或超时
点对点模式特点
- 每个消息只有一个消费者,一旦被消费,消息就不再在消息队列中;
- 发送者和接收者间没有依赖性,发送者发送消息之后,不管有没有接收者在运行,都不会影响到发送者下次发送消息
- 接收者在成功接收消息之后需向队列应答成功,以便消息队列删除当前接收的消息
发布订阅模式
- 发布/订阅模式下包括三个角色:
角色主题(topic)
,发布者(Publisher)
,订阅者(Subscriber)
- 消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被多个订阅者消费。
发布/订阅模式特点
- 每个消息可以有多个订阅者;
- 发布者和订阅者之间有时间上的依赖性
- 为了消费消息,订阅者必须保持在线运行
消息队列实现机制
JMS
- JMS(JAVA Message Service,Java消息服务)是一个Java平台中关于面向消息中间件的API
- 允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息
- 是一个消息服务的标准或者说是规范,是 Java 平台上有关面向消息中间件的技术规范
- 便于消息系统中的 Java 应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发
- JMS 消息机制主要分为两种模型:PTP 模型和 Pub/Sub 模型
- 实现产品:
Apache ActiveMQ
AMQP
- AMQP,即
Advanced Message Queuing Protocol
,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有RabbitMQ等。
JMS VS AMQP
JMS | AMQP | |
---|---|---|
定义 | Java api | Wire-protocol |
跨语言 | 否 | 是 |
跨平台 | 否 | 是 |
Model | 提供两种消息模型/模式:Peer-2-Peer 、Pub/sub | 提供了五种消息模型:direct exchange 、fanoutexchange 、topic change 、headers exchange 、systemexchange ,本质来讲,后四种和JMS的pub/sub 模型没有太大差别,仅是在路由机制上做了更详细的划分; |
支持消息类型 | 多种消息类型:TextMessage 、MapMessage 、BytesMessage 、StreamMessage 、ObjectMessage 、Message (只有消息头和属性) | byte[] 当实际应用时,有复杂的消息,可以将消息序列化后发送。 |
综合评价 | JMS 定义了JAVA API层面的标准;在java体系中,多个client均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差; | AMQP定义了wire-level层的协议标准;天然具有跨平台、跨语言特性 |
常见的消息队列产品
- RabbitMQ:2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
- ActiveMQ:是由Apache出品,ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能
- RocketMQ:出自阿里公司的开源产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一些改进,消息可靠性上比 Kafka 更好。RocketMQ在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理等
- Kafka:是一个分布式消息发布订阅系统。它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log),之后成为Apache项目的一部分。Kafka系统快速、可扩展并且可持久化。它的分区特性,可复制和可容错都是其不错的特性。
Kafka基本介绍
- 官网:http://kafka.apache.org/
- Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目
- 主要应用场景是:日志收集系统和消息系统。
- Kafka主要设计目标如下:
- **以时间复杂度为O(1)**的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间的访问性能
- 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输
- 支持普通服务器每秒百万级写入请求
- Memory mapped Files(内存映射文件)
- 支持Kafka Server间的消息分区,及分布式消费,同时保证每个partition内的消息顺序传输
- 同时支持离线数据处理和实时数据处理。
- Scale out:支持在线水平扩展
kafka的特点
- 解耦。Kafka具备消息系统的优点,只要生产者和消费者数据两端遵循接口约束,就可以自行扩展或修改数据处理的业务过程
- 高吞吐量、低延迟。即使在非常廉价的机器上,Kafka 也能做到每秒处理几十万条消息,而它的延迟最低只有几毫秒。
- 持久性。Kafka 可以将消息直接持久化在普通磁盘上,且磁盘读写性能优异。
- 扩展性。Kafka 集群支持热扩展,Kafka 集群启动运行后,用户可以直接向集群添加。
- 容错性。Kafka 会将数据备份到多台服务器节点中,即使 Kafka 集群中的某一台 Kafka 服务节点宕机,也不会影响整个系统的功能。
- 支持多种客户端语言。Kafka 支持 Java、.NET、PHP、Python 等多种语言
- 支持多生产者和多消费者
Kafka 的主要应用场景
- 消息处理
- KafKa 可以代替传统的消息队列软件,使用 KafKa 来实现队列有如下优点:
- KafKa 的 append 来实现消息的追加,保证消息都是有序的有先来后到的顺序
- 稳定性强,队列在使用中最怕丢失数据,KafKa 能做到理论上的写成功不丢失。
- 分布式容灾好(有分区和副本)。
- 容量大相对于内存队列,KafKa 的容量受硬盘影响。
- 数据量不会影响到 KafKa 的速度。
- 分布式日志系统 (Log):
- 在很多时候需要对一些庞大的数据进行存留,日志存储这块会遇到巨大的问题,日志不能丢,日志存文件不好找,定位一条消息成本高(遍历当天日志文件),实时显示给用户难,这几类问题 KafKa 都能游刃有余
- KafKa 的集群备份机制能做到 n/2 的可用,当 n/2 以下的机器宕机时存储的日志不会丢失
- KafKa 可以对消息进行分组分片
- KafKa 非常容易做到实时日志查询
- 流式处理:流式处理就是指实时地处理一个或多个事件流。
- 流式的处理框架 (spark、storm、flink) 从主题中读取数据,对其进行处理,并将处理后的结果数据写入新的主题,供用户和应用程序使用,Kafka 的强耐久性在流处理的上下文中也非常的有用。
Kafka架构
product --> 订单消息 --> kafka集群
Consumer 消费者<----- Kafka集群
Kafka集群:可以有很多个节点实例,每一个实例都是一个 Broker。
Producer,Kafka集群,Consumer ----> Zookeeper存储集群的元数据信息
- Kafka Cluster - 由多个服务器组成,每个服务器单独的名字 broker
- Kafka Broker - Kafka 集群中包含的服务器。
- Kafka Producer - 消息生产者,发布消息到 Kafka 集群的终端或服务。
- Kafka Consumer - 消息消费者,负责消费数据。
- Kafka Topic - 主题,一类消息的名称;存储数据时将一类数据存放在某个 Topic 下,消费数据也是消费一类数据。
- Kafka 的元数据都是存放在 Zookeeper 中
架构剖析
- Kafka 支持消息持久化,消费端为 pull 模型来拉取数据,消费状态和订阅关系有客户端负责维护,消息消费完后,不会立即删除,会保留历史消息。因此支持多订阅时,消息只会存储一份就可以了。
关系解释
- Topic & Partition 主题和分区:
- Topic 就是数据主题,是数据记录发布的地方,可以用来区分业务系统。
- Kafka 中的 Topics 总是多订阅者模式,一个 topic 可以拥有一个或者多个消费者来订阅它的数据。
- 一个 topic 为一类消息,每条消息必须指定一个 topic。
- 对于每一个 topic,Kafka 集群都会维持一个分区日志。
- 每个分区都是有序且顺序不可变的记录集,并且不断地追加到结构化的
commit log
文件。 - 分区中的每一个记录都会分配一个 id 号来表示顺序,称之为
offset
,offset 用来唯一的标识分区中每一条记录
- 在每一个消费者中唯一保存的元数据是offset(偏移量)即消费在log中的位置,偏移量由消费者所控制:通常在读取记录后,消费者会以线性的方式增加偏移量
- 但是实际上,由于这个位置由消费者控制,所以消费者可以采用任何顺序来消费记录。例如,一个消费者可以重置到一个旧的偏移量,从而重新处理过去的数据;也可以跳过最近的记录,从"现在"开始消费
- 这些细节说明Kafka 消费者是非常廉价的,消费者的增加和减少,对集群或者其他消费者没有多大的影响