1. 架构图
一个Kafka集群中包含若干个Broker(消息实例),Kafka支持Broker横向扩展,Broker越多,吞吐量越大,同时也包含了若干个Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等)和若干个Consumer(消费者)以及一个zookeeper集群,Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
2. Topic和Partition
Topic:是一个逻辑的概念,它可以认为类似于其他中间件中queue的概念,作为一组消息的一个集合,跟其他的消息中间件一样,每个消息的发送或者是消费都必须要指定Topic,表明将消息存在哪个Topic中。一个Topic可以接受多个Producer发送的消息和被多个Consumer消费。
Partition:了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件,同意Topic下不同分区包含的消息是不同的,当每个消息发送到分区时,都会分配到一个offset(偏移量)它是消息在此分区中的唯一编号,kafka通过offset保证消息在分区内的顺序,offset的顺序不跨分区,即kafka只保证在同一个分区内的消息是有序的;Partition是以文件的形式存储在文件系统中,存储在kafka-log目录下,命名规则为<topic_name>-<partition_id>,如新建一个Topic,
查看kafka_log目录
3. kafka高吞吐量的原因
因为每条消息都被append到该Partition中,属于顺序写磁盘,因此效率非常高,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证。
对于传统的message queue而言,一般会删除已经被消费的消息,而Kafka集群会保留所有的消息,无论其被消费与否。当然,因为磁盘限制,不可能永久保留所有数据(实际上也没必要),因此Kafka提供两种策略删除旧数据。一是基于时间,二是基于Partition文件大小。例如可以通过配置$KAFKA_HOME/config/server.properties
,让Kafka删除一周前的数据,也可在Partition文件超过1GB时删除旧数据,配置如下所示。
#The minimum age of a log file to be eligible for deletion log.retention.hours=168 # The maximum size of a log segment file. When this size is reached a new log segment will be created. log.segment.bytes=1073741824 # The interval at which log segments are checked to see if they can be deleted according to the retention policies log.retention.check.interval.ms=300000 # If log.cleaner.enable=true is set the cleaner will be enabled and individual logs can then be marked for log compaction. log.cleaner.enable=false
因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高Kafka性能无关。选择怎样的删除策略只与磁盘以及具体的需求有关。另外,Kafka会为每一个Consumer Group保留一些metadata信息——当前消费的消息的position,也即offset。这个offset由Consumer控制。正常情况下Consumer会在消费完一条消息后递增该offset。当然,Consumer也可将offset设成一个较小的值,重新消费一些消息。因为offset由Consumer控制,所以Kafka broker是无状态的,它不需要标记哪些消息被哪些消费过,也不需要通过broker去保证同一个Consumer Group只有一个Consumer能消费某一条消息,因此也就不需要锁机制,这也为Kafka的高吞吐率提供了有力保障。
在异步消息发送模式中,kafka允许进行批量发送,也就是先讲消息缓存到内存中,然后一次请求批量发送出去。这样减少了磁盘频繁io以及网络IO造成的性能瓶颈。
零拷贝也是提高吞吐量的一个点。