一 序

     过了一个春节假期,好久没看书了,决定翻翻kafka的书,之前项目作为消费者使用过,脑子有印象的就是其消费者与生产者。然其底层的存储机制,选举机制,备份机制等实现细节,需要对其源码进行学习。知其所以然是为了更好的使用。关于kafka的资料很多,官网是首选,还是先看本书再看下代码,《kafka权威指南》偏理论,这本《Apache kafka实战》更适合。

二 背景

     Kafka它本质上是一个消息系统,由当时从LinkedIn出来创业的三人小组开发,当时主要为了解决数据管道(data pipeline)的问题,当然现在官网介绍已经是支持分布式流式平台了(a distributed streaming platform)。书上给出了消息引擎系统的翻译,认为消息队列或者消息中间件不能准确反映出kafka的含义。

    2.1消息引擎系统考虑因素:

        消息设计:kafka二进制保存的,结构化设计。

        传输协议设计: 没有采用pB,自定义的二进制消息传输协议。

     2.2 消息引擎泛型:

1)基于队列,提供了点对点的消息传递方式,一旦消息被消费( consumed ),就会从队列中移除该消息 。 每条消息由一个发送者生产 出来,且只被一个消费者( consumer)处理一一发送者和消费。

2)发布/订阅模型:生成者发布一条消息可以被多个消费者消费,kafka 引入消费者组来支持这种模型。

2.3 kafka概要设计

    kafka在设计初衷就是为了解决互联网公司的超级大量级数据的实时传输。为了实现这个目标,kafka在设计之初就需要考虑以下四个方面:

  第一:吞吐量/延迟

  第二:消息持久化 

  第三:负载均衡和故障转移

  第四:伸缩性

2.3.1.吞吐量/延时介绍

  吞吐量/延时  是一对矛盾体。

     Kafka如何做到高吞吐量,低延迟的呢? 

  首先,kafka的写入操作是很快的,这主要得益于它对磁盘的使用方法的不同。虽然kafka会持久化所有数据到磁盘,但本质上每次写入操作其实都只是把数据写入到操作系统的页缓存(page cache)中,然后由操作系统自行决定什么时候把页缓存中的数据写入磁盘上。这样的设计由三个主要的优势:

  第一:操作系统页缓存是内存中分配的,所以消息写入的速度非常快;

  第二:kafka不必直接与底层的文件系统打交道。所以烦琐的I/O操作都交由操作系统来处理;

  第三:kafka写入操作采用追加写入(append)方式,避免了磁盘随机写操作(据资料统计,顺序磁盘I/O速度是毫不逊色于随机读写内存I/O速度。感兴趣的小伙伴可以使用相关工具测试一下。); 

    .Kafka的高吞吐量,低延迟的设计目标

  第一:大量使用操作系统页缓存,内存操作速度快且命中率高; 

  第二:Kafka不直接参与物理I/O操作,而是交由最擅长此时的操作系统来完成; 

  第三:采用追加写入方式,摒弃了缓慢的磁盘随机读/写操作;

  第四:使用sendfile为代表的零拷贝技术加强网络间的数据传输效率; 

2.3.2.消息持久化的优点

  第一:解耦消息发送和消息消费

  本质上来说,kakfa最核心的功能就是提供了生产者-消费者模式的完整解决方案。通过将消息持久化使得生产者方不再需要直接和消费者方耦合,它只是简单的把消息生产出来并交由kafka服务器保存即可,因此提升了整体的吞吐量。 

  第二:实现灵活的消息处理

  很多kafka的下游子系统(接受kafka消息的系统)都有这样的需求:对于已经处理过的消息可能在未来的某个时间点重新处理一次,即所谓的消息消息重演(message replay)。消息持久化便可以很方便地实现这样的需求。 

