Kafka各个组件工作原理(读书笔记暂未完成)
一、producer
producer 的首要功能就是向某个 topic 的某个分区发送一条消息,所以它首先需要借助分区器(partitioner)确认到底要向 topic 的哪个分区写入消息 。
如果消息指定了key,那么partitioner会根据key的哈希值选择目标分区;否则,会使用轮询的方式确认目标分区,这样可以保证消息被均匀的写入所有分区。
确定了目标分区后,还需要确认目标分区的leader副本,也就是分区leader副本所在的broker。每个topic分区中只有一个副本充当leader,只有leader能响应clients的请求,其他副本中的一部分副本与leader副本保持同步,即ISR(in-sync Replica)。在发送消息时,producer会通过定义的acks参数判断如何发送消息。
也就是说,kafka是主写主读的

producer重要参数
bootstrap.servers
指定一组broker list,格式host:port,用于建立和kafka broker的连接。可以指定一组broker,用逗号分隔,如kafka1:9092,kafka2:9092,kakfa3:9092,最好不要只指定一台,防止failover。
acks
当producer发送一条消息到Kafka集群时,这条消息会被发送到指定topic的分区的leader副本所在的broker上,producer在一段时间内(超时时间)等待该broker返回消息的写入结果来确定消息被吸入成功。当返回成功结果后,producer发送下一条消息。Kafka会保证consumer永远不会读取到尚未提交成功的消息。
broker何时返回写入结果给producer直接影响到producer的吞吐量。producer通过acks参数来控制有多少分区副本接收消息,才算写入成功。有三个值:0、1、all。
acks=0:表示producer在消息成功写入前不等待任何服务器的响应。这样如果消息没有写入成功,生产者就无从得知,消息就丢了。但是这可以使producer达到最大吞吐量
acks=1:默认参数,表示只要集群中的首领副本所在broker收到消息,就返回消息写入成功的响应,无须等待 ISR中其他副本写入该消息。如果消息没有到达首领节点(比如leader节点崩溃,新首领没有被选举出来),producer会收到错误响应,这时producer会重发消息。如果一个没有收到消息的节点成为leader,还是会丢数据。
acks=all或-1:表示当发送消息时,首领broker将消息写入本地日志,同时等待ISR中的副本同时写入后,才返回成功的响应。这种模式是最安全的,就算有节点宕机,集群依然可用,但是延迟最高。
buffer.memory
该参数用来设置生产者内存缓冲区的大小,生产者用它缓冲要发送到服务器的消息 。由于消息的发送时异步的,producer会先将消息写入一块内存缓冲区,I/O线程(sender线程)从缓冲区中读取消息发送到broker。这部分内存缓冲区大小由buffer.memory指定,默认32MB。如果向缓冲区写消息的速度超过I/O线程发送的速度,那么producer会阻塞等待I/O线程赶上来。如果赶不上会抛出异常。
compression.type
producer端是否压缩消息。默认是none,不压缩。常用的压缩算法有gzip、snappy、lz4。gzip压缩比最高,但最耗CPU,带宽不足时使用。snappy压缩算法压缩比不如gzip,但是CPU占用低,压缩速度快。lz4的压缩比和压缩速度都比较居中。
retries
生产者有可能从服务器端收到的错误有可能是临时错误(比如找不到分区首领或者网络抖动),这时retries参数决定生产者尝试重复发送消息的次数,如果达到次数上限,生产者会放弃重试发送并返回错误。producer在两次重试之间会有时间间隔,由retry.backoff.ms指定。默认100ms
默认值0,表示不重试。生产中,应该设置此参数来避免瞬时错误。有两点需要注意:
(1)重试可能造成消息重复发送。比如由于网络波动导致broker端已经写入成功,但没有成功响应producer,producer会重发消息。在0.11.0.0版本开始支持“精确一次(exactly once)”,避免了此类问题。
(2)重试可能造成消息乱序。producer会将多个消息发送请求(默认5个)缓存在内存中,如果发生发送消息重试,肯能造成消息乱序。producer提供了max.in.flight.requests.per.connection 参数,改参数决定生产者在收到服务器响应之前可以发送多少个消息 。值设置的越高,占用的内存越过,吞吐量越大。设置为1,可以保证消息按照发送顺序写入服务器。
batch.size
该参数指定了一个批次可以使用的内存大小,按照字节数计算(而不是消息个数)。 当批次被填满,批次里的所有消息会被发送出去。不过生产者井不一定都会等到批次被填满才发送,半满的批次,甚至只包含一个消息的批次也有可能被发送。
linger.ms
控制消息发送延时行为。默认值为0,表示不等待批次填满,消息立即发送。设置成大于0的数,可以提升吞吐量。
max.request.size
控制生产者发送的请求大小。实际上控制的是producer发送的单个消息的最大值,也可以是整个批次消息的总大小。因为broker对可接受的消息最大值也有限制(message.max.bytes),所以这两个参数最好设置一致的值,防止broker拒绝接受producer发送的消息。
timeout.ms 、request.timeout.ms 和 metadata.fetch.timeout.ms
request.timeout.ms 指定了生产者在发送数据时等待服务器返回响应的时间, metadata.fetch.timeout.ms指定了生产者在获取元数据(比如目标分区的首领是谁)时等待服务器返回响应的时间。如果等待响应超时,那么生产者要么重试发送数据,要么返回一个错误(抛出异常或执行回调)。 timeout.ms 指定了 broker 等待同步副本返回消息确认的时间,与asks 的配置相匹配一一如果在指定时间内没有收到同步副本的确认,那么 broker 就会返回一个错误 。
max.block.ms
该参数指定了在调用 send () 方陆或使用 partitionsFor() 方能获取元数据时生产者的阻塞时间,当生产者的发送缓冲区已满,或者没有可用的元数据时,这些方屈就会阻塞。在阻
塞时间达到 max.block.ms 时,生产者会抛出超时异常。
Kafka Consumer
消费者和消费者组
消费者使用一个消费者组名(group.id)标识自己。topic的每条消息都只会被发送到每个订阅它的消费者组的一个消费者实例上。
- 一个consumer group可以包含多个consumer instance
- 一个topic只能发送到同一个消费者组的一个consumer实例上
- 一个topic的消费可以被发送到多个consumer group上
所以消费者组内的消费者数,不应大于消费的主题的分区数,否则,会有一部分消费者闲置
下图展示了消费组消费的场景

