深入理解Kafka

一、Kafka概述
    Kafka是是分布式发布-订阅消息系统,是一个分布式的、可分区的、可复制的消息系统。
    它最初由 LinkedIn 公司开发,使用 Scala语言编写,之后成为 Apache 项目的一部分。    
    
    它提供了类似于JMS的特性,但是在设计实现上完全不同,此外它并不是JMS规范的实现[因为是Scala实现的不是java实现的]。kafka对消息保存时根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群由多个kafka实例组成,每个实例(server)称为broker。无论是kafka集群,还是producer和consumer都依赖于zookeeper来保证系统可用性集群保存一些meta信息。
    
    kafka的特点:
        高吞吐量:
            Kafka 每秒可以生产约 25 万消息(50 MB),每秒处理 55 万消息(110 MB)基本上等于磁盘的速度。
        持久化数据存储:直接到磁盘,没有内存缓存机制。[磁盘为什么慢:大量随机文件的读写。]
            可进行持久化操作。将消息持久化到磁盘,因此可用于批量消费,例如 ETL,以及实时应用程序。通过将数据持久化到硬盘以及 replication 防止数据丢失。
        [持久化数据存储尽可能进行连续的读写,避免随机的读写。]
        
        分布式系统易于扩展:
            所有的 producer、broker 和 consumer 都会有多个,均为分布式的。无需停机即可扩展机器。
        客户端状态维护:
            消息被处理的状态是在 consumer 端维护,而不是由 server 端维护。当失败时客户端能自动平衡。[服务器不关心任何会话信息
            ,让客户端维护]。
        消息持久化:
            通过O(1)的磁盘数据结构提供数据的持久化
二、Kafka中的基本概念
    Kafka将消息以topic[主题]为单位进行归纳。
    producers通过网络将消息发送到Kafka集群,集群向消费者提供消息。 
    将向Kafka topic发布消息的程序称为producers.
    将订阅topics并消费消息的程序称为consumer.
    Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker.
    
    *Broker:每一台机器叫一个Broker
    *Producer:日志消息生产者,主要来写数据
    *Consumer:日志消息的消费者,主要读数据
    *Topic:是一个虚拟概念,不同的Consumer去指定的Topic去读,不同的Producer往不同的Topic去写
    *Partition:是一个实际的概念,文件夹,在Topic基础上做了进一步的分层
    
三、Topics、Producers、Consumers
    1.Topics:[一个主题有多个分区]。 
      Kafka内部是分布式的、一个Kafka集群通常包括多个Broker。
      负载均衡:将Topic分成多个分区(Partition),每个Broker存储一个或多个Partition
        一个topic是对一组消息的归纳。对每个topic,Kafka对它的日志进行了分区。
        每个分区都由一系列有序的、不可变的消息组成,这些消息被连续的追加到分区中。
    *topic中的偏移量——offset,可以通过制定偏移量来定位到数据读取的位置
        分区中的每个消息都有一个连续的序列号叫做offset的id序列来唯一识别分区中的消息。
        在一个可配置的时间段内,Kafka集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为2天,那么在一个消息被发布的两天时间内,它都是可以被消费的。之后它将被丢弃以释放空间。
    *Kafka采用time-based消息保留策略(SLA),消息超过一定时间后,会被自动删除
    *无论有没有消费,消息会一直持久化,什么时候消息会被清理掉呢?
            1)配置配置持久化时间(7天)
            2)配置最大数据量)
    *Partition的功能
            目的:实现负载均衡,需要保证消息的顺序性
            顺序性的保证:订阅消息是从头后读的,写消息是尾部追加,所以对顺序性做了一个保证
  *Topic是一个虚拟概念, Partition是一个实际的概念
     一个或多个Partition组成了一个Topic
  *Partition是以文件夹的形式存在
  *Partition有两部分组成:
            1) index log:定位存储索引信息
            2) message log:真实数据
    *message是kafka最基本的单位,类似flume里面的event,Storm里面tuple,传输最大消息message的size不能超过1M,可以通过配置参数控制。
        [消费的数据不会立即被删除为了避免,随机读写。]
        Kafka的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。[因为数据的存储是连续的,本身读取的能力取决于磁盘的能力。]
        每个分区在Kafka集群的若干服务器中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的。副本使Kafka具备了容错能力和并发性。
        每个分区都由一个服务器作为“leader”,零或若干服务器作为“followers”,leader负责处理消息的读和写,followers和Leader同步只负责读,followers则去复制leader.如果leader down了,followers中的一台则会自动成为leader。集群中的每个服务都会同时扮演两个角色:作为它所持有的一部分分区的leader,同时作为其他分区的followers,这样集群就会据有较好的负载均衡。
    *副本算法:
        如果设置一个Topic,这个topic有5个Partition,3个replication
        Kafka分配replication的算法:
        假设:将第i个Partition分配到(i % N)个Broker上
                        将第i个Partition的第j个replication分配到((i+j) % N)个Broker上

        虽然Partition里面有多个replication
        如果里面有M个replication,其中有一个是Leader,其他M-1是follower
        
        **将日志分区可以达到以下目的:首先这使得每个日志的数量不会太大,可以在单个服务上保存。另外每个分区可以单独发布和消费,为并发操作topic提供了一种可能。
        **分区是负载均衡失败恢复分布式数据存储的基本单元
        每个分区代表一个并行单元。
    *传输效率:zero-copy
            0拷贝:减少kernel和user模式上下文的切换
                                    直接把disk上data传输给socket,而不是通过应用程序传输
