概述
Kafka是Apache软件基金会旗下的子项目,是一个分布式的消息系统,它用简洁的设计实现了一个消息系统的功能。
首先,回顾一下消息系统的基本语义:
- Kafka中消息的载体是一个主题(Topic)
- 向Topic中发布消息的程序是消息生产者(Producer)
- 从Topic中消费消息的程序是消费者(Consumer)
生产者-Kafka集群(Topic)-和消费者之间通过网络传输数据,那么,Kafka消息系统的基本拓扑如下所示:
Kafka是用Java实现的,但客户端程序却不仅限于Java。
Topic和Log
首先来看一下Kafka的一个高级抽象概念 Topic.
一个Topic就是一个接受一类消息的通道,Kafka为Topic提供了基于日志的分区,架构如下图所示:
每个分区就是一个有序的、不可变的消息序列,在分区中的每条消息都有一个唯一的标示,这个标示成为消息的偏移量。
在一个基于配置的时间区间内,不管消息是否已经被消费,Kafka都会保存所有接收到的消息,例如配置消息的有效时间为两天,那么在两天之内,所有接收到的消息都可以重复消费,消息一旦过期,则会被清除以释放存储空间。
前面说过,每条消息在分区中都有一个唯一的Id,而重要的是Kafka只需要维护这个有序Id即可,并不会维护消费者的状态,消费状态(即消费偏移量OffSet)是由消费者维护,所以消费者可以任意决定消费哪个区间的数据,这使得服务端复杂度大大下降,有效的简化了消费模型,提高系统的性能。
分布式
每个分区在Kafka集群中都有若干个副本,这样持有副本的多个服务就可以同时处理客户端请求,同时,副本的数量是可配置的,副本机制为Kafka集群提供了容错能力。
在一个分区的多个副本中,有一个节点是leader,另外的节点都是fellower,leader负责数据的读写,fellower从leader中复制数据,如果leader挂了,会在fellower中重新选择一个拥有最新数据的节点作为leader。集群中的每个节点都有可能拥有两个角色-leader和fellower,这样Kafka集群就会有很好的负载平衡。
生产者(Producer)
生产者将消息发送到Server端的Topic,并且有其自行决定发往哪个分区。对于分区选择算法可以简单的进行随机选取,可以基于一个分区函数来进行分区,一般基于分区函数的方式更好。
消费者(Consumer)
通常情况下,在消息系统中发布消息有两种方式:一种基于队列,一种基于发布-订阅。在基于队列的方式下,消费者被一个消费者池同一管理,消费者从队列中顺序消费数据,并且,一条消息只能被其中一个消费者读取到。在发布-订阅模式下,消息被广播给所有的消费者。Kafka提供了一种单一的消费者抽象-消费者组(Consumer Group),它可以兼顾队列和发布-订阅两种模式。
每个消费者会被一个消费组(Consumer group)标识,被发布到Topic中的消息会被属于同一个消费组的多个消费者中的一个消费,一个消费组中的消费者可以是不同的程序或者在不同的物理机器上运行。
基于上面的设计,有意思的来了。
当所有消费者属于同一个消费组时,就是基于队列的消息系统的实现,当所有消费者都属于不同的消费组时,就是基于发布-订阅的消息系统的实现。
通常情况下,会有少量的消费组同时指向一个Topic,每一个消费组被称为逻辑订阅者,为了容错和稳定性,每个消费组会用于一个以上的消费组
一箭双雕,多么简洁漂亮的设计!
与传统的消息系统相比,Kafka在消息的有序性上做的更好。
传统的消息系统在服务端维护消息的顺序,当多个消费者从队列中消费数数据时,消息会已接收的顺序发送给消费者,但问题在于,虽然服务端发送消息的有序的,但是异步发送给消费者,所以消费者接收到的消息的顺序很可能与服务端的发送顺序不一致,这意味着消息的顺序信息在消费者层面已经完全丢失,通常解决这个问题的办法是只允许一个线程从服务端消费数据,但这样就失去了并行化的特性,系统性能会急剧下降。
而Kafak在这方面则做的更好,通过利用分区的概念,可以在多个消费组并发的情况下提供消息的有序性和负载平衡:将一个分区的消息只发给一个消费组,这样分区与消费组是一对一的关系,顺序性完全能够保证,同时因为有多个分区存在,还可以在多个消费组之间做到负载均衡。需要注意的是消费组的数量不能多于分区数,也就是说分区数就是最大并发消费数。
Kafka只能在一个分区内保证消息的顺序,而不能跨分区,如果要保证一个topic中的数据的强顺序,那么久只能为这个topic设置为一个分区,一个消费者。
Push & Pull
Kafka在Producer端向Broker端push数据,在Consumer端从Broker端pull数据。生产者push数据是所有消息系统遵照的标准,消费者采用pull来获取数据而不是由broker来push,这是更好的选择。
在broker想Consumer中push消息的模式下,其主要目的是要尽可能快的发送数据,但当消费者的消费能力小于发送速率时,就会造成网络阻塞、消费者向应缓慢甚至宕机等问题,所以有Consumer来pull数据是更好的选择,首先,这样可以保证Consumer正则运行的情况下尽可能快的处理数据,其次,在Broker端不要维护Consumer的状态,简化了Broker的设计。
文章来源:http://blog.csdn.net/zhangdong2012/article/details/53010869