kafka笔记

954 阅读14分钟

kafka

1.前言

消费者组(Consumer Group)

  • 一个或者多个consumer thread(消费者线程) 组成一个Consumer group(消费者组),partition中的每个message只能被组(Consumer group )中的一个consumer( consumer 线程 )消费,如果一个message可以被多个 consumer( consumer 线程 ) 消费的话,那么这些consumer必须在不同的组。一个消费者组可以消费所有的partition,但是“同一时刻”,只有一个消费者去消费某一个partition,其他消费者不能同时消费同一个partition中的message。如果想多个不同的业务都需要这个topic的数据,起多个consumer group就好了,大家都是顺序的读取message,offset的值互不影响。这样没有锁竞争,充分发挥了横向的扩展性,吞吐量极高。这也就形成了分布式消费的概念。
  • 当启动一个consumer group去消费一个topic的时候,无论topic里面有多少个partition,无论consumer group里面配置了多少个consumer thread,这个consumer group下面的所有consumer thread一定会消费全部的partition;即便这个consumer group下只有一个consumer thread,那么这一个consumer thread也会去消费所有的partition。因此,最优的设计就是,consumer group下的consumer thread的数量等于partition数量,这样效率是最高的。
  • 当consumer group里面的consumer数量小于这个topic下的partition数量的时候,如下图groupA,groupB,就会出现一个conusmer thread消费多个partition的情况,总之是这个topic下的partition都会被消费。如果 consumer group里面的consumer数量等于这个topic下的partition数量的时候,如下图groupC,此时效率是最高的,每个partition都有一个consumer thread去消费。当 consumer group里面的consumer数量大于这个topic下的partition数量的时候,如下图GroupD,就会有一个consumer thread空闲。因此,我们在设定consumer group的时候,只需要指明里面有几个consumer数量即可,无需指定对应的消费partition序号,consumer会自动进行rebalance。

img

Consumer Rebalance触发条件

  • Consumer增加或删除会触发 Consumer Group的Rebalance
  • Broker的增加或者减少都会触发 Consumer Rebalance

Consumer

  • Consumer处理partition里面的message的时候是顺序读取的。所以必须维护着上一次读到哪里的offset信息。一个offset是归一个消费者组共有的,当一个消费者消费一条message后,整个消费者组的offset加一,这样避免了不同consumer对同一message的重复消费。
  • 所以我们线上的分布式多个service服务,每个service里面的kafka consumer数量都小于对应的topic的partition数量,但是所有服务的consumer数量之和等于partition的数量,这是因为分布式service服务的所有consumer都来自一个consumer group,如果来自不同的consumer group就会处理重复的message了(同一个consumer group下的consumer不能处理同一个partition,不同的consumer group可以处理同一个topic,那么都是顺序处理message,一定会处理重复的。一般这种情况都是两个不同的业务逻辑,才会启动两个consumer group来处理一个topic)。
  • 如果producer的流量增大,当前的topic的parition数量=consumer数量,这时候的应对方式就是横向扩展:增加topic下的partition,同时增加这个consumer group下的consumer。

Delivery Mode

  • Producer 发送message不用维护message的offset信息,因为这个时候,offset就相当于一个自增id,producer就尽管发送message就好了。Kafka的producer一般都是大批量的batch发送message,向这个topic一次性发送一大批message,load balance到一个partition上,一起插进去,offset作为自增id自己增加就好。
  • Consumer端是需要维护这个partition当前消费到哪个message的offset信息的,这个offset信息,high level api是维护在Zookeeper上,low level api是自己的程序维护。(Kafka管理界面上只能显示high level api的consumer部分,因为low level api的partition offset信息是程序自己维护,kafka是不知道的,无法在管理界面上展示 )当使用high level api的时候,先拿message处理,再定时自动commit offset+1(也可以改成手动), 并且kakfa处理message是无锁操作的。因此如果处理message失败,此时还没有commit offset+1,当consumer thread重启后会重复消费这个message。但是作为高吞吐量高并发的实时处理系统,at least once的情况下,至少一次会被处理到,是可以容忍的。如果无法容忍,就得使用low level api来自己程序维护这个offset信息,那么想什么时候commit offset+1就自己搞定了。

Topic和Partition

  • Topic相当于传统消息系统MQ中的一个队列queue,producer端发送的message必须指定是发送到哪个topic,但是不需要指定topic下的哪个partition,因为kafka会把收到的message进行load balance,均匀的分布在这个topic下的不同的partition上( hash(message) % [broker数量] )

  • 这个topic会分成一个或多个partition,每个partiton相当于是一个子queue。在物理结构上,每个partition对应一个物理的目录(文件夹),文件夹命名是[topicname][partition][序号],一个topic可以有无数多的partition,根据业务需求和数据量来设置。在kafka配置文件中可随时更改num.partitions参数来配置更改topic的partition数量,在创建Topic时通过参数指定parittion数量。 Topic创建之后通过Kafka提供的工具也可以修改partiton数量。

  • 一般来说

    • 一个Topic的Partition数量大于等于Broker的数量, 可以提高吞吐率。
    • 同一个Partition的Replica尽量分散到不同的机器, 高可用。
  • 当add a new partition的时候,partition里面的message不会重新进行分配,原来的partition里面的message数据不会变,新加的这个partition刚开始是空的,随后进入这个topic的message就会重新参与所有partition的load balance。

