消息队列
作用
异步处理
将消息丢进消息队列,不需要同步等待处理成功。比如对于一个处理过程,将其划分为同步和异步处理的几个模块,将同步处理完之后的结果丢进消息队列之后就可以返回了,缩短了处理时间,提高了并发处理能力。
但是使用消息队列就多了一个链路,增大响应时延,并且需要将前后链路从同步处理改为异步,增大了系统的复杂性。
流量控制
其实就是一个削峰填谷的作用。当日志的生产速度远大于消费的速度时,消息队列就会起到一个类似蓄水池的作用,当然如果蓄水池也满了那还是会出问题。
服务解耦
将同步处理改为异步处理
一个核心功能模块,随着业务的发展,与其交互的模块会越来越多,如果每接入一个模块,就修改代码或者配置,耦合性太高。这是就可以将核心功能对应的处理结果放入消息队列中,谁要对接该功能,只需要消费处理消息队列的结果消息即可,从而实现了解耦。
其他作用
发布订阅功能、消息日志的通道,连接流计算任务和数据
局限性
- 增加处理链路和响应时延
- 同步改异步,增加系统的复杂度
- 分布式的消息队列会带来消息丢失、数据不一致等新的问题
消息队列比较
RabbitMQ
优点
- 轻量、易于使用
- 支持AMQP协议,支持的客户端也很多
- 在生产者和消息队列之间有一个exchange,可以实现灵活的路由配置
缺点
- 只支持一个消费组,不能重复消费
- 对消息队列积压支持不好,如果出现大量积压,会造成性能的急剧下降
- 采用主从而不是分片架构,性能较差,每秒仅支持几万到十几万的消息量
- 开发语言使用的时Erlang,冷门并且学习门槛较高,碰到问题很难解决。
RocketMQ
使用Java开发,源码易于看懂,中文文档很全,并且时延较低。每秒的吞吐量有几十万条
Kafka
不仅是一个日志引擎系统,还是一个分布式流处理系统,甚至是一个分布式存储系统
优点
- 客户端使用批量、异步、压缩的方法,使得日志的吞吐量很高
- broker端使用mmap和zero copy的技术,提高IO速度
- 支持多个消费组和重复消费
缺点
- 集群复杂,并且需要结合zookeeper实现
实现
采用分区的机制,集群上的一个机器节点对应一个分区,各分区实现单独的消息收发处理,分区可动态水平扩展,当吞吐量不够时,可以直接通过加分区的方式扩展吞吐量
采用全异步的线程模型和传输方式、在发送一条消息时,不是把它立即发送出去,而是写到缓存队列中,然后再由另外的线程负责发送
异步的网络模型
采用批量发送,不是一条消息就发送一次,而是等攒够多少条之后发送一次;并且服务端在收到一批消息之后,也是一批消息为单位来进行存储、同步和消费的,消费的客户端是先拉取一批消息,然后解析成一条条的消息,再交给用户代码处理,这样就大大降低了请求和处理的次数
自定义的传输协议、序列化、反序列化实现:使用存储密度高的协议,抛弃可读性,不传输字段名,同时固定字段顺序,采用变长存储,定义每个字段的长度和实际的存储内容。
高效的内存管理:避免重复创建临时对象,而是只使用一个对象;对于大对象,使用对象池,可以重复使用,避免频繁的创建和回收
使用顺序读写提高磁盘IO性能:机械硬盘的随机读和顺序读的性能差距有几十倍,固态硬盘也有几倍,Kafka采用顺序写文件大大的提高了IO性能
使用pagecache加速文件读写:内存的随机读写速度是磁盘的十万倍,使用文件在内存中的缓存,尽量避免直接与磁盘IO打交道。
使用zero-copy技术,减少数据的拷贝次数
DMA
SSD硬盘的IOPS是几万,而CPU的主频可以达到2GHz,也就是每秒可以达到20亿次操作。因此如果有CPU来控制磁盘IO,大部分的CPU时间全部都浪费在了IO等待上
,因此就有了DMA技术,对应的是DMAC,也就是硬件协处理器。需要读写文件时,CPU只需要发信号给DMAC,然后读写工作由DMAC完成,此时CPU去处理别的事情,完成之后DMAC再发信号给CPU。
如果是一般的文件发送,需要经过下面四个操作
1、将数据从磁盘加载到内核空间上,由DMAC负责
2、将数据从内核空间拷贝用户应用空间,由CPU负责
3、将数据从用户应用空间拷贝到socket内核空间,由CPU负责
4、将数据从socket内核空间拷贝到网卡缓存空间,由DMAC负责。
上面的2,3步骤其实即使把数据从内存的一个地方拷贝到另一个地方,因此此我们可以把这两个步骤省略,直接将加载到内核空间的磁盘数据拷贝到网卡空间中。这样就大大的减少了数据的处理时间,这个过程也可以由DMAC直接实现,整个过程不需要CPU介入,因为这个过程没有在内存层复制数据,因此称为zero-copy技术。使用该技术可以将处理时间缩短为原来的三分之一。
Kafka是通过使用Java中NIO的FileChannel类的transferTo方法实现的,当然该方法调用的Linux的api方法sendFile实现的。
RDMA
https://www.jianshu.com/p/22bbb8f029e6
https://zhuanlan.zhihu.com/p/55142557
就是可以将用户空间的空间不经过内核空间,直接复制到网卡的缓存区,而接收方的网卡在收到数据后,也是直接将数据从网卡缓存区直接移动到用户空间。从而大大的提供了数据的传送效率,当然该技术也需要网卡层提供专门的硬件支持,是通过将协议固化在网卡层实现的,是一种host-bypass,host-offload的技术。
优势如下:zero-copy技术;内核旁路kernel bypass,也就是不需要经过系统内核空间;CPU不参与
使用数据压缩,节省存储空间,提高网络存储性能。压缩其实就是一个时间换空间的方案,解压缩都需要消耗CPU的时间,但是却减小了存储空间。因此对于IO密集的应用可以开启压缩,而计算密集的应用的慎重使用压缩。在Kafka中,数据是以一批为单位进行解压缩的,并且解压缩是分散在客户端进行的。
特性
如何保证消息不丢失
生产者的回调方法
生产者的发送接口中有回调方法,当cluster端无论是消息保存成功还是失败,都会触发生产者的回调函数,并告知存储结果。如果存储失败,生产者可以采用重试或者其他处理机制;
消费者的确认机制
消费者在拉取消息之后也有一个确认机制,默认是自动确认,只要消费到消息就默认该消息处理成功,也可以改成手动确认,只有消费者处理成功之后,才给cluster发送确认消息,这样可以保证消费者处理消息失败后下次消费到还是这条消息能够进行再次处理。
存储机制
设置为保存到硬盘中才认为是保存成功
不同副本分布在不同的分区,保存到多个副本中才认为是保存成功,副本的数据是参数可设的
数据不一致的副本不允许竞选leader副本
特殊的丢消息的场景
再添加分区时,如果生产者先于消费者检测到该分区,就开始向里面写消息,写入一些消息之后,消费者检测到该分区,可能会从最新的位移开始消费,而不是从消息开始的位置消费,当然可以通过参数设置从开始的位置消费。
消息积压怎么处理
生产者发送过快
关闭一些非必须的日志发送
消费过慢
增加消费者,但是同时也要增加分区数,因为有效消费者的最大值与分区数保持一致,每个分区只允许一个消费者消费
批量消费,有一定局限性,消费无需,并且允许批量确认
检查是否是消息消费失败到时重复消费
还有一种错误的解决办法是,将所有的消息先消费出来保存在一个新的队列里,然后在使用线程池或者慢慢消费,这样如果消费者队列里的消息丢失就永远丢失了
plusar
优点
采用计算和存储分离的设计,这样即使在节点或者请求量很多时,也不会出现性能瓶颈
缺点
多出一个系统,设计复杂;实际应用较少