生产者
负载均衡
生产者直接发送消息给分区leader,而不需要通过中间者进行转发。
这意味着生产者需要知道哪些服务器是存活的,以及主题分区leader在哪里的元数据请求。同时这也意味着生产者可以根据情况决定发给哪个broker,那么既可以随机负载,也可以进行散列
批量发送
生产者会尝试收集数据,然后在单次请求中发送大量数据,以牺牲少量额外延迟来换取更好的吞吐量。
消费者
push or pull?
kafka选择了pull。这是因为基于push的系统由broker控制着数据传输的速率,尽可能使消费者更快消费,但是如果消费者消费速度远低于生产速度,那这就成为一种拒绝服务攻击了。而消费者pull则能很好根据实际情况去pull消费,消费快就快pull,消费慢就慢pull
不过pull也会陷入轮询的陷阱。为了解决这种问题,kafka在pull request中设置参数使允许堵塞在长轮询中,直到数据到达
消费到哪里了?
确定消费到哪里了,并不是一个简单的问题。如果broker在发送消息后,立刻认为消息已经被消费了,那么在传输未到达等原因引发的消费未处理情况会使得这样的认为并不正确
如果采用消费者ack的模式,也就是消费者消费完成之后发送确定消息,那么如果消费者在发送ack的时候也失败了,这就会导致重复消费问题。此外,对每个消息进行状态维护也会对性能造成影响
为了避免这些问题,kafka将消息划分到有序的分区中,那么只要记录每个分区被每个消费者组消费数据即可。并且还可以支持回退到之前被消费的消息,以允许重复消费
那么kafka是如何解决重复消费的问题的呢?很可惜,kafka最多只能支撑生产者不发送重复的消息,如果是上述场景中消费者没有ack成功导致的重复消费,还是要进行额外幂等处理的
消息传递机制
- 至多一次:消息可能会丢失,并永远不会重新传递
- 至少一次:消息不会丢失,但可能被重新传递
- 只有一次:每个消息仅传递一次,既不会丢失,也不会重复
kafka支持生产者若没有收到ack,能重新发送消息,这样能保证至少一次的传递机制。并且还添加了(0.11.0.0之后)幂等,也就保证重发不会出现重复条目。
此外还提供了一个"事务",也就是支持发送消息到多个topic partitions的能力,要么都写入成功,要么都不成功
实现幂等的方法是为每个生产者分配id,并采用生产者发送的序列号去去重
从消费者角度来说,当消费者控制消息处理位置
-
读取消息后,更新已处理,最后处理消息。
这保证了至多一次。但如果消费者在消费失败后,反馈消费失败之前宕机,那么就会导致存在消息没有被消费
-
读取消息后,处理消息,最后更新已处理。
保证了至少一次。如果在消费成功之后宕机,那么将会重复收到已经处理过的消息
为了达成只有一次的情况,我们可以利用kafka事务功能,在事务被中止之后将消费者处理位置恢复到之前