zoukankan      html  css  js  c++  java
  • kafka系列 -- 基础概念

    kafka是一个分布式的、分区化、可复制提交的发布订阅消息系统
    传统的消息传递方法包括两种:

    • 排队:在队列中,一组用户可以从服务器中读取消息,每条消息都发送给其中一个人。
    • 发布-订阅:在这个模型中,消息被广播给所有的用户。
      kafka与传统的消息传递技术相比优势之处在于:
    • 快速:单一的Kafka代理可以处理成千上万的客户端,每秒处理数兆字节的读写操作。
    • 可伸缩:在一组机器上对数据进行分区和简化,以支持更大的数据
    • 持久:消息是持久性的,并在集群中进行复制,以防止数据丢失。
    • 设计:它提供了容错保证和持久性

    基本概念

    1. Topic(话题):Kafka中用于区分不同类别信息的类别名称。由producer指定
    2. Producer(生产者):将消息发布到Kafka特定的Topic的对象(过程)
    3. Consumers(消费者):订阅并处理特定的Topic中的消息的对象(过程)
    4. Broker(Kafka服务集群):已发布的消息保存在一组服务器中,称之为Kafka集群。集群中的每一个服务器都是一个代理(Broker). 消费者可以订阅一个或多个话题,并从Broker拉数据,从而消费这些已发布的消息。
    5. Partition(分区)Topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)
    6. Message:消息,是通信的基本单位,每个producer可以向一个topic(主题)发送一些消息。

    Log(日志)

    日志是一个只能增加的,完全按照时间排序的一系列记录。
    我们可以给日志的末尾添加记录,并且可以从左到右读取日志记录。
    每一条记录都指定了一个唯一的有一定顺序的日志记录编号。

    每个日志文件都是“log entries”序列,每一个log entry包含一个4字节整型数(值为N),其后跟N个字节的消息体。每条消息都有一个当前partition下唯一的64字节的offset,它指明了这条消息的起始位置

    这个“log entries”并非由一个文件构成,而是分成多个segment,每个segment名为该segment第一条消息的offset和“.kafka”组成。另外会有一个索引文件,它标明了每个segment下包含的log entry的offset范围。

    Topic & Partition


    谈到kafka的存储,就不得不提到分区。
    创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性,kafka在接收到生产者发送的消息之后,会根据均衡策略将消息存储到不同的分区中。

    为了使得Kafka的吞吐率可以水平扩展,物理上把topic分成一个或多个partition,每个partition在物理上对应一个文件夹,该文件夹下存储这个partition的所有消息和索引文件。

    消息以顺序存储:每一个分区都是一个顺序的、不可变的消息队列, 并且可以持续的添加,最晚接收的的消息会最后被消费。
    分区中的消息都被分配了一个序列号,称之为偏移量(64字节的offset),在每个分区中此偏移量都是唯一的。
    因为每条消息都被append到该partition中,是顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)

    与生产者的交互

    生产者在向kafka集群发送消息的时候,可以通过指定分区来发送到指定的分区中。
    也可以通过指定均衡策略来将消息发送到不同的分区中。
    如果不指定,就会采用默认的随机均衡策略,将消息随机的存储到不同的分区中。

    每一条消息被发送到broker时,会根据paritition规则选择被存储到哪一个partition
    如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。(如果一个topic对应一个文件,那这个文件所在的机器I/O将会成为这个topic的性能瓶颈,而partition解决了这个问题)。

    在发送一条消息时,可以指定这条消息的key,producer根据这个key和partition机制来判断将这条消息发送到哪个parition。
    paritition机制可以通过指定producer的paritition. class这一参数来指定,该class必须实现kafka.producer.Partitioner接口。
    本例中如果key可以被解析为整数则将对应的整数与partition总数取余,该消息会被发送到该数对应的partition。(每个parition都会有个序号)

    与消费者交互

    在消费者消费消息时,kafka使用offset来记录当前消费的位置。
    在kafka的设计中,可以有多个不同的group来同时消费同一个topic下的消息
    两个不同的group同时消费,他们消费的记录位置offset各不项目,不互相干扰。

    对于一个group而言,消费者的数量不应该多于分区的数量
    因为每个分区被同一个group中的一个消费者所消费。所以:

    1. 一个消费者可以消费多个分区,但一个分区只能给同一个组的一个消费者消费
    2. 同一个group下消费者对分区互斥,而不同消费组之间是共享的。
    3. 因此,若一个group中的消费者数量大于分区数量的话,多余的消费者将不会收到任何消息

    通过设置不同的消费者,可以实现传统的发布订阅模式和队列模式:

    offset偏移量

    1. 在每个分区中此偏移量都是唯一的。
    2. 消费者所持有的仅有的元数据就是这个偏移量,也就是消费者在这个log中的位置。
    3. 偏移量由消费者控制。正常情况当消费者消费消息的时候,偏移量也线性的的增加。消费者可以将偏移量重置为更老的一个偏移量,重新读取消息。
    4. 一个消费者的操作不会影响其它消费者对此log的处理
    5. 一个topic中有100条数据,我消费了50条并且提交了,那么此时的kafka服务端记录提交的offset就是49(offset从0开始),那么下次消费的时候offset就从50开始消费。

    手动控制Offest

    首先了解一下消息传递保证:

    1. At most once 最多一次 --- 消息可能丢失,但绝不会重发。
    2. At least once 至少一次 --- 消息绝不会丢失,但有可能重新发送。
    3. Exactly once 正好一次 --- 这是人们真正想要的,每个消息传递一次且仅一次。

    自动提交是在kafka拉取到数据完就直接提交数据偏移量。

    而业务系统中,消费数据还伴随着一些逻辑业务处理,插入数据库等。
    这事务过程,需要完成才提交。不然还没插进数据库,新的数据又来了。
    加入插入数据库还失败了,就没法再消费一次失败的数据了。

    所以要严格的不丢数据,需要手动控制offest。

    1. 手动commit offset,并针对partition_num启同样数目的consumer进程,这样就能保证一个consumer进程占有一个partition,commit offset的时候不会影响别的 partition 的 offset。但这个方法比较局限,因为partition和consumer进程的数目必须严格对应。
    2. 另一个方法同样需要手动commit offset,另外在consumer端再将所有fetch到的数据缓存到queue里,当把queue里所有的数据处理完之后,再批量提交offset,这样就能保证只有处理完的数据才被commit。

    官网还说:
    手动控制offest让我们能精确控制消息被消费(实现 提交了offest的不再消费,没提交过offest的数据会再次消费)。
    但这一过程可能在 数据插入数据库后,但是还没commit offset到kafka时 失败了;那么下一次消费将还是从上次消费的起点取到数据,会重复插入数据库,造成重复。
    kafka提供的是 "at-least once delivery" 保证, 即可能有两次三次被消费(我们项目就是这样)。

    at-least once还有一个问题:
    加入一个分区中先后进来 1,2 两条数据。在进来1时,网络不好导致插入失败,然后进行重试机制后网络还是不行,如果不停程序继续消费,这时2数据进来时网络好了,对2插入成功,那么偏移量就偏到数据2了,将1数据覆盖了,这时就不会再插入1数据了。就没法保证只at-least once了,除非停掉消费者,但是停了的话,同一组的消费者还会继续消费,这时又要实际到使用 assign模式还是subscribe模式订阅消息了。

  • 相关阅读:
    LeetCode OJ String to Integer (atoi) 字符串转数字
    HDU 1005 Number Sequence(AC代码)
    HDU 1004 Let the Balloon Rise(AC代码)
    HDU 1003 Max Sum(AC代码)
    012 Integer to Roman 整数转换成罗马数字
    011 Container With Most Water 盛最多水的容器
    010 Regular Expression Matching 正则表达式匹配
    007 Reverse Integer 旋转整数
    006 ZigZag Conversion
    005 Longest Palindromic Substring 最长回文子串
  • 原文地址:https://www.cnblogs.com/stillcoolme/p/9780964.html
Copyright © 2011-2022 走看看