常见的消息队列对比

详细对比见:https://note.dolyw.com/mq/00-MQ-Select.html#_6-%E5%AF%B9%E6%AF%94

仅截图部分优缺点对比。

Kafka VS Redis

参考自:https://juejin.cn/post/6960652072588935182

所谓用Redis实现消息队列,即:使用list结构。lpush左边插入数据,rpop右边取出数据即可。

使用Kafka而不使用Redis的几点原因:

消息持久化:redis是内存数据库,虽然有aof和rdb两种机制进行持久化,但这只是辅助手段,这两种手段都是不可靠的。当redis服务器宕机时一定会丢失一部分数据,这对于很多业务都是没法接受的。

热key性能问题:不论是用codis还是twemproxy这种集群方案,对某个队列的读写请求最终都会落到同一台redis实例上,并且无法通过扩容来解决问题。如果对某个list的并发读写非常高,就产生了无法解决的热key,严重可能导致系统崩溃。

没有确认机制:每当执行rpop消费一条数据,那条消息就被从list中永久删除了。如果消费者消费失败,这条消息也没法找回了。你可能说消费者可以在失败时把这条消息重新投递到进队列,但这太理想了,极端一点万一消费者进程直接崩了呢,比如被kill -9,panic,coredump…

不支持多订阅者:一条消息只能被一个消费者消费,rpop之后就没了。如果队列中存储的是应用的日志,对于同一条消息,监控系统需要消费它来进行可能的报警,BI系统需要消费它来绘制报表,链路追踪需要消费它来绘制调用关系……这种场景redis list就没办法支持了。

不支持二次消费:一条消息rpop之后就没了。如果消费者程序运行到一半发现代码有bug,修复之后想从头再消费一次就不行了。

Kafka为什么这么快?

参考自:https://blog.csdn.net/zl1zl2zl3/article/details/107963699
https://zhuanlan.zhihu.com/p/66482461

1、利用 Partition 实现并行处理

一个Topic下有多个Partition,每一个Partition可能位于不同的节点。因此Partition可以位于不同的机器,或者不同的磁盘。从而利用机器间并行处理或者磁盘间的并行处理。

2、顺序写磁盘

顺序写的资源消耗比随机写要小。
Kafka 中每个分区是一个有序的,不可变的消息序列,新的消息不断追加到 partition 的末尾,这个就是顺序写。

3、零拷贝技术

打个比方:COW机制(copy on write写时拷贝)就算零拷贝的一种。主要是避免了从磁盘拷贝数据,导致的性能损耗。

kafka中,所使用的是Memory Mapped Files技术。即将内存映射到磁盘的Page上。
Kafka提供了一个Flush参数,通过此控制内存数据保存的时机。如果Kafka写入到mmap之后就立即flush然后再返回Producer叫 同步 (sync);写入mmap之后立即返回Producer不调用flush叫异步 (async)。

因此数据相当于是:先存到内存上,然后再根据Flush同步到磁盘上。

4、批量数据压缩

主要是减轻了网络的负载。

如果每个消息都压缩,但是压缩率相对很低,所以Kafka使用了批量压缩,即将多个消息一起压缩而不是单个消息压缩
Kafka允许使用递归的消息集合,批量的消息可以通过压缩的形式传输并且在日志中也可以保持压缩格式,直到被消费者解压缩
Kafka支持多种压缩协议,包括Gzip和Snappy压缩协议

5、Page Cache(页缓存)

为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。这样做的好处有:

(1)避免Object消耗:如果是使用Java堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多。
(2)避免GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题。

Kafka的数据清理机制

概述

Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。清理策略针对的是过期的segment文件,而不是某条过期的数据。
可以单独针对某topic配置,也可针对kafka集群配置(config/server.properties)。
策略分三种:基于时间,基于日志文件大小,基于日志文件起始偏移量。为了避免在删除时阻塞读操作,采用了copy-on-write形式的实现。删除操作进行时,读取操作的二分查找功能实际是在一个静态的快照副本上进行的。

删除策略

当日志达到log.segment.bytes大小,会创建新的segment。当segment超过log.segment.bytes或保留时长达到log.retention.hours,就会被清理掉。达到清理条件的日志文件,进行“delete”标注,文件无法被索引到。log.segment.delete.delay.ms这个时间后,文件才会被真正的从文件系统中删除。

