美文网首页
kafka-2.7.0(一)概念

kafka-2.7.0(一)概念

作者: _大叔_ | 来源:发表于2021-02-26 09:12 被阅读0次

    概念

    1. Kafka 并没有完全遵照 JMS范,它另辟蹊径,探索出了一条独有的道路

    2. kafka是把消息写到页缓存中,然后由操作系统自行决定什么时候把页缓存中的数据写回磁盘上,这样的设计有3个主要优势:

    • 操作系统页缓存是在内存中分配的,所以消息写入的速度非常快。
    • Kafka 不必直接与底层的文件系统打交道。所有烦琐的 1/0 操作都交由操作系统来处理
    • Kafka 写入操作采用追加写入( append )的方式,避免了磁盘随机写操作,且不允许修改己写入的消息,因此它属于典型的磁盘顺序访问型操作。普通的磁盘顺序写入 跟 内存随机写入有过之而无不及。
    1. Kafka 就是依靠下列4点达到了高吞吐量、低延时的设计目标的。
    • 大量使用操作系统页缓存,内存操作速度快且命中率高。
    • Kafka 不直接参与物理 1/0 操作,而是交由最擅长此事的操作系统来完成。
    • 采用追加写入方式,摒弃了缓慢的磁盘随机读/写操作。
    • 使用以 sendfile 为代表的零拷贝技术加强网络间的数据传输效率。严格来说是通过 Java 的 FileChannel.transferTo 方法实现的。
    1. Kafka 中的 topic 通常都会被多个消费者订阅,因此出于性能的考量, Kafka 并不是 topicmessage 的两级结构,而是采用了 topic-partition-message 的三级结构来分散负载。

    2. topic partition 下的每条消息都被分配一个位移值。实际上 Kafka 消费者端也有位移( offset )的概念,但注意这两个 offset 属于不同的概念。每条消息在某个 partition 位移是固定的,但消费该 partition 的消费者的位移会随着消费进度(前提要提交offset)不断前移。

    3. 对于每条待发迭的消息,如果该消息指定了 key ,那么该 partitioner 会根据 key 的哈希值来选择目标分区:若这条消息没有指定 key ,则 partitioner 使用轮询的方式确认目标分区一一这样可以最大限度地确保消息在所有分区上的均匀性

    4. 消费者的offset没有提交的话,下次启动会从没提交的地方开始读

    5. kafka 的广播模式,一个topic可以被多个组的一个消费者进行消费,这样就实现了广播模式。也就是组与组之间共享数据,但组内的消费者竞争消费。

    6. kafka 消费过的数据依旧会保留在文件,可以通过两种方式删除旧数据。两种方式都可以通过修改 vim KAFKA_HOME/config/server.properties 的配置文件来设置。

    • 可以基于时间配置,让kafka删除一周前的数据。
    # 单位小时
    log.retention.hours=168
    
    • 也可以在 Partition 文件超过1GB时删除旧数据
    # 单位字节
    log.segment.bytes=1073741824
    

    kafka 副本分区

    kafka 的分区,在集群模式下,会均匀分布到每个集群节点。

    kafka 的副本指的是分区的副本。副本数量不能超过 集群节点 的数量

    kafka 集群是没有leader一说的,但对于 分区副本 是有leader的。既然有 leader那么肯定有选举机制,kafka 会在集群中的随机一个 broker(节点) 开启一个 controller 进程,用来进行leader的选举。

    kafka生产

    1. producer 先从zookeeper的 */brokers/.../state 节点找到改 partition 的 Leader
    2. producer 将消息发送给 Leader
    3. leader 将消息写到本地log
    4. follower 从 leader 批量拉取消息,写入本地log,成功向leader发送ACK
    5. leader 收到所有 replica 的ACK后,增加 HW(high watemark,最后commit的offset) 并向 producer 发送ACK。
      期间有ISR机制,ISR是指:比如有三个分布①②③,其中②是leader,①③是 follower。假设在数据同步过程中,①跟上leader,但是③出现故障没有同步,则①②是一个ISR,而③不是ISR成员。后期在Leader选举时,会用到ISR机制,优先从ISR中选择Leader。

    kafka Leader 选举

    这里所谓的 HA 指得就是对 partition 的 HA,只有 partition 有 Leader 和 follower 机制,所以 Leader 挂了之后要重新选举 Leader。在选举新 Leader 时,一个基本原则是,新的 Leader 必须拥有旧的 Leader commit 过的所有消息。

    由写入流程可知 ISR 里面的所有 replication 都跟上了 Leader,只有 ISR 里面的成员才能选为 Leader。对于 f+1 个 replication,一个 partition 可以容忍 f 个 replication 失效的情况下保证消息不丢失。比如一个分区 5个副本,挂了4个,还有一个,依然可以工作。

    当所有 replication 都不工作时,有两种可行的方案:kafka0.8* 以后默认使用第二种

    1. 等待 ISR 中的任一个 replication 活过来,并选它做为Leader。可保障数据不丢失,但时间可能相对较长。
    2. 选择第一个活过来的 replication(不一定是 ISR 成员)作为 Leader。无法保障数据不丢失,但相对不可用时间较短。

    kafka 消费模式

    首先明确一点,kafka 使用的是 pull(拉取) 模式。

    Kafka最初考虑的问题是,customer应该从 brokes 拉取消息还是 brokers 将消息推送到 consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 broker,consumer 从 broker 拉取消息。

    一些消息系统比如 Scribe 和 Apache Flume 采用了push模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率时,consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式。Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的消费能力去决定这些策略。Pull 有个缺点是,如果broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送消息。

    offset机制

    Consumer 在从 kafka 读取消息后,可以选择 commit,该操作会在 kafka 中保存该 Consumer 在该 partition 中读取的消息的 offset。该 Consumer 下一次再读该 partition 时会从下一条开始读取。通过这一特性可以保证同一消费者从 kafka 中不会重复消费数据。

    kafka 再启动以后会在配置文件中 log.dirs 设置的路径下生成 50 个 offset 文件,如果是集群启动会,会均分这50个offst文件。

    Kafka对于offset的处理有两种提交方式:(1) 自动提交(默认的提交方式) (2) 手动提交(可以灵活地控制offset)

    • 自动提交偏移量:
      Kafka 中偏移量的自动提交是由参数 enable_auto_commit 和 auto_commit_interval_ms 控制的,当 enable_auto_commit=true 时,Kafka 在消费的过程中会以频率为 auto_commit_interval_ms 向 Kafka 自带的 topic(__consumer_offsets) 进行偏移量提交,具体提交到哪个 Partation 是以算法:Math.abs(groupId.hasCode())%50 来计算的。group_id的获取方式可以通过如下命令查看
    ./kafka-consumer-groups.sh --bootstrap-server ip:port --list --topic_name
    

    最后调用 consumer.close() 时候也会触发自动提交,因为它默认autocommit=True

    • 手动提交偏移量
      对于手动提交offset主要有3种方式:1.同步提交 2.异步提交 3.异步+同步 组合的方式提交

    kafka 索引机制

    kafka 解决查询效率的手段直一是将数据文件分段,可以配置每个数据文件的最大值,每一个log文件的大小默认是1GB。每段放在一个单独的数据文件里面,数据文件以该字段中最小的 offset 命名,其他位置用0填充。最初始的文件是00000000000000000000.log命名的,但下一个log文件生成时的第一条消息的offset是18987,则该log文件的命名是00000000000000018987.log,并且每生成一个log文件就会对应产生一个index文件,是和log文件的命名相同的。这样在进行消息检索的时候可以快速利用二分的方法进行查找,定位到某一个分段文件中。

    稀疏索引+二分查找,可以加快查找速度
    index 文件中并没有为数据文件中的每条 message 建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。从而需要做一次顺序扫描,但是这场戏顺序扫描的范围就很小了。

    索引文件被映射到内存中,所以查找的速度还是很快的。

    kafka的消息系统语义

    在一个分布式发布订阅系统中,组成系统的计算机总会由于各自的故障而不能工作。在 kafka 中,一个单独的 broker,可能会在生产者发送消息到一个topic的时候宕机,或者出现网络故障,从而导致生产者发送消息失败。根据生产者如何处理这样的失败,产生了不同的语义。

    至少一次语义(at least once semantics)all 或 -1

    生产

    如果生产者收到了 Kafka broker 的确认(acknowledgement,ack),并且生产者的acks 配置项设置为all(或-1),这就意味着消息已经被精确一次写入 Kafka topic了。然而,如果生产者接收 ack 超时或者收到了错误,它就会认为消息没有写入 Kafka topic 而尝试重新发送消息。如果 broker 恰好在消息已经成功写入 Kafka topic 后,发送 ack 前,出了故障,生产者的重试机制就会导致这条消息被写入 Kafka 两次,从而导致同样的消息会被消费者消费不止一次。kafka 默认是该语义

    消费

    关闭自动提交,改为手动提交,但是在程序处理的过程中,已经报数据存储数据库中,但在提交 offset 的时候报错,下次会继续消费这条数据,导致数据重复。

    至多一次语义(at most once semantics)0

    生产

    如果生产者在ack超时或者返回错误的时候不重试发送消息,那么消息有可能最终并没有写入Kafka topic中,因此也就不会被消费者消费到。但是为了避免重复处理的可能性,我们接受有些消息可能被遗漏处理。

    消费

    自动提交机机制会引发这个问题,当消费者拿到数据后,就会立马提交 offset 偏移量,但是数据并没有处理,如果发生处理失败,则下次接收的则是下一个数据。

    精确一次语义(Exactly once semantics)

    生产

    在基于 至少一次语义(at least once semantics)上改进,生产者生成消息的时候,分配一个全局递增的 ID。broker 接收消息的时候,判断当前消息的 ID 是否和已存储的最新的消息 ID 相差 > 1,如果 <= 1,则说明此消费已处理过。如果 > 1,证明中间还有数据未到达。

    #代码中要设置
    acks=all
    # 必须设置enable.idempotence=true 才有效精确一次
    enable.idempotence=true
    
    消费

    精确一次必须配置 生产者的精确一次配置,并加如如下

    processing.guarantee=exact_once
    

    这样在消费者端,通过消息的 ID 实现精确消费。

    相关文章

      网友评论

          本文标题:kafka-2.7.0(一)概念

          本文链接:https://www.haomeiwen.com/subject/jrrtfltx.html