kafka工作流程

生产过程
  • 写入方式:producer采用推(push)的方式将消息发布到broker,每条消息都被追加(append)到分区(partition)中,属于顺序写磁盘,效率高于随机写内存,提升了kafka的吞吐量
  • 分区:消息都发送到一个topic中,其本质是一个目录,而topic是由一个partition logs分区日志组成,其中每一个partition中的消息都是有序的,而消息是不断追加到partition log上,其中每一个消息都被赋予了一个唯一的offset。
  • 分区的原因:
  • 方便在集群中扩展,每个分区可以通过调整以适应它所在的机器,而一个topic由多个分区组成,因此可以适应不同规模的业务
  • 提高并发,可以以分区为单位进行读写
  • 分区的原则:
  • 指定了分区则直接使用
  • 未指定分区但是指定key则通过keyhash出一个分区
  • 如果分区和key都没有指定分区,则会轮询选出一个分区
  • *分区相关java源码:
//DefaultPartitioner类
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List<PartitionInfo> partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        if (keyBytes == null) {
            int nextValue = nextValue(topic);
            List<PartitionInfo> availablePartitions = cluster.availablePartitionsForTopic(topic);
            if (availablePartitions.size() > 0) {
                int part = Utils.toPositive(nextValue) % availablePartitions.size();
                return availablePartitions.get(part).partition();
            } else {
                // no partitions are available, give a non-available partition
                return Utils.toPositive(nextValue) % numPartitions;
            }
        } else {
            // hash the keyBytes to choose a partition
            return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
        }
    }
  • 副本(replication):
  • 同一个partition可能会有多个副本,对应 server.properties 配置中的 default.replication.factor=N。
  • 一旦broker泵机,其上所有的分区的数据都将不可被读写,副本可以提高kafka数据的可靠性
  • 同一个分区会有多个副本,这些副本中会有一个被选为leader,producer和consumer都只会和这一个leader交互,而其他副本作为follower从leader中复制数据
  • 写入流程:
  • 首先,producer会从zookeeper的 "/brokers/…/state"节点找到该partition的leader
  • 然后producer会将消息发送给该leader
  • leader将消息写入本地log
  • follower从leader pull消息,写入本地log后发送ack给leader
  • leader搜到所有isr中的副本的ack后,增加HW(high watermark,最后commit 的offset)并向producer发送ack
broker保存消息
  • 存储方式
  • 物理上将topic分成一个或多个partition,每个partition物理上对应一个文件夹
  • 存储策略:不论消息是否被消费,kafka都会保留所有的消息,同时定期删除,一般有两种方法
  • 基于时间:通过设置log.retention.hours配置
  • 基于大小:通过设置log.retention.bytes配置
  • kafka是通过offset读取消息的,时间复杂度为O(1),所以删除过期文件与提高kafka性能无关
  • zookeeper中存储的kafka的元数据:有consumer的元数据如offset、ids等,一个admin节点存储了标记删除的topic节点,同时还储存了诸如broker中的topic及其分区等等数据(见下图:

java kafka 如何多个组使用不同的分区 kafka多分区下的顺序_数据

kafka的消费过程
  • kafka提供了两套consumer的API:高级consumer API和低级consumer API
  • 高级API
  • 优点:高级API写起来简单,不需要管理offset、分区、副本等等
  • 缺点:它的优点同时就是它的缺点:高级api不能自行控制offset、不能细化控制分区、副本、zk等等
  • 低级API
  • 优点:能自行控制offset,自由的读取消息、自行控制分区,进行负载均衡、还降低了对zk的依赖性
  • 缺点:太过复杂,全都要自行实现
  • 消费者组
  • 消费者以消费者组(consumer group)的方式工作的,由一个或多个消费者组成一个组
  • 每个组共同消费一个topic
  • 每个分区在同一时间只能被group中的某一个消费者读取,但是多个group可以共同消费这个分区
  • 某个消费者读取某个分区,叫做某个消费者是某个分区的拥有者
  • 通过消费者组这种方法,消费者可以通过水平扩展的方式同时读取大量的消息。
  • 如果一个消费者失败了,其他group成员会自动负载均衡读取前失败的消费者读取的分区
  • 消费方式
  • consumer采用拉(pull)模式从broker中读取数据
  • push模式很难适应消费速率不同的消费者,因为消息发送的速率是由broker来决定的,他的目标是尽可能快的传递消息,但是这样很容易照成consumer来不及处理消息,而pull模式则可以根据consumer的消费能力以适当的速率消费消息
  • pull模式的不足之处在于,如果kafka没有数据,消费者会陷入循环中,一直等待数据到达。为了避免这种情况,我们在我们的拉请求中有参数,允许消费者请求在等待数据到达的“长轮询”中进行阻塞(并且可选地等待到给定的字节数,以确保大的传输大小)