Partition Replica

  • 每个partition可以在其他的kafka broker节点上存副本,以便某个kafka broker节点宕机不会影响这个kafka集群。存replica副本的方式是按照kafka broker的顺序存。例如:有5个kafka broker节点,某个topic有3个partition,每个partition存2个副本,那么partition1存broker1,broker2,partition2存broker2,broker3。。。以此类推(replica副本数目不能大于kafka broker节点的数目,否则报错。这里的replica数其实就是partition的副本总数,其中包括一个leader,其他的就是copy副本)。这样如果某个broker宕机,整个kafka内数据依然是完整的。但是,replica副本数越高,系统虽然越稳定,但是会带来资源和性能上的下降;replica副本少的话,也会造成系统丢数据的风险。

    • 怎样传送消息:
      • producer先把message发送到partition leader,再由leader发送给其他partition follower。(如果让producer发送给每个replica那就太慢了)

    • 在向Producer发送ACK前需要保证有多少个Replica已经收到该消息:
      • 根据ack配置的个数而定

    • 怎样处理某个Replica不工作的情况:
      • 如果这个不工作的partition replica不在ack列表中,就是producer在发送消息到partition leader上,partition leader向partition follower发送message没有响应,这个不会影响整个系统,也不会有什么问题。如果这个不工作的partition replica在ack列表中的话,producer发送的message的时候会等待这个不工作的partition replca写message成功,一直等到time out,最后返回失败。因为某个ack列表中的partition replica没有响应,此时kafka会自动的把这个不工作的partition replica从ack列表中移除,以后的producer发送message的时候就不会有这个ack列表下的这个不工作的partition replica了。
    • 怎样处理Failed Replica恢复回来的情况:
      • 如果这个partition replica之前不在ack列表中,那么启动后重新接受Zookeeper管理即可,之后producer发送message的时候,partition leader会继续发送message到这个partition follower上。如果这个 partition replica在ack列表中,此时重启后,需要把这个partition replica再手动加到ack列表中。(ack列表是手动添加的,出现某个不工作的partition replica的时候自动从ack列表中移除)

Partition leader与follower

  • partition也有leader和follower之分。leader是主partition,producer写kafka的时候先写partition leader,再由partition leader push给其他的partition follower。partition leader与follower的信息受Zookeeper控制,一旦partition leader所在的broker节点宕机,zookeeper会在其他的broker的partition follower上将follower变为parition leader。

Topic分配partition和partition replica的算法

  • 将Broker(size=n)和待分配的Partition排序。
  • 将第i个Partition分配到第(i%n)个Broker上。
  • 将第i个Partition的第j个Replica分配到第((i + j) % n)个Broker上。

消息可靠性

  • 消息投递可靠性:一个消息如何算投递成功,Kafka提供了三种模式:
    • 啥都不管,发送出去就当作成功。(不能保证消息成功投递到broker)
    • Master-Slave模型,只有当Master和所有Slave都接收到消息时,才算投递成功。(模型提供了最高的投递可靠性,但损伤了性能)
    • 只要Master确认收到消息就算投递成功。(实际使用时,根据应用特性选择,大多数情况下会中和可靠性和性能选择本模型)
  • 消息在broker上的可靠性
    • 因为消息会持久化到磁盘上,所以如果正常stop一个broker,其上的数据不会丢失;但是如果不正常stop,可能会使页面缓存来不及写入磁盘的消息丢失,这可以通过配置flush页面缓存的周期、阈值缓解,但是频繁的写磁盘会影响性能,根据实际情况配置。
  • 消息消费的可靠性
    • Kafka提供的是“At least once”模型,因为消息的读取进度由offset提供,offset可以由消费者自己维护也可以维护在zookeeper里,但是当消息消费后consumer挂掉,offset没有即时写回,就有可能发生重复读的情况,这种情况同样可以通过调整commit offset周期、阈值缓解,甚至消费者自己把消费和commit offset做成一个事务解决,但是如果你的应用不在乎重复消费,那就干脆不要解决,以换取最大的性能。

Partition ack

  • 当ack=1:表示producer写partition leader成功后,broker就返回成功,无论其他的partition follower是否写成功。
  • 当ack=2:表示producer写partition leader和其中一个follower成功的时候, broker就返回成功,无论其他的partition follower是否写成功。
  • 当ack=-1 [parition的数量]:表示只有producer全部写成功的时候,才算成功,kafka broker才返回成功信息。

注意:如果ack=1的时候,有个broker宕机导致partition的follower和leader切换,会导致丢数据。

imgimg

