Kafka入门

91 阅读7分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。

名词解释

  1. Broker:消息中间件处理结点,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群。
  2. Topic:一类消息,例如page view日志、click日志等都可以以topic的形式存在,Kafka集群能够同时负责多个topic的分发。
  3. Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。
  4. Segment:partition物理上由多个segment组成,下面2.2和2.3有详细说明。
  5. offset:每个partition都由一系列有序的、不可变的消息组成,这些消息被连续的追加到partition中。partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息.

Segment的Index <--> DataFile对应关系物理结构

![IMAGE](quiver-image-url/11A89C3E64EB36FBC47E030191685C5B.jpg =552x330)

Kafka Consumer Api样例

Consumer Offset确认

  1. 自动确认Offset(enable.auto.commit=true)

  2. 手工确认Offset(enable.auto.commit=false)

  3. 分区订阅

    向特定Partition订阅消息。会失去partition的负载分担

  4. 外部存储

    消费者自己定义offset存储位置,目的是让消费者将数据和offset进行原子性的存储。避免重复消费问题。(enable.auto.commit=false; seek(TopicPartition, long);//重置)

  5. 控制消费位置(跳跃性数据消费)

    如时效性数据,只需要获取最近一段时间内的数据

  6. 控制消费流Consumption Flow Control

    一个consumer同时消费多个分区,默认情况这多个分区的优先级是一样的。

  7. 多线程处理模型 Multi-threaded Processing

    Kafka的Consumer的接口为非线程安全的。多线程共用IO,Consumer线程需要自己做好线程同步。

Consumer单线程、多线程模型优缺点

Consumer单线程模型

  • 优点:
    • 实现容易;
    • 没有线程之间的协作。通常比下面的那种更快;
    • 单分区数据的顺序处理;
  • 缺点:
    • 多个TCP连接,但是关系不大,kafka对自己的server自信满满;
    • 太多的Request可能导致server的吞吐降低一丢丢;
    • consumer数量受到分区数量限制,一个consumer一个分区;

Consumer多线程模型

  • 优点:
    • 一个consumer任意多的线程,线程数不用受到分区数限制;
  • 缺点:
    • 如果有保序需求,自己要加控制逻辑;
    • 该模型中如果手动offset,自己要加控制逻辑;
  • 一种可行的解决办法:为每个分区分配独立的存储,获取的数据根据数据所在分区进行hash存储。这样可以解决顺序消费,和offset的确认问题。

server.properties

apache kafka系列之server.properties配置文件参数说明

参数说明
broker.id=0每个Broker在集群的唯一标识,要求正数。服务器ip变化时,broker.id没有变化不影响consumers的消息情况
log.dirs=/data/kafka-logskafka数据的存放地址,多个地址用逗号隔开,分配到不同磁盘上可提高读写性能
port=9092broker server服务端口
message.max.bytes=6525000表示消息体的最大大小,单位是字节
num.network.threads=4broker处理消息的最大线程数,一般情况下数量为cpu核数
num.io.threads=8broker处理磁盘IO的线程数,数值为cpu核数2倍
background.threads=4一些后台任务处理的线程数,例如过期消息文件的删除等,一般情况下不需要去做修改
queued.max.requests=500等待IO线程处理的请求队列最大数,若是等待IO的请求超过这个数值,那么会停止接受外部消息,应该是一种自我保护机制
host.namebroker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK,一般不设置
socket.send.buffer.bytes=100*1024socket的发送缓冲区,socket的调优参数SO_SNDBUFF
socket.receive.buffer.bytes=100*1024socket的接受缓冲区,socket的调优参数SO_RCVBUFF
socket.request.max.bytes=10010241024socket请求的最大数值,防止serverOOM,message.max.bytes必然要小于socket.request.max.bytes,会被topic创建时的指定参数覆盖
log.segment.bytes=102410241024topic的分区是以一堆segment文件存储的,这个控制每个segment的大小,会被topic创建时的指定参数覆盖
log.roll.hours=24*7这个参数会在日志segment没有达到log.segment.bytes设置的大小,也会强制新建一个segment会被 topic创建时的指定参数覆盖
log.cleanup.policy=delete日志清理策略选择有:delete和compact主要针对过期数据的处理,或是日志文件达到限制的额度,会被 topic创建时的指定参数覆盖
log.retention.minutes=300 / log.retention.hours=24数据文件保留多长时间,存储的最大时间超过这个时间会根据log.cleanup.policy设置数据清除策略log.retention.bytes和log.retention.minutes或log.retention.hours任意一个达到要求,都会执行删除。有2删除数据文件方式:1)按照文件大小删除:log.retention.bytes 2)按照2中不同时间粒度删除:分别为分钟,小时
log.retention.bytes=-1topic每个分区的最大文件大小,一个topic的大小限制 = 分区数*log.retention.bytes。-1没有大小限log.retention.bytes和log.retention.minutes任意一个达到要求,都会执行删除,会被topic创建时的指定参数覆盖
log.retention.check.interval.ms=5minutes文件大小检查的周期时间,是否触发log.cleanup.policy中设置的策略
log.cleaner.enable=false是否开启日志压缩
log.cleaner.threads=2日志压缩运行的线程数
......
......
......
......

