Kafka产生背景和发展
产生背景
kafka的诞生,是为了解决linkedin的数据管道问题,起初linkedin采用了ActiveMQ来进行数据交换,大约是在2010年前后,那时的ActiveMQ还远远无法满足linkedin对数据传递系统的要求,经常由于各种缺陷而导致消息阻塞或者服务无法正常访问,为了能够解决这个问题,linkedin决定研发自己的消息传递系统,当时linkedin的首席架构师jay kreps便开始组织团队进行消息传递系统的研发,kafka由此诞生。 Kafka是最初由Linkedin公司开发,是一个分布式、分区的、多副本的、多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可以用于web/nginx日志、访问日志,消息服务等等,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
kafka的作用
一 削峰
在高并发环境下,系统的处理能力如Mysql的并发能力成为系统的短板,高并发情景将导致系统崩溃,利用kafka的高吞吐能力,将大量的请求先缓存到kafka中,再按系统能力拉取到系统处理
二解耦
使用kafka作为数据的管道,可以通过定义的统一的数据格式对外提供服务,而不对两边的服务形式进行规定,降低了系统的耦合性
三 异步通信
将信息写入kafka,并不立即去处理它,等需要再去处理它
Kafka强劲性能的来源
一 顺序读写
数据在磁盘顺序写入,机械磁盘可保证600M/s的写入速度,机械硬盘也可以满足要求
二 页缓存和零拷贝
kafka的零拷贝技术免除了应用层缓存和套接字缓存的开销,直接将数据写入网卡发送给消费者
三 分区并行
kafka可以并行消费数据,性能高
四 稀疏索引
kafka数据存储使用稀疏索引结构,可以快速定位数据,稀疏索引在kafka详细介绍
kafka的架构
kafka的架构特点(传统的架构)
kafka几个名词的介绍
- (1)Producer:消息生产者,就是向 Kafka broker 发消息的客户端。
- (2)Consumer:消息消费者,向 Kafka broker 取消息的客户端。
- (3)Consumer Group(CG):消费者组,由多个 consumer 组成。消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者。
- (4)Broker:一台 Kafka 服务器就是一个 broker。一个集群由多个 broker 组成。一个broker 可以容纳多个 topic。
- (5)Topic:可以理解为一个队列,生产者和消费者面向的都是一个 topic。
- (6)Partition:为了实现扩展性,一个非常大的 topic 可以分布到多个 broker(即服务器)上,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。
- (7)Replica:副本。一个 topic 的每个分区都有若干个副本,一个 Leader 和若干个Follower。
- (8)Leader:每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 Leader。
- (9)Follower:每个分区多个副本中的“从”,实时从 Leader 中同步数据,保持和Leader 数据的同步。Leader 发生故障时,某个 Follower 会成为新的 Leader。
kafka的生产流程
生产者发送数据流程
- 生产者生产信息,由RecordAccumulatorRecordAccumulator的线程多队列发送给Sender线程
- Sender 的数据达到设定的批次大小(batch size参数配置),数据发送到broker
几个重要的生产者配置参数
| 生产者几个重要参数 | |
|---|---|
| kbootstrap.servers | 生产者连接集群所需的 broker 地 址 清 单 。 例 如 |
| hadoop102:9092,hadoop103:9092,hadoop104:9092,可以 | |
| 设置 1 个或者多个,中间用逗号隔开。 | |
| key.serializer 和 value.serializer | key.serializer 和 value.serializer (一般为String格式) |
| buffer.memory | RecordAccumulator 缓冲区总大小,默认 32m |
| batch.size | 缓冲区一批数据最大值,默认 16k。适当增加该值,可以提高吞吐量,但是如果该值设置太大,会导致数据传输延迟增加。 |
| linger.ms | 如果数据迟迟未达到 batch.size,sender 等待 linger.time之后就会发送数据。单位 ms,默认值是 0ms,表示没有延迟。生产环境建议该值大小为 5-100ms 之间。 |
| acks | 0:生产者发送过来的数据,不需要等数据落盘应答。1:生产者发送过来的数据,Leader 收到数据后应答。-1(all):生产者发送过来的数据,Leader+和 isr 队列里面的所有节点收齐数据后应答。默认值是-1,-1和all 是等价的。 |
| max.in.flight.requests.per.connection | 允许最多没有返回 ack 的次数,默认为 5,开启幂等性要保证该值是 1-5 的数字。 |
| retry.backoff.ms | 两次重试之间的时间间隔,默认是 100ms。 |
| compression.type | 生产者发送的所有数据的压缩方式。默认是 none,也就是不压缩。支持压缩类型:none、gzip、snappy、lz4 和 zstd。 |
| max.in.flight.requests.per.connection | 保证分区内最新倒数几条数据有序有序,建议最大设置为5 |
几个生产者相关的总结
幂等性的解释
幂等性默认是开启的,但是幂等性用处可以防止分区内数据的重复,判断的标准是<PID, Partition, SeqNumber>,SeqNumber单调递增
ack机制的讨论
- ack=0,简单来说就是,producer发送一次就不再发送了,不管是否发送成功。
- ack=-1,简单来说就是,producer只有收到分区内所有副本的成功写入的通知才认为推送消息成功了。
- ack=1的情况下,producer只要收到分区leader成功写入的通知就会认为消息发送成功了。如果leader成功写入后,还没来得及把数据同步到follower节点就挂了,这时候消息就丢失了。
消费者消费数据的流程
消费者消费流程
分区从消费者组中选择消费者,由指定的消费者消费数据,一个分区只能由一个消费者组消费
关于分区的消费者的选择策略
kafka的Coordinator负责消费者分区的选择,具体的流程可以参考这篇文章,流程可以简化为
- 消费者多个消费者向Coordinator发送注册信息
- Coordinator决定leader和follower,所有consumer都往coordinator发送JoinGroup消息之后, coordinator会指定其中一个consumer作为leader,并把组成员信息以及订阅信息发给leader,其他consumer作为follower,然后由这个leader进行partition分配
- leader会将这个方案封装进SyncGroup请求中发给Coordinator,Coordinator给它返回null,follower发送 null的 SyncGroupRequest 给Coordinator,Coordinator回给它partition分配的结果
| 消费者参数 | |
|---|---|
| group.id | 标记消费者所属的消费者组 |
| enable.auto.commit | 默认值为 true,消费者会自动周期性地向服务器提交偏移量。 |
| auto.commit.interval.ms | 如果设置了 enable.auto.commit 的值为 true, 则该值定义了消费者偏移量向 Kafka 提交的频率,默认 5s。 |
| auto.offset.reset | 当 Kafka 中没有初始偏移量或当前偏移量在服务器中不存在(如,数据被删除了),该如何处理? earliest:自动重置偏移量到最早的偏移量。latest:默认,自动重置偏移量为最新的偏移量。 none:如果消费组原来的(previous)偏移量不存在,则向消费者抛异常。 anything:向消费者抛异常 |
| offsets.topic.num.partitions | __consumer_offsets 的分区数,默认是 50 个分区 |
| heartbeat.interval.ms | Kafka 消费者和 coordinator 之间的心跳时间,默认 3s。 该条目的值必须小于 session.timeout.ms ,也不应该高于session.timeout.ms 的 1/3。 |
| session.timeout.ms | Kafka 消费者和 coordinator 之间连接超时时间,默认 45s。超过该值,该消费者被移除,消费者组执行再平衡。 |
| max.poll.interval.ms | 消费者处理消息的最大时长,默认是 5 分钟。超过该值,该消费者被移除,消费者组执行再平衡。 |
| fetch.min.bytes | 默认 1 个字节。消费者获取服务器端一批消息最小的字节数。 |
| fetch.max.wait.ms | 默认 500ms。如果没有从服务器端获取到一批数据的最小字节数。该时间到,仍然会返回数据。 |
| max.poll.records | 一次 poll 拉取数据返回消息的最大条数,默认是 500 条。 |