kafka的consumer group实现了对consumer的横向扩展,提供了高容错性。消费者组内的消费者实例可以同时读取kafka的消息,某个consumer故障时,consumer group会将故障的consumer负责的分区交给组内其他consumer处理,这个过程称为再均衡(Rebalance)。
提交和偏移量
偏移量(offset)
这里的offset是指consumer的offset,不是分区日志中的offset。消费者会将偏移量写入一个叫_consumer_offset的主题,消息里包含了每个分区的偏移量。Kafka不像其他JMS队列那样需要得到消费者的确认,这样做有几个优点:
- broker是无状态的,减少了同步成本,伸缩性增强
- 无需引入应答机制(acknowledgement)来确认消费成功
- 数据结构简单,不浪费资源
如果消费者一直处于运行状态,那么偏移量的作用不大。如果消费者崩溃或者有新的消费者加入群组,那么会触发再均衡(Rebalance),再均衡之后,消费者可能被分配到新的分区,为了能够继续处理,消费者会读取每个分区最后提交的偏移量,从偏移量对应的位置继续处理。
更新分区当前位置的操作叫作提交 。
如果提交的偏移量小于客户端处理的最后一个消息的偏移量 ,那么处于两个偏移量之间的消息会被重复处理。

如果提交的偏移量大于客户端处理的最后一个消息的偏移量,那么处于两个偏移量之间的消息将会丢失。

