【Kafka 简明原理】broker 如何处理用户的请求?

1,221 阅读5分钟

文章首发于公众号【大数据学徒】,搜索 dashujuxuetu 或文末扫码关注

本文总结了 broker 是如何处理用户的各种请求,包括常见的 Produce 请求、Fetch 请求、Metadata 请求,清晰易懂。

文章思维导图:

1. 请求处理概述

Kafka 在 TCP 之上有一套自己的二进制协议,定义了如何处理各种请求的规范,详细的协议规范可以参见 协议文档。Kafka 用户请求有一些公共字段,比如请求类型、协议版本、客户端 ID、请求 ID 等等。

broker 对于每个监听的端口会启动一个 acceptor 线程用来接收用户请求,然后有若干个 processor 和 handler 线程处理这些请求,processor 负责将请求放入一个请求队列,以及从响应队列中取出响应返回给用户,handler 是实际处理请求(读写各个 topic 的消息等)的线程

2. Metadata 请求

Produce 请求(来自生产者生产数据的请求)和 Fetch 请求(来自消费者消费数据和 follower 同步数据的请求)都是由每个分区副本的 leader 来处理的。如果用户把对于一个分区的请求发到了 follower 上,那么会得到一个“我不是 Leader”的错误,将请求发送 leader 这一点由客户端自己保证,怎么保证呢?用 metadata 请求。

metadata 请求中包含了若干个 topic 的名称,客户端可以向任意 broker 发送 metadata 请求,broker 会告诉客户端这些 topic 有哪些分区,分区有哪些副本,以及谁是副本 leader,客户端会缓存这些元信息。缓存有过期时间,对应可配置参数 metadata.max.age.ms,默认是 5 分钟,过期后,客户端会重新发送此请求更新元信息。此外,如果客户端收到了“我不是 Leader”这种错误,那么也会重新发送 metadata 请求,因为这个错误说明它的元信息已经过期。

为什么客户端访问 Kafka 时不需要指定全部的 broker,就是因为只要能连接上一个 broker,客户端就可以获取到所有需要访问的 broker 信息,所以叫 bootstrap-server。

3. Produce 请求

Produce 请求中会包含一个可配置参数 acks,表示需要多少个 broker 确认接收到这些消息才可以认为写入成功。这个参数有三个合法值:0,1 和 all,0 表示不确认,1 表示 leader 确认收到就行,all 表示必须 ISR 中的所有 broker 都确认才行,显然数据越多,数据的可靠性更强,其中 all 等同于-1。

当一个 leader 收到 produce 请求时,它会做以下判断:

  • 用户是否对应 topic 的写权限(开启认证和 ACL 机制之后)?
  • acks 参数的值是否合法,如果不是 0,1 或 all,则返回错误。
  • 如果 acks 被设置为 all,则需要判断 ISR 中是否有足够的 broker,如果没有则返回一个 NotEnoughReplicas 的错误,怎么判断是否足够呢?有一个参数叫做 min.insync.replicas ,表示 ISR 中 broker 数目所允许的最小值。如果有足够的 broker,则 leader 会先将请求中的消息写入磁盘,然后这个请求会被放在一个缓冲区中,等待所有 ISR中的 follower 确认已经同步了这个请求中的消息,最后向客户端返回写入成功的响应。

4. Fetch 请求

消费者和 follower 的 Fetch 请求内容大致类似于:请把这个 topic 的这些分区分别从这些 offset 开始的消息给我,非常直接,但指定的 offset 必须是存在的,否则会返回错误。

Fetch 请求分别可以指定两个参数: fetch.max.bytesfetch.min.bytes,用来限制 leader 返回消息的最大值和最小值,默认值分别是 50M 和 1B,限制最大值是防止撑爆客户端的缓存,限制最小值是避免消息不多浪费网络开销。对于这两个限制还有两个参数分别和它们有关系,对于最大值,有一个参数叫做 max.partition.fetch.bytes 用来限制单个分区所能返回的数据,默认值是 1M,对于最小值,有一个参数叫做 fetch.max.wait.ms,表示等待最大的时间,即使没有满足最小值的要求也要返回响应,默认值是 500ms。

leader 处理 Fetch 请求使用了“零拷贝”技术,即消息是直接从日志文件写到网络的,中间没有经过任何缓冲区,免去了很多拷贝和管理缓存区的开销,性能极高。

此外,如果 Fetch 请求中指定的 offset 只在 leader 上存在还没有同步到其它所有 ISR 中的 follower,leader 也不会返回这些消息而是返回一个空响应。这样做的原因是:如果这时允许客户端读取这些消息,如果这台 leader 突然挂掉,而一台没有这些消息的 follower 成为 leader,如果客户端就无法再消费这些消息,这种数据不一致的情况是一个消息系统要避免的。

5. 协议版本的问题

由于 Kafka 的协议一直在演进,很多协议格式有新老多个版本,这时候就涉及到版本兼容的问题。总的来说,在 0.10.0 之后的版本,Kafka 的网络协议是前后兼容的,即高版本的客户端可以和低版本的 broker 通信,低版本的客户端也可以和高版本的 broker 通信(高版本的迁就低版本的),0.10.0 之前的 broker 收到高版本的客户端请求可能会报错。因此,如果老版本(0.10.0 之前)的系统需要升级,先升级 broker,再升级客户端,会比较稳妥。

欢迎交流讨论,吐槽建议,分享收藏。

勤学似春起之苗,不见其增,日有所长 辍学如磨刀之石,不见其损,日有所亏 关注【大数据学徒】,用技术干货助你日有所长

大数据学徒