Kafka

Kafka 有哪些特点

  • 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition,通过consumer group 对partition进行consumer操作。
  • 可扩展性:kafka集群支持热扩展
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
  • 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
  • 高并发:支持数千个客户端同时读写

Kafka的应用场景

  • 日志收集:一个公司可以用Kafka收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、HBase、Solr等。
  • 消息系统:解耦生产者和消费者、缓存消息等。
  • 用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。
  • 运营指标:Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。
  • 流式处理:比如spark streaming和 Flink

Kafka的集群安装

  1. 启动zookeeper集群

  2. 进入到config目录下修改server.properties

    broker.id=1
    listeners=PLAINTEXT://192.168.11.140:9092
    zookeeper.connect = 192.168.11.140:2181, 192.168.11.139:2181, 192.168.11.138:2181
    
  3. 启动

    sh kafka-server-start.sh -daemon ../config/server.properties
    sh kafka-server-stop.sh
    
  4. Kafka常用命令

    ./kafka-server-start.sh   ../config/server.properties    //kafka服务启动
    ./kafka-topics.sh --list --zookeeper zk服务IP:2181    //查看有哪些主题
    ./kafka-topics.sh --create --zookeeper zk服务IP:2181 --replication-factor 1 --partitions 1 --topic WordCount    //创建topic
    ./kafka-topics.sh -zookeeper zk服务IP:2181 -describe -topic WordCount    //查看topic的详细信息
    ./kafka-console-producer.sh --broker-list kafka服务IP:9092 --topic WordCount    //生产者客户端命令
    ./kafka-console-consumer.sh -zookeeper zk服务IP:2181 --from-beginning --topic WordCount    //消费者客户端命令
    ./kafka-topics.sh --zookeeper zk服务IP:2181 --delete --topic WordCount    //删除topic    (注:不能真正删除topic只是把这个topic标记为删除(marked for deletion),要彻底把topic删除必须把kafka中与当前topic相关的数据目录和zookeeper中与当前topic相关的路径一并删除。)
    

Kafka的架构

Kafka 架构分为以下几个部分

  • Producer :消息生产者,就是向 kafka broker 发消息的客户端。
  • Consumer :消息消费者,向 kafka broker 取消息的客户端。
  • Topic :可以理解为一个队列,一个 Topic 又分为一个或多个分区。
  • Consumer Group:是 kafka 用来实现一个 topic 消息的广播(发给所有的 consumer)和单播(发给任意一个 consumer)的手段。一个 topic 可以有多个 Consumer Group。
  • Broker :一台 kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic。
  • Partition:
    • 为了实现扩展性,一个非常大的 topic 可以分布到多个 partition上,每个 partition 是一个有序的队列。
    • partition 中的每条消息都会被分配一个有序的id(offset)。
    • 将消息发给 consumer,kafka 只保证单个 partition 中的消息的顺序,不保证一个 topic 的整体(多个 partition 间)的顺序。
  • Segment:partition物理上由多个segment组成。
  • offset:
    • 每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。
    • partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息。
    • kafka 的存储文件都是按照 offset.kafka 来命名,用 offset 做名字的好处是方便查找。
    • 例如你想找位于 2049 的位置,只要找到 2048.kafka 的文件即可。当然 the first offset 就是 00000000000.kafka。
  • Message: 消息是kafka中最基本的数据单元。消息由一串字节构成,其中主要由key和value构成,key和value也都是byte数组。key的主要作用是根据一定的策略,将消息路由到指定的分区中,这样就可以保证包含同一key的消息全部写入到同一个分区中,key可以是null。

Kafka在zookeeper的存储结果

消息可靠性机制