20、Kafka的消息是无状态,消费者必须自己维护已消费的状态信息
    *给定一个顺序数字序列,如果快速查找到其中某一个值的位置?
     查找算法:二分法+顺序遍历
  * 如果Consumer Group里面消费者的个数大于topic的partition个数的话,导致Consumer Group中的个别Consumer 会出现等待的情况,因为一个partition同一时间只能服务于一个Consumer。
  
    2.Producers
        Producer将消息发布到它指定的topic中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。使用的更多的是第二种。
    *Producer分为同步模式和异步模式
            producer.type指定:sync:同步模式(实时)
                                                async:异步模式(达到设定发送条件:时间、数据量)
    *交付保证:
            Kafka默认采用at least once的消息投递策略 : 至少发送一次
            at most once:最多发送一次
            exactly once:只有一次(最理想),目前不支持,目前只能靠客户端维护
    *Kafka集群里面,topic内部由很多partition(包含很多replication),达到高可用的目的
            日志副本策略:保证可靠性
            分为两个角色:主,从
            ISR:是一个集合,所有在set里面的follower才有机会被选为leader
            如何让leader知道follower是否成功接收数据:(心跳,ack)
            如果心跳正常,代表节点活着
    *如何判断“活着”
            1)心跳
            2)如果是slave则能够紧随leader的更新不至于落得太远
            如果挂掉,从ISR集合里剔除掉slave
    *ISR(同步副本):Kafka在zk里面动态维护的集合,里面表示的所有的副本,都是跟上了leader的节奏
            ISR里副本需要删除的两种情况:
            1、消息延迟的问题
            2、数据落后太多
    3.Consumers
        **实际上每个consumer[消息接受者]唯一需要维护的数据是消息在日志中的位置,也就是offset.这个offset由consumer[消息接受者]来维护:一般情况下随着consumer[消息接受者]不断的读取消息,这offset的值不断增加,但其实consumer可以以任意的顺序读取消息,比如它可以将offset设置成为一个旧的值来重读之前的消息。[消息队列中的数据并没有删除,在一定的时间段内还存在。]
        **以上特点的结合,使Kafka consumers非常的轻量级:它们可以在不对集群和其他consumer造成影响的情况下读取消息。你可以使用命令行来"tail"消息而不会对其他正在消费消息的consumer造成影响。

        **消费消息通常有两种模式:队列模式(queuing)和发布-订阅模式(publish-subscribe)。

        (1)队列模式
            队列模式中,多个consumers可以同时从服务端读取消息,每个消息只被其中一个consumer读到;[多个consumer竞争消息消费数据]
        (2)发布订阅模式
            发布-订阅模式中消息被广播到所有的consumer中。    [多个consumer共享消息消费数据]
        (3)Consumers可以加入一个consumer group,组内的Consumer是一个竞争的关系,共同竞争一个topic内的消息,topic中的消息将被分发到组中的一个成员中,同一条消息只发往其中的一个消费者。同一组中的consumer可以在不同的程序中,也可以在不同的机器上。而如果有多个Consumer group来消费相同的Topic中的消息,则组和组之间是一个共享数据的状态,每一个组都可以获取到这个主题中的所有消息。
            如果所有的consumer都在一个组中,这就成为了传统的队列模式,在各consumer中实现负载均衡。
            如果所有的consumer都不在不同的组中,这就成为了发布-订阅模式,所有的消息都被分发到所有的consumer中。
            更常见的是,每个topic都有若干数量的consumer组来消费,每个组都是一个逻辑上的“订阅者”,为了容错和更好的稳定性,每个组都由若干consumer组成,在组内竞争实现负载均衡。实现了组内竞争负载均衡,组间共享互不影响,这其实就是一个发布-订阅模式,只不过订阅者是个组而不是单个consumer(参看图片:Consumer组.png)。
            每一个应用有各自的Consumer group,每个Consumer group里面有多个Consumer
            --------------------------------------        
                相比传统的消息系统,Kafka可以很好的保证有序性。
                传统的队列在服务器上保存有序的消息,如果多个consumers同时从这个服务器消费消息,服务器就会以消息存储的顺序向consumer分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各consumer上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用“专用consumer”的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。
                在这方面Kafka做的更好,通过分区的概念,Kafka可以在多个consumer组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个consumer组,这样一个分区就只被这个组的一个consumer消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个consumer组之间进行负载均衡。注意consumer组的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。
                Kafka只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要topic中所有消息的有序性,那就只能让这个topic只有一个分区,当然也就只有一个consumer组消费它。
            --------------------------------------    