2.3.3:负载均衡和故障转移 

  作为一个功能完备的分布式系统,kafka如果只提供了最基本的消息引擎功能肯定不足以帮助它脱颖而出。一套完整的消息引擎解决方案中韩必然要提供负载均衡(load balancing)和故障转移(fail-over)功能。

  何为负载均衡?顾名思义就是让系统的负载根据一定的规则均衡地分配在所有参数工作的服务器上,从而最大限度的提升整体的运行效率。kafka实现负载均衡实际上是通过智能化的分区领导者选举(partition leader election)来实现的。 

  除了负载均衡,完备的分布式系统还支持故障转移,所谓故障转移,是指当服务器意外终止时,整个集群可以快速的检测到该失效(failure),并立即将该服务器上应用或服务自动转移到其他服务器上。故障转移通常是“心跳”和“会话“的机制来实现的。kafka服务器支持故障转移的方式就是使用绘画机制。每台kafka服务器启动后会以会话的形式把自己注册到zookeeper服务器上。一旦该服务运转出现问题,与zookeeper的会话变不能维持从而超时失效,此时kafka集群会选举出另外一台服务器来完全代替这台服务器继续提供服务。

2.3.4:伸缩性

  所谓伸缩性,英文名是scalability。伸缩性表示想分布式系统中增加额外的计算资源(比如CPU,内存,存储或带宽)时吞吐量提升的能力。阻碍线性扩容的一个很常见的因素就是状态的保存。我们知道,不论是哪类分布式系统,集群的每台服务器一定会维护很多内部状态。如果由服务器自己来保存这些状态信息,则必须处理一致性的问题。相反,如果服务器是无状态的,状态的保存和管理交与专门的协调服务来做(比如zookeeper)。那么整个集群的服务武器之间就无需繁重的状态共享,者极大的降低了维护复杂度。倘若要扩容集群节点,只需要简单的启动新的节点集群和进行自动负载均衡就可以了。 

  Kafka正式采用了这样的思想:每台kafka服务器上的状态统一交友zookeeper保管。扩展kafka集群也只需要一步:启动新的kafka服务器即可。当然这里需要言明的是,在kafka服务器上并不是所有的状态信息都不保存,它只保存了很轻量级的内部状态(比如从kakka 0.10.x版本之后,它将每个topic的消费者的偏移量自己维护了,把这些偏移量存放到了一个叫做“__consumer_offsets”的的topic进行维护)。

2.4 基本概念

  

kafka哪本书比较好 kafka看什么书_设计目标

网上有很多图,图片来自

简单概括kafka架构就是:Producers往Brokers里面的指定Topic中写消息。Consumers从Brokers里面拉去指定Topic的消息进行业务处理。brokers依托zk集群进行服务的协调管理。

常见的图上的术语如下。

消息:

     首先,kakfa中的消息有很多字段组成,其中有的很多字段都是用于管理消息的原数据字段,对用户来说是完全透明的。kakfa的消息格式经历过3次变迁(我们此次暂不考虑新出的kafka 2.0.版本)他们分别称为V0,V1和V2版本。其中V1 版本消息格式如下:

  

kafka哪本书比较好 kafka看什么书_设计目标_02

        消息由消息头部,key和value组成。消息头部包括消息的CRC码,消息版本号,属性,时间戳,键长度,和消息长度等信息。其实对于普通用户来说,掌握一下3个字段的含义一般就够用了:

    key :消息键,对消息做partition时使用,即决定消息被保存在某topic下的哪个partition。

    value:消息体,保存实际的消息数据。

    timestamp:消息发送时间戳, 用于流式处理及其他依赖时间的处理语义。如果不指定,则取当前时间。

  另外这里单独提一下消息的属性字段,kafka为该字段分配了一个字节,目前只使用了最低的3我为用于保存消息的压缩类型,其余5为尚未使用。当前支持4中压缩类型:0(无压缩),1(GZIP),2(Snappy)和3(LZ4)。

  其次,kafka使用紧凑的二进制字节数组来保存消息格式的字段,也就是说没有任何多余的比特位浪费。kafka在消息设计时特意避开了繁重的Java堆内存分配,直接使用紧凑二进制字节数组ByteBuffer而不是独立的对象,因此我们至少能够访问多一倍的可用内存。按照Kafka官网的说法,在一台32GB内存的机器上,Kafka几乎能够用到28~30GB的物理内存,同时还不必担心GC的糟糕性能。如果使用ByteBuffer来保存同样的消息,只需要24字节,比起纯Java堆的实现减少了40%的空间占用,好处不言而喻。这种设计的好处还包括加入了扩展的可能性。

  同时,大量使用也缓存而非对内存还有一个好处:当出现Kafka broker进程崩溃时,堆内存的数据也一并小时,但页缓存的数据易燃存在。下载Kafka broker重启后可以继续提供服务,不需要再单独“热”缓存了。