旧版本的kafka的consumer将偏移量提交到zookeeper中。新版的consumer偏移量由kafka的一个内部topic管理。
offset对consumer至关重要,因为它是实现消息交付语义保证( message delivery semantic ),常见的三种语义:
- 最多一次( at most once )处理语义:消息可能丢失,但不会被重复处理 。
- 最少一次( at least once )处理语义:消息不会丢失,但可能被处理多次。
- 精确一次( exactly once )处理语义:消息一定会被处理且只会被处理一次 。
消费者配置
bootstrap.servers
和producer的配置一样。注意:如果broker端没有显式配置listeners(或者advertised.listeners)使用IP地址,最好将此参数配置成主机名而不要使用IP地址,因为Kafka内部使用绝对域名FQDN(Fully Qualified Domain Name)。如果配置不一致,会出现无法获取元数据的错误
group.id
指定消费者组的组名,唯一标识一个consumer group。默认值为空,应配置成和业务相关的名字
key.deserializer
consumer 从 broker 端获取的任何消息都是字节数组,需要反序列化,由此参数指定。 可以自定义反序列化器,需要与producer端定义的serializer相对应。必须指定成类的完全限定名。
value.deserializer
改参数用来对消息体进行反序列化成原对象类型。必须指定成类的完全限定名。
fetch.min.bytes
指定消费者从服务器端获取的最小字节数,默认值1。如果consumer向broker请求数据时,broker的可用消息小于请求的fetch.min.bytes 大小,broker会等有足够数据再返回消息给consumer。这样可以降低broker负载。
- 当消费者数量较多,可以将该参数设置的大一些
- 当消费者的CPU使用率很高,但没有很多可用消息时,可以将此参数值调大
fetch.max.bytes
consumer单词获取数据的最大字节数。如果业务的单个消息很大,必须设置为一个比较大的值,否则consumer无法消费
max.poll.records
poll()方法单次返回的最大消息数。默认值500,如果consumer的瓶颈在于poll速度太慢,可以酌情增大此值。
heartbeat.interval.ms
两次心跳的时间间隔,心跳用于确保消费者的会话保持活动状态,并在新消费者加入或离开该组时再均衡(Rebalance)。该值必须设置为小于session.timeout.ms,但通常应设置为不大于该值的1/3。可以将其调整得更低,以控制正常重新平衡的预期时间。默认值3000ms
max.partition.fetch.bytes
broker服务器返回的每个分区的最大数据量,默认1MB。如果提取的第一个非空分区中的第一条消息大于此限制,则仍将返回该消息以确保使用者可用。broker接受的最大消息大小是通过message.max.bytes(server.properties)定义的。
session.timeout.ms
该属性指定了消费者在被认为死亡之前可以与服务器断开连接的时间,默认是 10s。如果消费者在规定时间内,没有发送心跳给群组协调器(coordinator),会被认为故障,则broker将把该消费者从组中删除并启动重新平衡。
auto.offset.reset
该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下(因消费者长时间失效,包含偏移量的记录已经过时井被删除)该作何处理 。默认值是latest,表示总最新的提交记录开始读取数据。另一个值是 earliest ,意思是说,在偏移量无效的情况下,消费者将从起始位置读取分区的记录。
enable.auto.commit
设置为false,防止出现重复数据和数据丢失。
connections.max.idle.ms
经常有用户抱怨在生产环境下周期性地观测到请求平均处理时间飙升,这很有可能是因为 Kafka 会定期地关闭空闲 Socket 连接导致下次 consumer 处理 请求时需要重新创建连向 broker 的 Socket 连接 。 默认值9分钟,可以将其设置为-1,即不关闭空闲连接。
posted on 2020-03-07 11:36 hopeless-dream 阅读(294) 评论(0) 收藏 举报
浙公网安备 33010602011771号