Kafka学习
最近因为项目需要,了解了一下Kafka作为中间件的应用,之前一般只是单个小系统或者Web应用的开发,所以使用RabbitMQ就可以解决大多数问题了,但是这次项目是一个整体基础设施平台的搭建,对于吞吐量以及并发性要求较高,因而采用了Kafka作为技术选型。
之于Kafka和RabbitMQ之间的区别的话,网上相关的讨论有很多,在我看来的话主要是两者的偏重点不同,Kafka基于它的分区模型以及分布式复制机制提供了相较于RabbitMQ更好的吞吐量以及数据可靠性,而RabbitMQ基于它的Exchange与Queue之间用Key绑定的模型实现了更加灵活的路由机制,同时其所占用的资源和维护成本相较于Kafka更小,更适合于小型应用。
说了这么多,我们来看看Kafka一些比较基础的模型和用法吧。
首先,Kafka工作机制本质上是拉的,生产者产生消息推到broker,broker进行对应的分区计算存到对应的分区中,消费者首先订阅主题并和主题中部分分区进行绑定,然后基于主动轮询的方式从它绑定的分区获取最新消息(这些都被poll api给隐藏了起来),这里需要注意offset的手动或者自动commit来减少因为分区重平衡导致的重复消息,除了直接拿去最新消息,消费者也可以通过seek接口指定从哪个offset开始获取消息,因为Kafka会对于一定时间段或者一定大小内的消息进行存储,从而使消费者可以重复消费。
Kafka与其他MQ最大的不同点在于它每个topic有多个分区,每条消息都会round robin到一个对应的分区中。每个topic可以被多个消费者群组订阅,消费者群组之间是独立的,它们各自享有该topic所有的消息。每个消费者群组内部有多个消费者,每个消费者会和1个或多个分区绑定,该分区中的消息只会被对应绑定了的消费者消费,每个分区在一个消费者群组里只能有一个消费者绑定,所以消费者数量如果超过分区数量则多余的消费者无效,通过这种机制Kafka实现了消费者之间更好的并发消费,也实现了比其他MQ好的多的吞吐量(每个分区都可以在不同的broker上,从而在网络io上没有任何干扰,同时对于消费者,生产者以及Broker可以各自独立地进行伸缩)
消费者在调用poll的时候会做很多事情,包括群组协调,分区再均衡,发送心跳(在新版本中是单独的一个线程主动发送并通过heartbeat.interval.ms来控制对应的间隔,另外有session.timeout.ms来控制多久没收到心跳就表示消费者挂了,该值一般设置为心跳间隔的3倍)和获取数据,需要注意的是这里获取的数据是从所有和消费者绑定的分区中获得的,所以每一条record除了key value还会有它所在的分区,offset, topic等信息,同时,Kafka使用TopicPartitionState来记录分区相关的信息,其中有两个属性分别是position和committed,前一个用来指示下次拉取开始的offset,后一个表示当前提交了的offset(也即主动提交或自动提交更新的值)用来在分区重平衡的时候指示新的消费者从哪里开始读取,在第一次poll的时候会updateFetchPositions使用committed更新position,在此之后position会在每次poll的时候做更新,和committed并不相同
offset是指数据在队列中位置,而commit的offset是用来指示如果发生了分区重平衡以后,控制权发生了转移的分区应该从哪里开始被读,因此它往往是当前处理的最后一个offset+1,另外手动commit的情况下要注意需要commit绑定的所有分区的offset,自动commit往往是每个几秒进行commit,这会在分区重平衡(加入或减少消费者)的时候导致消息的重复消费,手动commit可以缓解这种情况但不能彻底根治,需要业务逻辑的介入才行,另外分区重平衡的时候可以在消费者处设置对应的Listener,其两个方法分别对应于失去分区控制权之前和获得分区控制权之后,也可以一定程度上缓解重复消费
在消费者机制上,除了消费者群组订阅主题,对于单消费者(一个消费者作为消费者群组)也可以采用主动assign分区的方式来进行订阅,但是在这种情况下消费者无法感知到分区的增加或者减少
在架构方面,Kafka使用ZooKeeper来作为分布式coordinator,ZooKeeper的价值在于它在本身是一个高可用分布式集群的情况基于paxos协议实现了客户端数据并发写的同步控制,比如一个3节点的ZooKeeper集群,客户端可能并发地去创建同一个临时节点来确定谁是首领,它们的请求可能发给了不同的节点但是最后有且只有一个broker能成功创建节点成为首领(ZooKeeper保证了这一点),这也就是ZooKeeper提供的一个高可用的分布式协调者角色,而在Kafka中ZooKeeper用来担任选举Kafka中的控制器的角色,控制器是用来决定各个分区的分区首领的,除此之外ZooKeeper还负责存储一些元数据和提供服务注册和发现,通过Watch机制用来发现Broker的加入和退出。
在数据可靠性方面,需要通过Broker,生产者,消费者三者的协调配合来共同实现数据可靠性。 Broker需要注意的点:
- 复制系数
- 不完全的首领选举
- 最少同步副本
生产者需要注意的点:
- 发送确认配置
- 重试参数配置
- 错误处理
消费者需要注意的点:
- 显示提交偏移量
- 提交频率
- 再均衡的处理
- 重试机制
- 幂等性实现
现如今,数据库开始变的像message queue,很多数据库比如DyanamoDB,开始提供Change Data Capture把对表的更改变成Data Stream供别的系统使用;另一方方面,message queue开始变的像一个可以持久化数据的数据库,当我们也有了可以从任意点replay的可以长期存储的Stream(kafka),Database和Message queue的概念开始变的模糊,而这也正是Kafka和其他MQ另外一点最大的不同,Kafka的出现,直接解决了replayable的数据框架的问题