@TOC
1、简易架构图
2、设计理念简述
- 文件系统。可扩展性,可持久化,低成本的存储,不低的读写速度(读采用偏移量,写只追加)。
- 零拷贝技术。多消费者(不同消费组)场景下,重复消息能被复用
- 端到端的批量压缩。相同格式消息批量压缩能节省很大空间
- Producer
- 负载均衡。可自选策略,producer消息直达partition leader(所有的kafka broker都能提供leader存活情况和元数据)
- 异步发送。将消息缓存在内存中,当缓存达到定值或消息延迟达到定值(可配置)再发送,能减少网络IO次数,有效提高消息发送速率。
- Consumer
- Pull模式,根据Consumer的消费能力处理消息。若消息数量少,为避免紧密的空轮询,可调整长轮询相关参数。
- Partition保存消费偏移量,低成本且允许故意回退消费
- 支持离线消息加载
- static membership (kafka2.3+)避免不必要的rebalance
- kafka保障三种可能的消息传递语义
- At most once—Messages may be lost but are never redelivered.
- 若consumer先提交偏移量再处理消息,消息丢失则不能再处理,消息最多被消费一次。
- acks=0,producer不必等待partition写入成功
- At least once—Messages are never lost but may be redelivered.
- 若consumer先处理消息再提交偏移量,偏移量提交失败时会使得消息重复消费,消息最少被消费一次
- kafka0.11.0.0以前,producer在提交消息后未能收到响应(实际上已成功提交),会重发该消息,保证消息不丢失。
- acks=1,需等待partition leader写入成功并响应
- acks=-1或all,需等待所有partition写入成功并响应
- Exactly once—this is what people actually want, each message is delivered once and only once.
- kafka0.11.0.0及以后,producer支持幂等,为producer分配ID,为每一批次消息分配序号,删除重复消息;同时producer也支持向多主题发消息的事务操作。
- 也可在业务上实现幂等。
- producer发生异常时检查最后一条消息来决定重传或是跳过;或是在发送消息时生成唯一主键,consumer消费时进行判重。
- consumer关闭自动提交,由业务代码进行维护offset,在事务中同时更改offset和处理消息。
- At most once—Messages may be lost but are never redelivered.
- 副本,保障可用性。
- 判断partition是否为存活
- 保持和zookeeper的会话(通过zookeeper心跳机制)
- 若该partition为follower,在leader被写入时,follower需要在一定时间内同步(配置项为 replica.lag.time.max.ms)
- kafka只尝试恢复突然停止工作的partition,不处理由bug或犯规引起的partition故障。
- producer可使用参数acks来权衡延迟和持久性。
- kafka能保障在短暂的故障转移后的可用性,但网络分区时也无能为力
- 判断partition是否为存活
- 日志同步复制
- 在leader不可用时,应将日志最完整的follower挑选为leader。常见的方法是采用副本多数票选举法,但kafka采用的是动态地维护一组ISR。ISR相对于多数票选举法用一部分时间换取额外磁盘空间和吞吐量(多数票选举法写入后提交确认并不需要等待最慢的服务器)。(若需要容忍一次故障时,多数票选举法需要3个副本,一次确认;ISR需要2个副本,一次确认)
- 不幸地遇到所有ISR都不可用时,kafka有两种恢复策略
- 追求一致性:等待ISR中的某副本可用,并将其选为leader(kafka 0.11.0.0后地默认策略)
- 追求可用性:将第一个恢复可用的副本选为leader,无论它是ISR还是OSR(通过参数 unclean.leader.election.enable=true 可更改为此模式)
- 持久性与可用性保障
- kafka配置ack=-1也不能保证消息不丢失,它只能尽力保障可用性而不能百分百保证持久性(唯一存活的ISR,回复确认写入后死亡)。
- 若相对于可用性,您更倾向于消息持久性,kafka提供两个相关配置项
- 禁用unclean.leader.election.enable ,牺牲可用性来尽量减少消息丢失风险
- 设置min.insync.replicas数量,当acks=-1时,此配置生效,表示最少几个副本响应确认才能算写入成功。数字越大可用性越低,消息丢失风险越小。
- 副本管理
- kafka集群使用轮询方式将partition平衡分散在broker中,同时也将partion leader均匀的分散。
- kafka会选择一个broker成为控制器,它以broker等级来检测故障,并负责更改该broker所有受影响的partition的leader,因此kafka能批量处理partition变化,使leader选举成本更低速度更快(相比于各broker处理各自partition leader选举的方式)。当该控制器出故障时,kafka会选择一个存活的broker成为新的控制器。
- 日志清理
- log.cleaner.backoff.ms=15000(默认),检查日志是否需要清理的间隔时间,默认15秒
- kafka提供了两种日志清理策略,可同时使用
- log.cleanup.policy=delete(默认),先入先出,当日志大小达到一定容量,或数据段存留了一定时间后会开始删除
- log.retention.hours=168(默认),segment保留时间,默认为一周
- log.retention.bytes=-1(默认),日志最大容量,默认为无穷大
- log.cleanup.policy=compact,日志清理线程会遍历消息(第一遍记录各最新key hashcode所在的偏移量,第二遍标记需要删除的旧key),对重复的key进行删除,只保留最新的key-value
- log.cleaner.min.compaction.lag.ms最新写入的消息在一定时间内不会被标记删除
- log.cleaner.max.compaction.lag.ms消息在写入一定时间后一定可以被标记删除
- delete.retention.ms被标记删除的消息在一定时间(默认为24小时)内对consumer可见,随后完全删除
- 日志压缩后,存活下来的消息仍然相对有序
- 日志压缩后,存活下来的消息偏移量不变,若consumer要消费的偏移位置的消息已被删除,则顺延到该位置下一个存活消息进行消费
- log.cleanup.policy=delete(默认),先入先出,当日志大小达到一定容量,或数据段存留了一定时间后会开始删除
- 资源配额(限流)
- producer/consumer在消费大量数据或以高速率生产请求时,会垄断broker资源导致网络饱和,从而影响到其他客户端或broker。quota可以解决该问题,其在大型多租户系统集群中尤为重要。
- 配额粒度:(user,client-id),(user),(client-id)
- 可配置项
- producer_byte_rate 生产者每秒可发送到单broker字节数
- consumer_byte_rate 消费者每秒可从单broker拉取字节数
- 若资源超额,kafka会立即响应,响应时会附带延时时长,同时关闭与客户端的链接,不再接受客户请求直到延迟结束
- zookeeper配额配置文件优先级
- /config/users//clients/
- /config/users//clients/
- /config/users/
- /config/users//clients/
- /config/users//clients/
- /config/users/
- /config/clients/
- /config/clients/
3、优劣势
kafka优势及适用场景:
高吞吐量,低延迟: kafka基于文件系统,与基于内存的消息中间件最大的区别就是其速度和容量。文件系统所用的磁盘资源相对于内存来说是非常廉价的。因此相对于内存消息中间件它能存储更多的资源。为了弥补磁盘速度的劣势。kafka对其进行了多处优化,使其最低延迟也能达到毫秒级。
- 写操作只追加,不插入不删除。磁盘是采用指针寻址的方式,若采用随机读写会引起频繁的寻址,导致写入速度下降。kafka采用顺序写,保存读偏移量的方式来操作数据。为了防止日志文件过大,kafka也提供了两种策略来对日志文件瘦身。
- 零拷贝技术。从kafka的服务器读取数据时,不再经过用户态缓冲区,在内核态缓冲区完成数据拷贝,将磁盘和内存将系统上下文切换次数从4次减少为2次。
- 批量读写。kafka为了减少网络IO带来的时间开销,当消息总量达到一定大小或消息停留时间达到一定大小才发送。
- 分区分段+索引。kafka每个message都有自己的topic,topic又被分为多个partition存储,每个partition又分为多个segment存储,kafka为segment建立了索引,不仅提升了读消息的效率还提升了写消息的并行度。
- 批量压缩技术。对于kafka而言,磁盘资源很少成为系统性能的瓶颈,其瓶颈普遍体现在网络IO上,为了减少网络IO的带宽消耗及减少时延,kafka提供了多种压缩技术,如Gzip和Snappy
- 采用PageCache技术来加快读速度。
可扩展性 kafka支持热扩展。kafka集群由zookeeper来管理,可动态增加broker,但只会接收新topic的存储,不会改变旧topic的存储位置。一个消费组中的消费者数量只有小于topic的partition数才能有效扩展。 持久性,可靠性 kafka的消息被写在磁盘文件系统上,不会像内存一样重启旧消失,保证了持久性。每个partion拥有多个副本来跟leader节点进行同步,保证了可靠性。 可用性,分区容忍性 kafka采用ISR的方式来保证可用性,n个partition副本最多可有n-1个副本故障,全部故障时等待ISR恢复一个即可继续提供读写服务。 适用场景:
- 日志系统。kafka的研发初衷就是一个日志系统。其文件系统的大容量,顺序读写等特性非常适合日志这种大量消息的存储。
- 消息系统。kafka生产者和消费者的机制可满足发布/订阅模型。
- 数据采集分析。kafka可记录用户的行为活动信息或网站流量数据,将此大量离线数据提供给数据仓库来进行分析。
- 流处理应用。kafka支持流处理,从大量的数据中提取有价值的信息存储为新的主题。
- 待补充。。。
kafka劣势及注意事项:
非实时性 kafka为了减少网络时延和带宽消耗,采用批量读写的方式,读操作以pull的方式进行。因此消息需要达到一定条件才能发送,无法保证实时性。 不保证全局有序 kafka的topic被分为多个partition存储。其只能保证分区内消息有序。 消息可能丢失或重复 kafka可配置ack=-1并配置min.insync.replicas方式来保证有多少个副本复制成功才算消息写入成功,但这将牺牲一定的可用性,且可能因副本响应成功较慢导致消息重复。 注意事项:
- 消费者数量需小于topic的partition数量,等于最佳
- kafka使用文件存储,其以日志系统为初衷设计。若需要实时性较强或可靠性高的场景应选用其他消息中间件,而不是kafka
待补充。。。