压缩

将数据压缩,只保留每个key最后一个版本的数据,offset可能不连续。
这种策略只适合特殊场景,比如消息的key是用户ID,消息体是用户的资料,通过这种压缩策略,整个消息集里就保存了所有用户最新的资料。压缩策略支持删除,当某个Key的最新版本的消息没有内容时,这个Key将被删除,这也符合以上逻辑。

Kafka的两种数据传递模式

点对点模式

概述

其中有三个角色:消息队列、生产者、消费者

生产者发送到队列中,消费者从其中获取消费。被消费后,队列中不再存储。消费者不可消费已被消费过的消息。

特点

每个消息只有一个接收者(Consumer)(即一旦被消费,消息就不再在消息队列中);
发送者和接收者间没有依赖性,发送者发送消息之后,不管有没有接收者在运行,都不会影响到发送者下次发送消息;
接收者在成功接收消息之后需向队列应答成功,以便消息队列删除当前接收的消息;

发布/订阅模式

概述

其中有三个角色:主题、生产者、消费者

发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

特点

每个消息可以有多个订阅者;发布者和订阅者之间有时间上的依赖性。
针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
为了消费消息,订阅者需要提前订阅该角色主题,并保持在线运行;

Kafka的三种ack机制

参考 https://www.cnblogs.com/yeyuzhuanjia/p/15654374.html
https://www.volcengine.com/theme/745554-K-7-1

概念

Kafka中的ack应答机制是为了确保生产者发送的消息被消费者正确接收的机制。在Kafka中,生产者将消息发送到Kafka集群中的一个或多个分区,而消费者从这些分区中获取消息进行消费。当消费者接收到一条消息时,它需要向Kafka集群确认已经正确接收该消息。这个确认过程就是ack应答机制。

Kafka的ack应答机制的实现基于分布式系统的幂等性。在分布式环境中,由于网络延迟等原因,可能会出现重复或丢失消息的情况。如果在生产者和消费者之间无法确保幂等性,就会导致数据的一致性问题。因此,在Kafka中,通过ack应答机制来确保消息被准确地传递到消费者。

相关概念:Leader与Follower

参考 https://blog.csdn.net/chenshijie2011/article/details/118074168
简单来说,每个分区中都会有一个leader与(可能)多个follwer。leader负责一切数据的收发,follower只复制从leader中读取数据 用于备份。leader挂了再从follwer中选举新的leader。

三种ack类型

0:不等broker确定,就继续发。

1:等leader确认,确认了再继续发。

-1:等follower确认,确认了再发。

三种机制性能递减,可靠性递增。

此部分学习记录:https://blog.csdn.net/Ws_Te47/article/details/136391804

Kafka的三种消息发送模式

参考自 https://developer.aliyun.com/article/765609

1、发后即忘(fire-and-forget)

只发送消息,不关心消息是否发送成功。即acks = 0

2、同步(sync)

同步发送,send()方法会返回Futrue对象,通过调用Futrue对象的get()方法,等待直到结果返回,根据返回的结果可以判断是否发送成功。

3、异步(async)

异步发送,在调用send()方法的时候指定一个callback函数,当broker接收到返回的时候,该callback函数会被触发执行。

只关心发送是否成功,不关心发送顺序。

Kafka的三种Offset提交模式

本部分参考自 : https://worktile.com/blog/know-681/
参考自:公众号-我的IT技术路

自动提交offset

通过enable_auto_commit参数控制是否自动提交。当其为true时候,会以频率auto_commit_interval_ms向Topic提交。具体的Partation是按partation=hash(group_id)%50来计算的。

手动提交offset

同步手动提交偏移量

会阻塞,直至提交成功才拉取消费后续消息。

异步手动提交偏移量+回调函数

消费者线程不会阻塞,提交失败的时候也不会进行重试,并且可以配合回调函数在broker做出响应的时候记录错误信息。

异步+同步 组合的方式提交偏移量

针对异步提交偏移量丢失的问题,通过对消费者进行异步批次提交并且在关闭时同步提交的方式,这样即使上一次的异步提交失败,通过同步提交还能够进行补救,同步会一直重试,直到提交成功。

kafka消费速度跟不上生产速度