Loading... # 消息中间件之Apache Kafka(一) Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以收集并处理用户在网站中的所有动作流数据,以及物联网设备的采样信息。 Kafka提供了消息订阅与发布的消息队列,一般用作系统间解耦、异步通信、削峰填谷等作用。同时Kafka又提供了Kafka Streaming插件包,实现了实时在线流处理。相比较一些专业的流处理框架,Kafka Streaming计算是运行在应用端,具有简单、入门要求低、部署方便等优点。 ## Kafka基础架构 Kafka集群以Topic形式负责分类集群中的Recoed(消息),每一个Record属于一个Topic。每个Topic底层都会对应一组分区的日志,用于持久化Topic中的Record。同时在Kafka集群中,Topic的每一个日志的分区,都一定会有1个Broker担当该分区的Leader,其它的Broker担当该分区的Follower,Leader负责分区数据的读写操作,Follower负责同步该分区的数据。这样,如果分区的Leader宕机,该分区的其它Follower会选取出新的Leader继续负责该分区数据的读写。其中集群中Leader的监控和Topic的部分元数据是存储在Zookeeper中。 也就是说,每个Broker也就是服务器都是自己所对应分区的Leader,然后根据设置的副本因子,决定每个分区的副本个数,这些副本会保存在其它的Broker中,也就是其它Broker是该分区的Follower。全局上来说,一个Broker是自己分区的Leader同时也是其它分区的Follower,当一个Broker宕机,因为其它的Broker中也存在它的副本,这时,Zookeeper就会从其它Broker中选出一个晋升为该分区的Leader,这个被选中的Broker就同时是两个分区的Leader了。就算之前的Leader恢复了,由于它下线这段时间,已经不是完整的数据了,因此,它只能以Follower的身份存在。 ## Topic与日志 Kafka中所有消息是通过Topic为单位进行管理,每个Kafka中的Topic通常会有多个订阅者,负责订阅发送到该Topic中的数据。Kafka负责管理集群中每个Topic的一组日志分区数据。 生产者将数据发布到相应的Topic,根据不同的负载策略,选择将哪个记录分发到Topic中的哪个Partition。例如round-robin方式,轮询负载,这种方式可以平均分配每个Partition中的Record。也可以根据某些语义分区功能(例如基于记录中的Key)进行此操作。 每组日志分区是一个有序的不可变的日志序列,分区中的每一个Record都被分配了唯一的序列编号称为offset,Kafka集群会持久化所有发布到Topic中的Record信息,该Record的持久化时间是通过配置文件指定的,默认是168小时。 ``` log.retention.hours=168 ``` Kafka底层会定期检查日志文件,然后将过期的数据从log中移除,由于Kafka使用硬盘存储日志文件,因此使用Kafka长时间缓存一些日志文件是不存在问题的。 在消费者消费Topic中数据的时候,每个消费者会维护本次消费对应分区的偏移量,消费者会在消费完一个批次的数据之后,将本次消费的偏移量提交给Kafka集群,因此对于每个消费者而言,可以随意控制该消费者的偏移量。因此,在Kafka中,消费者可以从一个Topic分区中的任意位置读取队列数据,由于每个消费者控制了自己的消费偏移量,因此多个消费者之间彼此相互独立。 Kafka中对Topic实现日志分区有以下目的: - 首先,它们允许日志扩展到超出单个服务器所能容纳的大小。每个单独的分区都必须受限于托管它的服务器,但是一个Topic可以有很多分区,因此它可以处理任意数量的数据(无容量限制)。 - 其次,每个服务器充当某些分区的Leader,也可能充当其它分区的Follower,因此,集群中的负载得到了很好的平衡 ## Consumer 消费者使用Consumer Group名称标记自己,并且发布到Topic的每条记录都会传递到给每个订阅了该Topic的Consumer Group中的一个消费者实例。如果所有Consumer实例都具有相同的Consumer Group,那么Topic中的记录会在该Consumer Group中的Consumer实例中进行均分消费。如果所有Consumer实例具有不同的Consumer Group,则每条记录将广播到所有Consumer Group中。 更常见的是,我们发现Topic具有少量的Consumer Group,每个Consumer Group可以理解为一个“逻辑订阅者”。每个Consumer Group均由许多Consumer实例组成,以实现可伸缩性和容错能力。这无非就是发布-订阅模型,其中订阅者是消费者的集群,而不是单个进程。这种消费方式Kafka会将Topic按照分区的方式均分给一个Consumer Group下的实例。如果Consumer Group下有新的成员加入,则新加入的Consumer实例会去接管Consumer Group内其他消费者负责的某些分区,同样如果一个Consumer Group下的Consumer宕机,则由该Consumer Group中的其它实例接管。 因为Consumer Group是按照分区均分给该Consumer Group下的实例的,因此,最好保证一个Consumer Group下的Consumer数量小于等于分区数,当大于分区数时,多出来的Consumer将无法分得消息,只能等待某个Consumer宕机。这里就体现了分区对于性能的影响,首先,单一Borker吞吐量有限,多个Broker可以增加生产者发布消息时的吞吐量,增加生产效率。其次,分区越多,代表可以执行的有效消费者越多,消费并发量越高,提升了消费效率。 由于Kafka的Topic分区策略,因此Kafka仅提供分区中记录的有序性,也就意味着相同Topic的不同分区记录之间无顺序。因为针对绝大多数的大数据应用和使用场景,使用分区内部有序或者使用Key进行分区策略已经能够满足了。但是,如果需要记录的全局有序,则可以通过只有一个分区Topic来实现,尽管这将意味着每个Consumer Group只有一个有效的Consumer进程。 ## Kafka如何保证高性能 ### 写入高性能 Kafka的特性之一就是高吞吐率,但是Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是低效的,但是Kafka却可以轻松支持每秒百万级的写入请求,超过了大部分消息中间件,这种特性也使得Kafka在日志处理等海量数据场景中广泛使用。Kafka会把收到的消息都写入到硬盘中,防止丢失数据。为了优化写入速度,Kafka采用了两个技术: - 顺序写入 - MMFile 因为硬盘是机械结构,每次读写都会先寻址,再写入。其中,寻址是一个“机械动作”,它是最耗时的。所以硬盘最讨厌随机I/O,喜欢顺序I/O。为了提高写硬盘的速度,Kafka就是使用顺序I/O。这样省去了大量的内存开销以及节省了I/O寻址的时间。但是单纯的使用顺序写入,Kafka的写入性能也不可能和内存进行对比,因此Kafka的数据并不是实时地写入硬盘中。 Kafka充分利用了现代操作系统分页存储,利用内存提高I/O效率。Memory Mapped File(mmap)也称为内存映射文件,在64位操作系统中,一般可以表示20G的数据文件,它的工作原理是直接利用操作系统的Page实现文件到物理内存的直接映射。完成mmap后,用户对内存的所有操作,会被操作系统自动的刷新到磁盘上,极大地降低了I/O使用率。也就是用户将数据写入内存中的映射区就完事儿了,内核会选择合适的时间将数据同步到磁盘上,对于客户端来说,就是仅仅写入了内存中。 ### 读取高性能 Kafka服务器在响应客户端读取的时候,底层使用<b>ZeroCopy(零拷贝)</b>技术,磁盘数据无需拷贝到用户空间,而是直接通过内核空间传递输出,数据并没有抵达用户空间。 #### 传统IO操作 1. 用户进程调用read等待系统调用向操作系统发出IO请求,请求读取数据到自己的内存缓冲区中,自己进入阻塞状态。 2. 操作系统接收到请求后,进一步将IO请求发送磁盘。 3. 磁盘驱动器收到内核的IO请求,把数据从磁盘读取到驱动器的缓冲区中,此时不占用CPU。当驱动器的缓冲区被读满后,向内核发起中断信号告知自己缓冲区已满。 4. 内核收到中断,使用CPU时间将磁盘驱动器缓冲区的数据拷贝到内核缓冲区。 5. 如果内核缓冲区的数据少于用户申请的数据,重复3、4步骤,直到内核缓冲区的数据足够为止。 6. 将数据从内核缓冲区拷贝到用户缓冲区,同时从系统调用中返回。完成任务 #### DMA读取 1. 用户进程调用read等待系统调用向操作系统发出IO请求,请求读取数据到自己的内存缓冲区中,自己进入阻塞状态。 2. 操作系统收到请求后,进一步将IO请求发送DMA,然后让CPU去干别的活。 3. DMA进一步将IO请求发送给磁盘。 4. 磁盘驱动器收到DMA的IO请求,把数据从磁盘读取到驱动器的缓冲区中。当驱动器的缓冲区被读满后,向DMA发起中断信号告知自己缓冲区已满。 5. DMA收到磁盘驱动器的信号,将磁盘驱动器缓冲区中的数据拷贝到内核缓冲区中,此时不占用CPU。这个时候只要内核缓冲区的数据少于用户申请的数据,内核就会一直重复3、4步骤,直到内核缓冲区数据足够为止。 6. 当DMA读取了足够多的的数据,就会发送中断信号给CPU。 7. CPU收到DMA的信号,知道数据已经准备好,于是将数据从内核拷贝到用户空间,系统调用返回。 跟传统IO中断模式相比,DMA模式下,DMA就是CPU的一个代理,它负责了一部分拷贝工作,从而减轻了CPU的负担。DMA的优点就是: 中断少,CPU负担低。 #### 一般方案 1. 文件在磁盘中数据被copy到内核缓冲区 2. 从内核缓冲区copy到用户缓冲区 3. 用户缓冲区copy到内核与socket相关的缓冲区 4. 数据从socket缓冲区copy到相关协议引擎发送出去 ![常规IO.jpg][1] #### 零拷贝 1. 文件在磁盘中数据被copy到内核缓冲区 2. 从内核缓冲区copy到内核与socket相关的缓冲区 3. 数据从socket缓冲区copy到相关协议引擎发送出去 ![零拷贝.jpg][2] [1]: https://www.princelei.club/usr/uploads/2020/07/3610655682.jpg [2]: https://www.princelei.club/usr/uploads/2020/07/1163819121.jpg Last modification:December 9th, 2020 at 10:10 am © 允许规范转载