WAHT ? 什么是MQ
生产消费模型
生产消费模型: 生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者把资料做成产品.
两种消息模型
(1) 队列模型
最初的一种消息模型:队列模型。
生产者(Producer)发消息就是入队操作,消费者(Consumer)收消息就是出队也就是删除操
作,服务端存放消息的容器自然就称为“队列”。
(2) 发布 - 订阅模型
如果需要将一份消息数据分发给多个消费者。
消息的发送方称为发布者(Publisher),消息的接收方称为订阅者(Subscriber),服务端存放消息的容器称为主题(Topic)。发布者将消息发送到主题中,订阅者在接收消息之前需要先“订阅主题”:
WHY ? 为什用MQ
什么时候需要消息队列?
- 异步处理:
例如短信通知、终端状态推送、App推送、用户注册等
有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
- 数据同步:业务数据推送同步
- 重试补偿:记账失败重试
- 系统解耦 降低工程间的强依赖程度,针对异构系统进行适配。在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束
- 流量消峰:秒杀场景下的下单处理
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果以为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
- 发布订阅:HSF的服务状态变化通知、分布式事件中心
- 数据流处理:日志服务、监控上报
- 分布式事务 类似两阶段提交。
存储架构选择
MQ 消息中间件的存储架构选择: 首先对比下常用的存储行中间件 Redis ,Mysql ,MQ :
说到存储系统,大家可能对 MySQL 比较熟悉,也知道 MySQL 是基于 B+ tree 来作为它的索引数据结构。redis 是基于 内存的 hash 结构。
Kafka 又是基于什么机制来存储?为什么要设计成这样?它解决了什么问题?
1, Redis
场景:热点数据缓存,分布式锁,排行榜,全局队列,计数器.....
- 写操作: 少,数据存储有限
- 读操作: 多,对性能要求很高 吗,几万的QPS
读多写少的一个高性能缓存系统。数据主要放在内存中。虽然也要同步RDB or AOF 。
2, Mysql
场景: 主要作为数据存储。
- 写操作:多,要求有大量的数据存储
- 读操作:相对少。 内存有限,几千QPS
3, MQ
使用场景: 如上介绍...
特点主要包括:
- 写操作:并发要求非常高,基本得达到百万级 TPS,顺序追加。
- 读操作:相对写来说,比较简单,按照规则高效查(offset或者时间戳)
写多,读少。
so .....
1,MQ 采用 Hash 方式 问题 :
哈希索引【底层基于Hash Table 实现】,为了提高读速度,我们只需要在内存中维护一个映射关系即可,每次根据 Offset 查询消息的时候,从哈希表中得到偏移量,
再去读文件就可以快速定位到要读的数据位置。但是哈希索引通常是需要常驻内存的,对于Kafka 每秒写入几百万消息数据来说,
是非常不现实的,很容易将内存撑爆,造成 oom 。
2,磁盘存储:
设想把消息的 Offset 设计成一个有序的字段,这样消息在日志文件中也就有序存放了,也不需要额外引入哈希表结构,
可以直接将消息划分成若干个块,对于每个块,我们只需要索引当前块的第一条消息的 Offset ,
这个是不是有点二分查找算法的意思。
这样就可以快速定位到要查找的消息的位置了,在 Kafka 中,我们将这种索引结构叫做 “稀疏索引”。
HOW? MQ 怎么做的
消息中间件的必要特性
1 消息可靠, 不丢消息;
2 支持集群,确保某个节点宕机任可用;
3 高性能,具备足够好的性能。
4 高扩展 。
市场上现有产品的特点对比:
1. RabbitMQ:
老牌儿消息队列 RabbitMQ, 开发语言:Erlang 。
特点:
RabbitMQ 一个比较有特色的功能是支持非常灵活的路由配置,和其他消息队列不同的是,
它在生产者(Producer)和队列(Queue)之间增加了一个[ Exchange ] 模块,你可以理解为交换机.路由规则灵活。可以自己定义。
客户端语言支持丰富。
问题:
1, 对消息堆积的支持并不好。 当大量消息积压,会导致 RabbitMQ 性能急剧下降
2, 性能是我们介绍的这几个消息队列中最差的. 大概每秒处理几万到十几万条消息。
3, 使用的编程语言 Erlang , 学习曲线非常陡峭。难度大。
2. RocketMQ:
RocketMQ 是阿里巴巴在 2012 年开源的消息队列产品,后来捐赠给 Apache 软件基金会,2017 正式毕业,成为 Apache 的顶级项目。
RocketMQ 就像一个品学兼优的好学生,有着不错的性能,稳定性和可靠性,具备一个现代的消息队列应该有的几乎全部功能和特性,并且它还在持续的成长中。
优点:
1 有非常活跃的中文社区,Java 语言开发,容易对 RocketMQ 进行扩展或者二次开发。
2 响应时延 做了很多的优化,大多数可以做到毫秒级的,
3 性能高 ,每秒钟处理约几十万条消息。
劣势:作为国产的消息队列,在国际上还没有那么流行,与周边生态系统的集成和兼容程度要略逊一筹。
3. Kafka:
Kafka 最早是由 LinkedIn 开发,目前也是 Apache 的顶级项目。Kafka 最初的设计目的是用于处理海量的日志。
早期的版本中,为了获得极致的性能,在设计方面做了很多的牺牲,比如不保证消息的可靠性,可能会丢失消息,也不支持集群,功能上也比较简陋。
但是,请注意,重点一般都在后面。随后的几年 Kafka 逐步补齐了这些。
Kafka 已经发展为一个非常成熟的消息队列产品,无论在 数据可靠性、稳定性 和 功能特性 等方面都可以满足绝大多数场景的需求。
优点
1 ,Kafka 与周边生态系统的兼容性是最好之一,尤其是大数据和流计算。
2,语言为 java 和 scala ,使用了异步和批量思想 ,做到高性能。尤其是异步收发。大约每秒钟可以处理几十万条消息
Kafka 的极限处理能力可以超过每秒 2000 万条消息。
不足
这种异步批量的设计带来的问题:
1,同步收发消息的响应时延比较高。
当客户端发送一条消息的时候,在它的 Broker 中,很多地方都会使用这种“先攒一波再一起处理”的设计。
4 Pulsar
Pulsar 是一个新兴的开源消息队列产品,最早是由 Yahoo 开发,目前处于成长期。
与其他消息队列最大的不同是,Pulsar 采用存储和计算分离的设计,有可能会引领未来消息队列的一个发展方向.
RocketMQ 与 ActiveMQ 与 Kafka请注意本文档由 RocketMQ 团队编写。虽然理想是对技术和特性进行无私的比较,但作者的专业知识和偏见显然有利于 RocketMQ。
下表是一个方便的快速参考,可让您一目了然地发现 RocketMQ 及其最受欢迎的替代方案之间的差异。
Motivation - Apache RocketMQ让您一目了然地发现 RocketMQ 及其最受欢迎的替代方案之间的差异。
消息产品
客户端SDK
协议和规范
有序消息
预定消息
批量消息
广播消息
消息过滤器
服务器触发重新投递
消息存储
消息追溯
消息优先级
高可用性和故障转移
消息跟踪
配置
管理和操作工具
ActiveMQ Java、.NET、C++ 等。 推送模型,支持OpenWire、STOMP、AMQP、MQTT、JMS 独占消费者或独占队列可以确保订购 支持的 不支持 支持的 支持的 不支持 使用 JDBC 和高性能日志支持非常快速的持久化,例如 levelDB、kahaDB 支持的 支持的 支持,取决于存储,如果使用 levelDB 则需要 ZooKeeper 服务器 不支持 默认配置为低级,用户需要优化配置参数 支持的 卡夫卡 Java、Scala 等 拉模式,支持TCP 确保分区内的消息排序 不支持 支持,带有异步生产者 不支持 支持,可以使用Kafka Streams过滤消息 不支持 高性能文件存储 支持的偏移指示 不支持 支持,需要 ZooKeeper 服务器 不支持 Kafka 使用键值对格式进行配置。这些值可以从文件或以编程方式提供。 支持,使用终端命令公开核心指标 RocketMQ Java、C++、Go 拉模型,支持TCP、JMS、OpenMessaging 确保消息的严格排序,并且可以优雅地横向扩展 支持的 支持,同步模式避免消息丢失 支持的 支持,基于 SQL92 的属性过滤器表达式 支持的 高性能和低延迟的文件存储 支持的时间戳和偏移量两个表示 不支持 受支持的主从模式,无需其他套件 支持的 开箱即用,用户只需要注意几个配置 支持的、丰富的 web 和终端命令来公开核心指标
> Kafka TODO
> RocketMq TODO
Kafka如何实现高性能IO?
1, 批量消息 减少频繁IO :
批量处理是一种非常有效的提升系统吞吐量的方法。
虽然它提供的 API 每次只能发送一条消息,但实际上,Kafka的客户端 SDK 在实现消息发送逻辑的时候,采用了异步批量发送的机制。当调用 send() 发送一条消息后,无论同步发送,异步发送,Kafka 都不会立即把这条消息发出去。而是存放在内存中缓存起来,然后选择合适的时机组成一批,一次性发给 Broker。
在消费时,消息同样是以批为单位进行传递的,Consumer 从 Broker 拉到一批消息后,在客户端把批消息解开,再一条一条交给用户代码处理。
2, 顺序读写 提升磁盘 IO 性能
相比于网络传输和内存,磁盘 IO 的速度是比较慢的。对于消息队列的服务端来说,性能的瓶颈主要在磁盘 IO 这一块 。
顺序读写相比随机读写省去了大部分的寻址时间,它只要寻址一次,就可以连续地读写下去。
Kafka 就是充分利用了磁盘的这个特性。它的存储设计非常简单,对于每个分区,它把从
Producer 收到的消息,顺序地写入对应的 log 文件中,一个文件写满了,就开启一个新的文件这样顺序写下去。
消费的时候,也是从某个全局的位置开始,也就是某一个 log 文件中的某个位置开始,顺序地把消息读出来。
3, PageCache 加速消息读写
无论使用什么语言,在调用系统的 API 读写文件的时候,并不会直接去读写磁盘上的文件,应用程序实际操作的都是 PageCache,也就是文件在内存中缓存的副本。应用程序在写入文件的时候,操作系统会先把数据写入到内存中的 PageCache,然后再一批一批地写到磁盘上。
4, 零拷贝技术
在正常系统执行的文件IO流程:
磁盘 -》系统内核空间pageCache -》 用户内存空间 -》Soket 缓存区
Kafka 使用零拷贝技术可以把这个复制次数减少一次,上面的 2、3 步骤两次复制合并成一次复制。直接从 PageCache 中把数据复制到 Socket 缓冲区中,更重要的是 ,DMA 控制器可以直接完成数据复制,不需要 CPU 参与,速度更快。
kafka 和 RocketMQ 相比差异:
1,RocketMQ 延迟消息、Kafka暂无、
2,注册中心 RocketMQ 使用 nameSev , Kafka 使用ZK 、
3,集群方式 R: Dledger 主从 , K : 主备。
4,RockertMQ 一个TOpic 下还有更细的区分 Tag、