message状态

  • 在Kafka中,消息的状态被保存在consumer中,broker不会关心哪个消息被消费了以及被谁消费了,只记录一个offset值(指向partition中下一个要被消费的消息位置),这就意味着如果consumer处理不好的话,broker上的一个消息可能会被消费多次。

message持久化

  • Kafka中会把消息持久化到本地文件系统中,并且保持极高的效率。众所周知IO读取是非常耗资源的,性能也是最慢的,这就是为什么数据库的瓶颈经常在IO上,需要换SSD硬盘的原因。但是Kafka作为吞吐量极高的MQ,却可以非常高效的把message持久化到文件。这是因为Kafka是顺序写入的,时间复杂度为o(1),速度非常快,这也是高吞吐量的原因。由于message的写入持久化是顺序写入的,因此message在被消费的时候也是按顺序被消费的,保证partition的message也是顺序消费的。一般的机器,单机每秒100k条数据。

message有效期

  • Kafka会长久保留其中的消息,以便consumer可以多次消费,当然其中很多细节是可配置的。

Produer

  • Producer向Topic发送message,不需要指定partition,直接发送就好了。kafka通过partition ack来控制是否发送成功并把信息返回给producer,producer可以有任意多的thread,这些kafka服务器端是不care的。Producer端的delivery guarantee默认是At least once的。也可以设置Producer异步发送实现At most once。Producer可以用主键幂等性实现Exactly once。

Kafka高吞吐量

  • Kafka的高吞吐量体现在读写上,分布式并发的读和写都非常快,写的性能体现在以o(1)的时间复杂度进行顺序写入。读的性能体现在以o(1)的时间复杂度进行顺序读取, 对topic进行partition分区,consume group中的consume线程可以以很高的性能进行顺序读。

Kafka delivery guarantee(message传送保证)

  • At most once 消息可能会丢,绝对不会重复传输
  • At least once 消息绝对不会丢,但是可能会重复传输
  • Exactly once 每条信息会被传输一次且仅传输一次,这是用户想要的。

批量发送

  • Kafka支持以消息集合为单位进行批量发送,以提高push效率。

push-and-pull

  • Kafka中的Producer和Consumer采用的是push-and-pull模式,即Producer只管向broker push消息,Consumer只管从broker pull消息,两者对消息的生产和消费是异步的。

Kafka集群中broker之间的关系

  • 不是主从关系,各个broker在集群中地位一样,我们可以随意的增加或删除任何一个broker节点。

负载均衡方面

  • Kafka提供了一个 metadata API来管理broker之间的负载(对Kafka0.8.x而言,对于0.7.x主要靠zookeeper来实现负载均衡)。

同步异步

  • Producer采用异步push方式,极大提高Kafka系统的吞吐率(可以通过参数控制是采用同步还是异步方式)。

分区机制partition

  • Kafka的broker端支持消息分区partition,Producer可以决定把消息发到哪个partition,在一个 partition 中message的顺序就是Producer发送消息的顺序,一个topic中可以有多个partition,具体partition的数量是可配置的。partition的概念使得kafka作为MQ可以横向扩展,吞吐量巨大。partition可以设置replica副本,replica副本存在不同的kafka broker节点上,第一个partition是leader,其他的是follower,message先写到partition leader上,再由partition leader push到parition follower上。所以说kafka可以水平扩展,也就是扩展partition。

离线数据装载

  • Kafka由于对可拓展的数据持久化的支持,它也非常适合向Hadoop或者数据仓库中进行数据装载。

实时数据与离线数据

  • kafka既支持离线数据也支持实时数据,因为kafka的message持久化到文件,并可以设置有效期,因此可以把kafka作为一个高效的存储来使用,可以作为离线数据供后面的分析。当然作为分布式实时消息系统,大多数情况下还是用于实时的数据处理的,但是当cosumer消费能力下降的时候可以通过message的持久化在淤积数据在kafka。

插件支持

  • 现在不少活跃的社区已经开发出不少插件来拓展Kafka的功能,如用来配合Storm、Hadoop、flume相关的插件。

解耦

  • 相当于一个MQ,使得Producer和Consumer之间异步的操作,系统之间解耦

冗余

  • replica有多个副本,保证一个broker node宕机后不会影响整个服务

扩展性

  • broker节点可以水平扩展,partition也可以水平增加,partition replica也可以水平增加

峰值

  • 在访问量剧增的情况下,kafka水平扩展, 应用仍然需要继续发挥作用

可恢复性

  • 系统的一部分组件失效时,由于有partition的replica副本,不会影响到整个系统。

顺序保证性

  • 由于kafka的producer的写message与consumer去读message都是顺序的读写,保证了高效的性能。

缓冲

  • 由于producer那面可能业务很简单,而后端consumer业务会很复杂并有数据库的操作,因此肯定是producer会比consumer处理速度快,如果没有kafka,producer直接调用consumer,那么就会造成整个系统的处理速度慢,加一层kafka作为MQ,可以起到缓冲的作用。

异步通信

  • 作为MQ,Producer与Consumer异步通信

2.Kafka文件存储机制