为什么大数据环境下的消息队列常选择kafka?
    分布式存储数据,提供了更好的性能 可靠性 可扩展能力
    利用磁盘存储数据,且按照主题、分区来分布式存放数据,持久化存储,提供海量数据存储能力
    采用磁盘存储数据,连续进行读写保证性能,性能和磁盘的性能相关和数据量的大小无关    
    
四、zookeeper在kafka的应用:
    *Broker Node Registry
      /brokers/ids/[0...N] --> host:port (ephemeral node)
        broker启动时在/brokers/ids下创建一个znode,把broker id写进去。
        因为broker把自己注册到zookeeper中实用的是瞬时节点,所以这个注册是动态的,如果broker宕机或者没有响应该节点就会被删除。
    Broker Topic Registry
    /brokers/topics/[topic]/[0...N] --> nPartions (ephemeral node)
        每个broker把自己存储和维护的partion信息注册到该路径下。

    *Consumers and Consumer Groups
        consumers也把它们自己注册到zookeeper上,用以保持消费负载平衡和offset记录。
        group id相同的多个consumer构成一个消费组,共同消费一个topic,同一个组的
        consumer会尽量均匀的消费,其中的一个consumer只会消费一个partion的数据。

  *Consumer Id Registry
    /consumers/[group_id]/ids/[consumer_id] --> {"topic1": #streams, ...,
        "topicN": #streams} (ephemeral node)
        每个consumer在/consumers/[group_id]/ids下创建一个瞬时的唯一的consumer_id,用来
        描述当前该group下有哪些consumer是alive的,如果消费进程挂掉对应的consumer_id就会从该节点删除。

    *Consumer Offset Tracking
    /consumers/[group_id]/offsets/[topic]/[partition_id] -->offset_counter_value ((persistent node)
      consumer把每个partition的消费offset记录保存在该节点下。
    Partition Owner registry
    /consumers/[group_id]/owners/[topic]/[broker_id-partition_id] -->
        consumer_node_id (ephemeral node)
        该节点维护着partion与consumer之间的对应关系。
        
        

猜你喜欢

转载自blog.csdn.net/u013953226/article/details/82823617
今日推荐