.topic和partition

  在概念上来说,topic只是一个逻辑概念,代表了一类消息,也可以认为是消息被发送到的地方。通常我们可以使用topic来区分实际业务,比如业务A使用一个topic,业务B使用另一个topic。从本质上说,每个Kafka topic都由若干个partition组成,而Kafka的partition是不可修改的有序消息序列,也就是说是有序的消息日志。每个partition有自己专属的partition号,通常是从0开始的。用户堆partition我唯一能做的操作就是在消息序列的尾部追加写入消息。

  partition上的每条消息都会被分配一个唯一的序列号,按照Kafka的术语来讲,该序列号被称为位移(offset)。该位移值是从0开始顺序递增的证书。位移信息可以唯一定义到某partition下的一条消息。值得一提的是,Kafka的partition实际上并没有太多的业务含义,它的引入就是单纯的为了提升系统的吞吐量,因此在创建Kafka topic的时候可以根据集群实际配置设置具体的partition数,实现整体性能的最大化。

kafka哪本书比较好 kafka看什么书_kafka哪本书比较好_03

offset 

上面说过,topic partition下的每条消息都被分配了一个位移值。实际上,Kafka消费者端也有位移(offset)的概念,但一定要注意这两个offset属于不同的概念。

  显然,每条消息在某个partition的位移是固定的,但消费该partition的消费者的位移是会随着消费进度不断迁移,但终究不可能超过该分区最新一条消息的位移。综合之前说的topic,partition和offset,我们可以断言Kafka中的一条消息其实就是一个<topic,partition,offset>三元组(tuple),通过该元组值我们可以在Kafka集群中找到位移对应的那条消息。

replica

  既然我们已知partition是有序的消息日志,那么一定不能只保存者一份日志,否则一旦保存在partition的Kafka服务器挂掉了,其上保存的消息也就都丢失了。分布式系统必然要实现高可靠性,而目前实现的主要途径还是依靠冗余机制。换句话说,就是备份多份日志。这些分贝日志在Kafka中被称为副本(replica),它们存在的唯一目的就是防止数据丢失.

leader和follower

  副本(replia)分为两类:领导者副本(leader replia)和追随者副本(follower replia)。follower replica是不能提供服务给客户端的,也就是说不负责响应客户端发来的消息写入和消息消费请求。它只是被动地向领导者副本(leader replia)获取数据,而一旦leader replica 所在的broker宕机,Kafka会从剩余的replica中选举出新的leader继续提供服务。

  Kafka保证同一个partition的多个replica一定不会分配在同一台broker上。毕竟如果同一个broker上有同一个partition的多个replica,那么将无法实现备份冗余的效果。

ISR

  ISR的全称是in-sync replica,翻译过来就是与leader replica保持同步的replica集合。这是一个特别重要的概念。前面讲了很多关于Kafka的副本机制,比如一个partition可以配置N个replica,那么这是否就意味着该partition可以容忍N-1个replica实现而不丢失数据呢?答案是:“否”!

  副本数对Kafka的吞吐率是有一定的影响,但极大的增强了可用性。默认情况下Kafka的replica数量为1,即每个partition都有一个唯一的leader,为了确保消息的可靠性,通常应用中将其值(由broker的参数offsets.topic.replication.factor指定)大小设置为大于1,比如3。 所有的副本(replicas)统称为Assigned Replicas,即AR。ISR是AR中的一个子集,由leader维护ISR列表,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower剔除出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。相反的,当这些replicas重新“追上”了leader的进度时,那么Kafka会将他们加回

到ISR中。这一切都是自动维护的,不需要用户进行人为干预,因而在保证了消息交付语义的同时,还简化了用户的操作成本。

2.5 使用场景

 消息传输

网站行为日志追踪

审计数据收集

日志收集

Event Sourcing

流式处理