Producer端配置

参数说明
compression.type=nonegzip。默认发送不进行压缩,推荐配置一种适合的压缩算法,可以大幅度的减缓网络压力和Broker的存储压力
buffer.memory=33554432Producer用来存放尚未发送出去的Message的缓冲区大小。缓冲区满了之后可以选择阻塞发送或抛出异常,由block.on.buffer.full的配置来决定。
linger.ms=0Producer默认会把两次发送时间间隔内收集到的所有Requests进行一次聚合然后再发送,以此提高吞吐量,而linger.ms则更进一步,这个参数为每次发送增加一些delay,以此来聚合更多的Message。
batch.size=16384Producer会尝试去把发往同一个Partition的多个Requests进行合并,batch.size指明了一次Batch合并后Requests总大小的上限。如果这个值设置的太小,可能会导致所有的Request都不进行Batch。
acks=1这个配置可以设定发送消息后是否需要Broker端返回确认。0—不需要进行确认,速度最快。存在丢失数据的风险。1-仅需要Leader进行确认,不需要ISR进行确认。是一种效率和安全折中的方式。 all-需要ISR中所有的Replica给予接收确认,速度最慢,安全性最高,但是由于ISR可能会缩小到仅包含一个Replica,所以设置参数为all并不能一定避免数据丢失。
......

Consumer端配置

参数说明
num.consumer.fetchers=1启动Consumer的个数,适当增加可以提高并发度。
fetch.min.bytes=1每次Fetch Request至少要拿到多少字节的数据才可以返回。
fetch.wait.max.ms:100在Fetch Request获取的数据至少达到fetch.min.bytes之前,允许等待的最大时长。对应上面说到的Purgatory中请求的超时时间。
......

其他

  1. Kafka Consumer手动执行commit时实际上是对这个Consumer进程所占用的Partition进行commit
  2. Kafka 0.9.*版本中重写了Consumer API,Consumer维护了消费者当前状态,不是线程安全的
  3. 新的Consumer基于单线程模型,offset自动提交在poll方法中进行,0.9~0.10.0.1客户端的心跳也是在poll方法中进行,在0.10.1.0版本中,客户端心跳在后台异步发送
  4. 0.10.0.1版本中可以通过在consumer的props中设置max.poll.records来限制每回返回的最大数据条数
  5. 广播模式,相同的topic不同的group。如果相同的topic多个监听里相同的group,会造成只有一个监听能处理其中的消息

Command

  1. 增加分区

./bin/kafka-topics.sh --alter --topic eng_gds --zookeeper 127.0.0.1:2181 --partitions 8

  1. 分区迁移

一些问题

  1. session.timeout.ms时间内没有处理完,broker会认为Consumer挂掉

解决方案:1.提高partition数量,从而提高consumer的并行能力。2.降低一次获取数据量

参考