消息发送端

  • Producer异步发送
    (1) 设置 producer.type=async
    (2) 优点是可批量发送消息(消息个数达到 batch.num.messages=200 或 时间达到 linger.ms “间隔时间")、吞吐量佳
    (3) 缺点是发送不及时可能导致丢失;

  • Producer同步发送
    (1) 设置 producer.type=sync
    (2) 三种确认方式

    • acks = 0: producer不会等待broker(leader)发送ack 。因为发送消息网络超时或broker crash,既有可能丢失也可能会重发
      (1.Partition的Leader还没有commit消息 2.Leader与Follower数据不同步)

    • acks = 1: (默认值) 当leader接收到消息之后发送ack。
      (之后 Leader 向 Followers 同步时,如果 Leader 宕机会导致消息没同步而丢失,producer 却依旧认为成功)

    • acks=all/-1: producer 等待 Leader 写入本地日志、而且 Leader 向 Followers 同步完成后才会确认;最可靠。

  • Producer为消息选择分区
    (1) 当key为空时,消息随机发送到某个分区
    (2) 用key的hash值对partition个数取模,决定要把消息发送到哪个partition上

消息接收端

  • consumer存在两种消费模型

    • push:优势在于消息实时性高。劣势在于没有考虑consumer消费能力和饱和情况,容易导致producer压垮consumer。
    • pull:优势在可以控制消费速度和消费数量,保证consumer不会出现饱和。劣势在于当没有数据,会出现空轮询,消耗cpu。
  • kafka采用pull

  • Kafka Consumer 有两个接口

    • Low-level API: 消费者自己维护 offset 等值,可以完全控制;
    • High-level API: 封装了对 parition 和 offset 的管理,使用简单;可能遇到 Consumer 取出消息并更新了 offset,但未处理消息即宕机,从而相当于消息丢失;
  • Kafka 支持 3 种消息传递语义

    • 消息最多消费一次(at most once):consumer先读取消息,再确认offset,最后处理消息(消息可能会丢失,但永远不会重新发送)
    • 消息至少消费一次(at least once):consumer先读取消息,再处理消息,最后确认offset(消息永远不会丢失,但可能会重新传递) —> 首选
    • 恰恰一次(exactly once): 消息只会发送一次。kafka中并没有严格的去实现(基于2阶段提交),我们认为这种策略在kafka中是没有必要的
  • 消息的消费原理

    • 之前Kafka存在的一个非常大的性能隐患就是利用ZK来记录各个Consumer Group的消费进度(offset)。当然JVM Client帮我们自动做了这些事情,但是Consumer需要和ZK频繁交互,而利用ZK Client API对ZK频繁写入是一个低效的操作,并且从水平扩展性上来讲也存在问题。所以ZK抖一抖,集群吞吐量就跟着一起抖,严重的时候简直抖的停不下来。
    • 新版Kafka已推荐将consumer的位移信息保存在Kafka内部的topic中,即__consumer_offsets内置topic中。通过以下操作来看看__consumer_offsets内置topic是怎么存储消费进度的,__consumer_offsets内置topic默认有50个分区
      1. 计算consumer group对应的hash值,结果放在了15分区上

        Math.abs(“my_consumer_offsets_group".hashCode()) % 50
        
      2. 获得consumer group的位移信息

        bin/kafka-simple-consumer-shell.sh --topic __consumer_offsets --partition 15 -broker-list 192.168.11.140:9092,192.168.11.141:9092,192.168.11.138:9092 --formatter kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter
        

Kafka消息存储

Topic & Partition

同一个Topic 通常存储的是一类消息,每个topic内部实现又被分成多个partition,每个partition在存储层面是append log文件。

在Kafka文件存储中,同一个topic下有多个不同partition,每个partition为一个目录,partiton命名规则为topic名称+有序序号,第一个partiton序号从0开始,序号最大值为partitions数量减1。

  • 每个partion(目录)相当于一个巨型文件被平均分配到多个大小相等segment(段)数据文件中。但每个段segment file消息数量不一定相等,这种特性方便old segment file快速被删除。

  • 每个partiton只需要支持顺序读写就行了,segment文件生命周期由服务端配置参数决定。这样做的好处就是能快速删除无用文件,有效提高磁盘利用率。

  • segment file组成:由2大部分组成,分别为index file和data file,此2个文件一一对应,成对出现,后缀”.index”和“.log”分别表示为segment索引文件、数据文件。

  • segment文件命名规则:partion全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充。

  • segment中index与data file对应关系物理结构如下:

    • 上图中索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址。
    • 其中以索引文件中元数据3,497为例,依次在数据文件中表示第3个message(在全局partiton表示第368772个message),以及该消息的物理偏移地址为497。
  • 了解到segment data file由许多message组成,下面详细说明message物理结构如下:

  • 查看kafka数据文件内容
    在使用kafka的过程中有时候需要我们查看产生的消息信息,这些都被记录在kafka的log文件中。由于log文件的特殊格式,需要通过kafka提供的工具来查看

    ./bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files /tmp/kafka-logs/*/000**.log  --print-data-log {查看消息内容}
    

副本(replication)策略

  • 数据同步
    (1) kafka在0.8版本前没有提供Partition的Replication机制,一旦Broker宕机,其上的所有Partition就都无法提供服务,而Partition又没有备份数据,数据的可用性就大大降低了。所以0.8后提供了Replication机制来保证Broker的failover。
    (2) 引入Replication之后,同一个Partition可能会有多个Replica,而这时需要在这些Replication之间选出一个Leader,Producer和Consumer只与这个Leader交互,其它Replica作为Follower从Leader中复制数据。

  • 副本放置策略

    • 为了更好的做负载均衡,Kafka尽量将所有的Partition均匀分配到整个集群上。Kafka分配Replica的算法如下:
      (1) 将所有存活的N个Brokers和待分配的Partition排序
      (2) 将第i个Partition分配到第(i mod n)个Broker上,这个Partition的第一个Replica存在于这个分配的Broker上,并且会作为partition的优先副本
      (3) 将第i个Partition的第j个Replica分配到第((i + j) mod n)个Broker上

    • 假设集群一共有4个brokers,一个topic有4个partition,每个Partition有3个副本。下图是每个Broker上的副本分配情况。

  • 副本同步策略

    • ISR(副本同步队列)
      维护的是有资格的follower节点

      • 副本的所有节点都必须要和zookeeper保持连接状态
      • 副本的最后一条消息的offset和leader副本的最后一条消息的offset之间的差值不能超过指定的阀值。
        这个阀值是可以设置的(replica.lag.max.messages)
      • 副本同步时间限制(replica.lag.time.max.ms)
        假如设置500毫秒,只要 follower副本每隔500毫秒或更早地向 leader 副本发送一个 fetch 请求,它们就不会被标记为死亡并且不会从 ISR 中删除。
    • LEO(Log End Offset)
      LEO 是所有副本都会有的一个offset标记,它指向追加到当前副本的最后一个消息的offset。当生产者向leader副本追加消息的时候,leader副本的LEO标记就会递增;当follower副本成功从leader副本拉取消息并更新到本地的时候,follower副本的LEO就会增加。

    • HW(HighWatermark)
      (1) HW标记了一个特殊的offset,当消费者处理消息的时候,只能拉去到HW之前的消息,HW之后的消息对消费者来说是不可见的。
      (2) 也就是说,取partition对应ISR中最小的LEO作为HW,consumer最多只能消费到HW所在的位置。
      (3) 每个replica都有HW,leader和follower各自维护更新自己的HW的状态。
      (4) 对于leader新写入的消息,consumer不能立刻消费,leader会等待该消息被所有ISR中的replicas同步更新HW,此时消息才能被consumer消费。
      (5) 这样就保证了如果leader副本损坏,该消息仍然可以从新选举的leader中获取。

    • 同步过程
      (1) Producer在发布消息到某个Partition时,先通过ZooKeeper找到该Partition的Leader,然后无论该Topic的Replication Factor为多少,Producer只将该消息发送到该Partition的Leader。Leader会将该消息写入其本地Log。每个Follower都从Leader pull数据。这种方式上,Follower存储的数据顺序与Leader保持一致。Follower在收到该消息并写入其Log后,向Leader发送ACK。一旦Leader收到了ISR中的所有Replica的ACK,该消息就被认为已经commit了,Leader将增加HW并且向Producer发送ACK。

      (2) 为了提高性能,每个Follower在接收到数据后就立马向Leader发送ACK,而非等到数据写入Log中。因此,对于已经commit的消息,Kafka只能保证它被存于多个Replica的内存中,而不能保证它们被持久化到磁盘中,也就不能完全保证异常发生后该条消息一定能被Consumer消费。

      (3) Consumer读消息也是从Leader读取,只有被commit过的消息才会暴露给Consumer。

      (4) Leader会跟踪与其保持同步的Replica列表,该列表称为ISR(即in-sync Replica)。如果一个Follower宕机,或者落后太多,Leader将把它从ISR中移除。这里所描述的“落后太多”指Follower复制的消息落后于Leader后的条数超过预定值或者Follower超过一定时间未向Leader发送fetch请求。

      (5) Kafka只解决fail/recover,一条消息只有被ISR里的所有Follower都从Leader复制过去才会被认为已提交。这样就避免了部分数据被写进了Leader,还没来得及被任何Follower复制就宕机了,而造成数据丢失(Consumer无法消费这些数据)。而对于Producer而言,它可以选择是否等待消息commit。这种机制确保了只要ISR有一个或以上的Follower同步了被commit的消息就不会丢失。

    • leader选举

      • Leader选举本质上是一个分布式锁,有两种方式实现基于ZooKeeper的分布式锁
        (1) 节点名称唯一性:多个客户端创建一个节点,只有成功创建节点的客户端才能获得锁
        (2) 临时顺序节点:所有客户端在某个目录下创建自己的临时顺序节点,只有序号最小的才获得锁
      • 控制器(Broker)选举
        在多个broker中选举出一个leader来管理其他broker
        应用上面第一种策略
      • 分区副本选举
        也是应用上面第一种策略
        如何处理所有的Replica不工作的情况?
        • 在ISR中至少有一个follower时,Kafka可以确保已经commit的数据不丢失,但如果某个Partition的所有Replica都宕机了,就无法保证数据不丢失
          (1) 等待ISR中的任意一个Replica“活”过来,并且选它作为Leader
          (2) 选择第一个“活”过来的Replica(不一定是ISR中的)作为Leader

        • 这就需要在可用性和一致性当中作出一个简单的折衷。
          (1) 如果一定要等待ISR中的Replica“活”过来,那不可用的时间就可能会相对较长。而且如果ISR中的所有Replica都无法“活”过来了,或者数据都丢失了,这个Partition将永远不可用。
          (2) 选择第一个“活”过来的Replica作为Leader,而这个Replica如果不是ISR中的Replica,那即使它并不保证已经包含了所有已commit的消息,它也会成为Leader而作为consumer的数据源(前文有说明,所有读写都由Leader完成)。
          (3) Kafka0.8.*使用了第二种方式。Kafka支持用户通过配置选择这两种方式中的一种,从而根据不同的使用场景选择高可用性还是强一致性

kafka高吞吐量的原因

  • 顺序写的方式存储数据
    每条消息都被append到该Partition中,属于顺序写磁盘,因此效率非常高。对于传统的message queue而言,一般会删除已经被消费的消息,而Kafka是不会删除数据的,它会把所有的数据都保留下来,每个消费者(Consumer)对每个Topic都有一个offset用来表示读取到了第几条数据。

  • 批量发送;在异步发送模式中。kafka允许进行批量发送,也就是先将消息缓存到内存中,然后一次请求批量发送出去。这样减少了磁盘频繁io以及网络IO造成的性能瓶颈

    batch.size //每批次发送的数据大小    
    linger.ms  //间隔时间
    
  • 零拷贝

    • 消息从发送到落地保存,broker维护的消息日志本身就是文件目录,每个文件都是二进制保存,生产者和消费者使用相同的格式来处理。在消费者获取消息时,服务器先从硬盘读取数据到内存,然后把内存中的数据原封不动的通过socket发送给消费者。虽然这个操作描述起来很简单,但实际上经历了很多步骤:

      (1) 操作系统将数据从磁盘读入到内核空间的页缓存
      (2) 应用程序将数据从内核空间读入到用户空间缓存中
      (3) 应用程序将数据写回到内核空间到socket缓存中
      (4) 操作系统将数据从socket缓冲区复制到网卡缓冲区,以便将数据经网络发出

    • 通过“零拷贝”技术可以去掉这些没必要的数据复制操作,同时也会减少上下文切换次数

kafka的分区分配策略

  • 同一个consumer group里面的consumer怎么去分配应该消费哪个分区里的数据?如下有两种分区策略。

    • Range 范围分区(默认的)
      假如有11个分区,3个消费者,把分区按照序号排列0,1,2,3,4,5,6,7,8,9,10;消费者为C1,C2,C3,那么用分区数除以消费者数来决定每个Consumer消费几个Partition,除不尽的前面几个消费者将会多消费一个。

      C1:0,1,2,3
      C2:4,5,6,7
      C3:8,9,10

    • RoundRobin 轮询分区

      • 把所有的partition和consumer列出来,然后轮询consumer和partition,尽可能的把partition均匀的分配给consumer

      • 案例
        假如有3个Topic T0(三个分区P0-0,P0-1,P0-2),T1(两个分区P1-0,P1-1),T2(四个分区P2-0,P2-1,P2-2,P2-3)
        有三个消费者:C0(订阅了T0,T1),C1(订阅了T1,T2),C2(订阅了T0,T2)

        那么分区过程如下图所示

        分区将会按照一定的顺序排列起来,消费者将会组成一个环状的结构,然后开始轮询。

      • 过程
        P0-0分配给C0,
        P0-1分配给C1但是C1并没订阅T0,于是跳过C1把P0-1分配给C2,
        P0-2分配给C0,
        P1-0分配给C1,
        P1-1分配给C0,
        P2-0分配给C1,
        P2-1分配给C2,
        P2-2分配给C1,
        P2-3分配给C2

      • 结果
        C0: P0-0,P0-2,P1-1
        C1:P1-0,P2-0,P2-2
        C2:P0-1,P2-1,P2-3

  • consumer rebalance(Kafka 的再均衡)

    • kafka提供了一个角色Coordinator来执行。当Consumer Group的第一个Consumer启动的时候,他会向kafka集群中的任意一台broker发送GroupCoordinatorRequest请求,broker会返回一个负载最小的broker设置为coordinator,之后该group的所有成员都会和coordinator进行协调通信
    • 整个Rebalance分为两个过程 joinGroup和sysncJoin
      • joinGroup过程
        在这一步中,所有的成员都会向coordinator发送JionGroup请求,请求内容包括group_id,member_id.protocol_metadata等,coordinator会从中选出一个consumer作为leader,并且把组成员信息和订阅消息,leader信息,rebanlance的版本信息发送给consumer
      • Synchronizing Group State阶段
        组成员向coordinator发送SysnGroupRequet请求,但是只有leader会发送分区分配的方案(分区分配的方案其实是由消费者确定的),当coordinator收到leader发送的分区分配方案后,会通过SysnGroupResponse把方案同步到各个consumer中

日志策略

  • 日志保留策略
    无论消费者是否已经消费了消息,kafka都会一直保存这些消息,但并不会像数据库那样长期保存。为了避免磁盘被占满,kafka会配置相应的保留策略(retention policy),以实现周期性地删除陈旧的消息

    • 根据消息保留的时间,当消息在kafka中保存的时间超过了指定时间,就可以被删除
    • 根据topic存储的数据大小,当topic所占的日志文件大小大于一个阀值,则可以开始删除最旧的消息
  • 日志压缩策略
    在很多场景中,消息的key与value的值之间的对应关系是不断变化的,就像数据库中的数据会不断被修改一样,消费者只关心key对应的最新的value。我们可以开启日志压缩功能,kafka定期将相同key的消息进行合并,只保留最新的value值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值