MQ消息中间件分布式原理及常见问题
消息队列
为什么用?
解耦,削峰,异步
基本模型
生产者 -> MQ -> 消费者
Kafka 十万级/s 适用于大数据领域实时计算,日志采集,
ActiveMQ 万/s , RabbitMQ 万/s , RocketMQ 十万/s
Kafka为什么性能这么快?
Producer设计
1.批量发送消息 Kafka 采用了批量发送消息的方式,通过将多条消息按照分区进行分组,然后每次发送一个消息集合,从而大大减少了网络传输的 overhead(建立TCP连接损耗)
2.消息压缩 在批量发送的基础上,同时对多条消息进行压缩,能大幅减少数据量,从而更大程度提高网络传输率
3.紧凑方式序列化 Kafka 消息中的 Key 和 Value,都支持自定义类型,只需要提供相应的序列化和反序列化器即可。用户可以根据实际情况选用快速且紧凑的序列化方式(比如 ProtoBuf、Avro)来减少实际的网络传输量以及磁盘存储量,进一步提高吞吐量。
4.内存池复用 Producer一上来就会占用一个固定大小内存块,比如 64MB,然后划分成 M 个小内存块Batch 16k,按Batch块批量发送消息。 当需要创建一个新的 Batch 时,直接从内存池中取出一个 16 KB 的内存块即可,然后往里面不断写入消息
Broker设计
5.IO多路复用 基于Reactor 网络通信模型实现单线程监听大量连接
6.顺序写磁盘 Kafka 选用的是「日志文件」来存储消息,仅仅将消息追加到文件末尾即可 (磁盘顺序写的性能远远高于磁盘随机写,甚至高于内存随机写)
7.Page Cache Page Cache是操作系统提供的机制, 缓存的是最近会被使用的磁盘数据,最近访问的数据很可能接下来再访问到。而预读到 Page Cache 中的磁盘数据,依据是:数据往往是连续访问的。非常契合MQ这种消息中间件
8.分区分段存储 一个Topic对应多个partition,一个partition对应多个sagment。 同一个Topic下的partition可以存储在多台broker 大大提升每个Topic的吞吐量,同一个partation对应多个sagment文件,sagment文件支持顺序读写,删除旧sagment文件
9.稀疏索引 消息的 offset是一个单调递增 long 类型的字段,这样消息在日志文件中本身就是有序存放的了,便没必要为每个消息建 hash 索引,完全可以将消息划分成若干个 block,只索引每个 block 第一条消息的 offset 即可,先根据大小关系找到 block,然后在 block 中顺序搜索,这便是 Kafka “稀疏索引” 的设计思想。 当给定一个 offset 时,Kafka 采用的是二分查找来高效定位不大于 offset 的物理位移,然后找到目标消息。
10.mmap()读稀疏索引文件
11.sendfile()读磁盘文件
造成问题及解决
1.系统可用性降低 (MQ发生故障,全崩了)
2.MQ重复发送消息 重复消费问题 MQ丢消息问题 MQ发送消息顺序错乱
3.不一致性,MQ给客户端显示执行成功,结果在服务器一端执行失败了,导致不一致
MQ高可用问题:
RabbitMQ 普通集群模式,一个机器,多个节点 消费者随机找到一个MQ节点消费,该进程会去找其他MQ节点的数据同步过来,返还给消费者。 集群内部存在大量数据传输。一旦其中一个节点挂了,数据就没了。
RabbitMQ 镜像集群模式,每个节点的数据都会同步到其他全部节点,保证了每个节点都有全部数据,数据量过大同步困难
Kafka高可用框架:
同一数据存在不同机器上,每一份数据leader 都同步副本数据在一台机器上作为follower作为备份
消息重复消费问题:
Kafka重复消费问题:生产者生产的数据进入到kafka后都会为每一条数据标识一个offset值,消费者消费到该数据后,把offset值提交给zookeeper用来记录offset,再传回kafka证明消息已经消费完毕。
如果消费者消费完准备提交offset,还未提交就挂了,重启后不再提交到zookeeper,导致kafka以为该消息还没消费,继续发送给消费者导致重复消费。
解决办法:消费者程序做幂等性来保证
消息丢失问题:
Kafka
1.消费者到broker: 消费者把消息发送到broker主节点,并且同步到从节点后返回ack,消费者做try catch处理是否重复发送
2.broker到消费者: 关闭kafka自动提交offset,改成手动提交offset,在代码中消费到数据并完完全全彻底处理完数据后才提交offset
配置kafka四个参数
参数1:topic的relacation必须大于1(必须有多个follower)
参数2:min_insync.replicas必须大于1(必须至少有一个follower与自己保持联系)
参数3:asKs=all 要求必须生产者的数据发送到leader,并且同步到全部follower才算一次发送成功
参数4:retries=max 如果未同步到全部follower一直重试
消费大量积压问题
原因:一般是消费者挂掉导致大量消息积压在MQ
解决:临时创建一个topic,多开几倍的partition。原来的消费者更改逻辑 消费到的数据发到新topic上,多开几个消费者同时消费新的topic。
消费顺序出错问题
原因;把应该保证顺序的多条消息分散发给多个消费者,再从多个消费者整合到一起导致消息顺序错乱。
解决:把需要保证顺序的多条消息写入同一个消费者。
Kafka中写入到一个partition中的数据一定是有顺序的。
Kafka为啥这么快,性能这么高?
1.顺序写入
每个partation只能append追加,顺序IO写到磁盘而不是随机IO,避免了寻址时间
2.sendfile零拷贝读取
利用DMA技术从磁盘拷贝到内核缓冲区,内核缓冲区通过管道连接socket缓冲区,socket缓冲区拷贝到网卡上
消息队列推拉模式