• 初识Apache Kafka 核心概念


                    初识Apache Kafka 核心概念

                                                        作者:尹正杰

    版权声明:原创作品,谢绝转载!否则将追究法律责任。

    一.kafka概要设计

      kafka在设计初衷就是为了解决互联网公司的超级大量级数据的实时传输。为了实现这个目标,kafka在设计之初就需要考虑以下四个方面:
        (1)吞吐量/延迟
        (2)消息持久化 
        (3)负载均衡和故障转移
        (4)伸缩性

    1>.吞吐量/延迟

    一.吞吐量/延时介绍
      我们先打个比方:若kafka处理一条消息需要花费2ms,那么计算得到的吞吐量不会超过500条消息每秒(1000ms/2ms=500条/s)。但是若我们采用批处理(batching)的思想,假设在发送前我们首先会等待一段时间(假设是8ms),那么此时消息发送的延迟变成了10ms(2ms+8ms),即延迟增加了4倍,但假设在这8ms中我们总共积累了1000条消息,那么整个系统的吞吐量就变成了100000 条/s。
      此时你会发现吞吐量提升了200倍!看到micor-batch的威力了吧?这就是目前诸如Storm Trident 和 Spark Streaming等消息处理平台所采用的批处理思想。 
    
    二.Kafka如何做到高吞吐量,低延迟的呢? 
      首先,kafka的写入操作是很快的,这主要得益于它对磁盘的使用方法的不同。虽然kafka会持久化所有数据到磁盘,但本质上每次写入操作其实都只是把数据写入到操作系统的页缓存(page cache)中,然后由操作系统自行决定什么时候把页缓存中的数据写入磁盘上。这样的设计由三个主要的优势:
        (1)操作系统页缓存是内存中分配的,所以消息写入的速度非常快;
        (2)kafka不必直接与底层的文件系统打交道。所以烦琐的I/O操作都交由操作系统来处理;
        (3)kafka写入操作采用追加写入(append)方式,避免了磁盘随机写操作(据资料统计,顺序磁盘I/O速度是毫不逊色于随机读写内存I/O速度。感兴趣的小伙伴可以使用相关工具测试一下。); 
    
    三.Kafka的高吞吐量,低延迟的设计目标
      (1)大量使用操作系统页缓存,内存操作速度快且命中率高; 
      (2)Kafka不直接参与物理I/O操作,而是交由最擅长此时的操作系统来完成; 
      (3)采用追加写入方式,摒弃了缓慢的磁盘随机读/写操作;
      (4)使用sendfile为代表的零拷贝技术加强网络间的数据传输效率;

    2>.消息持久化的优点

      (1)解耦消息发送和消息消费
          本质上来说,kakfa最核心的功能就是提供了生产者-消费者模式的完整解决方案。通过将消息持久化使得生产者方不再需要直接和消费者方耦合,它只是简单的把消息生产出来并交由kafka服务器保存即可,因此提升了整体的吞吐量。 
      (2)实现灵活的消息处理
          很多kafka的下游子系统(接受kafka消息的系统)都有这样的需求:对于已经处理过的消息可能在未来的某个时间点重新处理一次,即所谓的消息消息重演(message replay)。消息持久化便可以很方便地实现这样的需求。 

    3>.负载均衡和故障转移

      作为一个功能完备的分布式系统,kafka如果只提供了最基本的消息引擎功能肯定不足以帮助它脱颖而出。一套完整的消息引擎解决方案中必然要提供负载均衡(load balancing)和故障转移(fail-over)功能。
      何为负载均衡?顾名思义就是让系统的负载根据一定的规则均衡地分配在所有参数工作的服务器上,从而最大限度的提升整体的运行效率。kafka实现负载均衡实际上是通过智能化的分区领导者选举(partition leader election)来实现的。 
      除了负载均衡,完备的分布式系统还支持故障转移,所谓故障转移,是指当服务器意外终止时,整个集群可以快速的检测到该失效(failure),并立即将该服务器上应用或服务自动转移到其他服务器上。故障转移通常是“心跳”和“会话“的机制来实现的。kafka服务器支持故障转移的方式就是使用会话机制。每台kafka服务器启动后会以会话的形式把自己注册到zookeeper服务器上。一旦该服务运转出现问题,与zookeeper的会话变不能维持从而超时失效,此时kafka集群会选举出另外一台服务器来完全代替这台服务器继续提供服务。

    4>.伸缩性 

      所谓伸缩性,英文名是scalability。伸缩性表示想分布式系统中增加额外的计算资源(比如CPU,内存,存储或带宽)时吞吐量提升的能力。阻碍线性扩容的一个很常见的因素就是状态的保存。我们知道,不论是哪类分布式系统,集群的每台服务器一定会维护很多内部状态。如果由服务器自己来保存这些状态信息,则必须处理一致性的问题。相反,如果服务器是无状态的,状态的保存和管理交与专门的协调服务来做(比如zookeeper)。那么整个集群的服务武器之间就无需繁重的状态共享,这极大的降低了维护复杂度。倘若要扩容集群节点,只需要简单的启动新的节点集群和进行自动负载均衡就可以了。 
      Kafka正式采用了这样的思想:每台kafka服务器上的状态统一交友zookeeper保管。扩展kafka集群也只需要一步:启动新的kafka服务器即可。当然这里需要言明的是,在kafka服务器上并不是所有的状态信息都不保存,它只保存了很轻量级的内部状态(比如从kakka 0.10.x版本之后,它将每个topic的消费者的偏移量自己维护了,把这些偏移量存放到了一个叫做“__consumer_offsets”的的topic进行维护)。

    二.Kafka简介

    1>.什么是JMS

      在Java中有一个角消息系统的东西,我们叫他Java Message Service,简称JMS。比如各种MQ。我们举个简单的例子,在java中进程之间通信需要socket,“路人甲”要向“路人乙”发送数据,需要“路人乙”开启服务,暴露端口。这样面临的问题就是如果“路人乙”不在线,“路人甲”就不能发送数据给“路人乙”。
    
      为了解决该问题就需要在“路人甲”和“路人乙”之间引入消息中间件,进行解耦。如下图所示:

    2>.JMS的两种工作模式

      第一种模式:点到点(point to point,简称P2P),典型的一对一模式(一个人发送数据的同时只有一个人接收数据),也有人称之为端到端(peer to peer)或者队列模式(queue)。
    
      第二种模式:发布订阅模式(publish subscribe,简称P-S),典型的一对多模式(一个人发送数据的同时可以有多个人接收数据),也有人称为主题模式(在生产者和消费者之间加入了topic(主题),主题相当于公告栏,生产者发送消息到主题后,所有消费者都可以看到,功能类似于咱们平时接触的微信公众号)。

    3>.Kafka的工作模式

        Kafka的工作模式可以把JMS的两种模式结合在一起,我们称之为消费者组模式。

    4>.什么是Kafka

    Kafka最初是由LinkedIn公司开发,并于 2011年初开源。在流式计算中,Kafka一般用来缓存数据,Storm,Spark,Fink等通过消费Kafka的数据进行计算。Apache Kafka是一个开源消息系统,由Scala写成。是由Apache软件基金会开发的一个开源消息系统项目。
    
    作为一个流系统,Apache Kafka有三种关键能力:   (
    1)发布和订阅记录流。在这个方面其等于一个消息队列或企业级消息中间件,也是我们最熟悉的用法   (2)以容错的方式存储记录流。这是Kafka和很多仅基于内存的消息队列的重要区别   (3)对流中的记录进行处理。这是Kafka 0.10后新增的能力(Kafka Streams) 为进一步学习其机制,你需要了解:   (1)Kafka以集群形式运行在一到多台服务器上,每个Kafka工作进程称为broker;   (2)Kafka集群对记录的流进行分类存储,这种分类称为主题(topic);   (3)每条记录由键(key:消息键,对消息做partition时使用,即决定消息被保存在某topic下的哪个partition)、值(value:消息体,保存实际的消息数据)、时间戳(timestamp:消息发送时间戳, 用于流式处理及其他依赖时间的处理语义。如果不指定,则取当前时间)等,感兴趣的可以深入了解一下Kafka的消息格式,网上有很多相关资料,我这里就不当搬运工了.

    5>.Kafka版本

      Kafka是一种高吞吐,分布式,基于发布订阅的流系统。最初由LinkedIn公司开发,后成为Apache顶级项目。目前(20190710)社区版本最新版本为2.2.x,从0.10.0版本开始核心设计没有大的变化,API也基本稳定。
    
      如下图所示,我们可以对比 Cloudera Distribution of Apache Kafka(简称CDK)和 Apache Kafka的对应关系,原链接为:https://www.cloudera.com/documentation/kafka/latest/topics/kafka_packaging.html#concept_fzg_phl_br。

    6>.Kafka核心API

    如下图所示,Kafka有四类核心API: 
      (1)Producer API
          允许应用程序将记录流发布到一个或多个topic中.
      (2)Consumer API
          允许应用程序从一个或多个topic中订阅记录流.
      (3)Streams API
          允许应用程序作为流处理器,从一个或多个topic中消费输入流,并向一个或多个topic写出输出流,在输入流和输出流中做转换.
      (4)Connector API
          允许创建和运行可重用的生产者或消费者,其将topic连接到现有应用程序或数据系统,例如一个到关系型数据库的connector可将表的变动捕捉到topic中.

     

    7>.kafka特点

      第一:可以处理大量数据,TB级别;
    
      第二:高吞吐量,支持每秒种百万消息,传输速度可达到300MB/s;
    
      第三:分布式,支持在多个Server之间进行消息分区;
    
      第四:多客户端支持,和多种语言进行协同;
    
      第五:它是一个集群,扩容起来也相当方便;

    三.kafka消息队列

    1>.kafka消息队列内部实现原理

    点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除,pull)
        点对点模型通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。这个模型的特点是发送到队列的消息被一个且只有一个接收者接收处理,即使有多个消息监听者也是如此。
    
    
    发布/订阅模式(一对多,数据生产后,推送给所有订阅者,push)
        发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者,临时订阅者只在主动监听主题时才接收消息,而持久订阅者则监听主题的所有消息,即使当前订阅者不可用,处于离线状态。

    2>.为什么需要消息队列

      (1)解耦:
          允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
      (2)冗余:
          消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
      (3)扩展性:
          因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
      (4)灵活性 & 峰值处理能力:
          在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
      (5)可恢复性:
          系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
      (6)顺序保证:
          在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka保证一个Partition内的消息的有序性)
      (7)缓冲:
          有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
      (8)异步通信:
          很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

    3>. Kafka架构图简介

     

    Kafka关键名词介绍:
    一.Producer :
        消息生产者,就是向kafka broker发消息的客户端。
    
    二.Consumer :
        消息消费者,向kafka broker取消息的客户端
    
    三.Topic :
        可以理解为一个队列,它是Kafka管理消息的实例。
    
    四.Consumer Group (我们这里简称CG,即消费者组):
        这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。一个topic可以有多个CG。topic的消息会复制-给consumer。如果需要实现广播,只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。
        关于Consumer Group我们要注意以下几点:
            1>.在同一个CG的中,同时只能有一个consumer对topic进行消费;
            2>.在同一个CG中,所有的consumer是不会重复消费数据的,也就是说,同一个topic中的某个Partition中的数据被当前CG的一个consumer消费后,是不会再被这个GC中的其它consumer再次进行消费啦;
            3>.在同一个CG中,每一个consumer消费单元是都以Partition为消费单元的,换句话说,在同一个CG中,只要consumer和topic中的Partition建立RPC连接后,那么这个Partition中的所有数据只会被这个consumer消费。
    
    五.Broker :
        一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。
    
    六.Partition:
        Kafka集群中,Partition是生产者和消费者操作的最小单元。为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。
    
    七.Offset:
        kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka
    
    八.Zookeeper集群
        Zookeeper保存的东西有两个:
            1>.Kafka集群节点的状态信息(便于管理leader和follower角色);
            2>.消费者当前正在消费消息的状态信息(比如保存消费者消费的偏移量(Offset))。
            温馨提示:它并没有保存生产者的一些元数据信息。
    
    九.Replica
        Kafka存储数据的分区分为主从,即Leader和Follower。也就是说,kafka自己就有冗余机制,它将数据写入Leader的Partition之后,会将这份数据拷贝到其它broker的follower的Partition之中。温馨提示:这个存储在follower的Partition数据不会直接和消息生成者沟通,更不会跟消息消费者进行沟通,它仅仅是起到一个数据备份的作用!当Kafka的leader节点挂掉时,follower的各个节点会重写选举出新的leader,并由新的leader向外提供服务。             
    
    十.Event
        Kafka集群保存消息是以Partition去保存的,每一个Partition是按照队列去保存的,消息是以Event来包装消息的,一个消息就是一个event。因此每个Partition的消息是有序的,多个Partition之间的消息是无序的!

    四.Kafka核心概念

    1>.topic和partition

      在概念上来说,topic只是一个逻辑概念,代表了一类消息,也可以认为是消息被发送到的地方。通常我们可以使用topic来区分实际业务,比如业务A使用一个topic,业务B使用另一个topic。从本质上说,每个Kafka topic都由若干个partition组成,而Kafka的partition是不可修改的有序消息序列,也就是说是有序的消息日志。每个partition有自己专属的partition号,通常是从0开始的。用户堆partition我唯一能做的操作就是在消息序列的尾部追加写入消息。
    
      partition上的每条消息都会被分配一个唯一的序列号,按照Kafka的术语来讲,该序列号被称为位移(offset)。该位移值是从0开始顺序递增的证书。位移信息可以唯一定义到某partition下的一条消息。值得一提的是,Kafka的partition实际上并没有太多的业务含义,它的引入就是单纯的为了提升系统的吞吐量,因此在创建Kafka topic的时候可以根据集群实际配置设置具体的partition数,实现整体性能的最大化。

    2>.offset

      上面说过,topic partition下的每条消息都被分配了一个位移值。实际上,Kafka消费者端也有位移(offset)的概念,但一定要注意这两个offset属于不同的概念。
    
      显然,每条消息在某个partition的位移是固定的,但消费该partition的消费者的位移是会随着消费进度不断迁移,但终究不可能超过该分区最新一条消息的位移。综合之前说的topic,partition和offset,我们可以断言Kafka中的一条消息其实就是一个
    <topic,partition,offset>三元组(tuple),通过该元组值我们可以在Kafka集群中找到位移对应的那条消息。

    3>.Replica

      既然我们已知partition是有序的消息日志,那么一定不能只保存者一份日志,否则一旦保存在partition的Kafka服务器挂掉了,其上保存的消息也就都丢失了。分布式系统必然要实现高可靠性,而目前实现的主要途径还是依靠冗余机制。换句话说,就是备份多份日志。这些分贝日志在Kafka中被称为副本(replica),它们存在的唯一目的就是防止数据丢失,这一点一定要记住!

    4>.leader和follower

      副本(replia)分为两类:领导者副本(leader replia)和追随者副本(follower replia)。follower replica是不能提供服务给客户端的,也就是说不负责响应客户端发来的消息写入和消息消费请求。它只是被动地向领导者副本(leader replia)获取数据,而一旦leader replica 所在的broker宕机,Kafka会从剩余的replica中选举出新的leader继续提供服务。
    
      Kafka保证同一个partition的多个replica一定不会分配在同一台broker上。毕竟如果同一个broker上有同一个partition的多个replica,那么将无法实现备份冗余的效果。

    5>.producer

      生产者将数据发布到它们指定的topics。生产者负责选择将记录分配到topic中的哪个分区。可以以round-robin方式分配以简单地负载均衡,或可以按可以按某个分区函数(基于记录的键来计算)来分配。 

    6>.consumer

      消费者拥有一个消费者组(consumer group)名,topic的每条记录会被 传输给其消费者组中的唯一一个消费者。消费者实例可以是单独的线程,也可以位于单独的机器上。 

      如果所有的消费者实例有相同的消费者组名,记录会在这些消费者实例间 有效地负载均衡。如果所有的消费者实例都属于不同的消费者组,则每条 记录会被广播到所有的消费者线程上。

      Kafka中消费的实现,是将log中的分区划分到消费者实例上,使得任何时 刻每个实例都是分区“公平划分”后的排他的消费者。这个管理组内成员 的过程是由Kafka协议动态处理的。如果新实例加入消费者组,它们会从 其他组内成员接管部分分区;如果一个实例挂掉,其分区会被分配给其他 实例。 Kafka只提供分区内记录的全局有序,而不是某topic内不同分区间的全局 有序。分区有序加上按键分区数据的能力对大多数应用来说足够。然而, 如果一定需要记录的全局有序,可以通过指定topic只有一个分区实现, 但这意味着每个消费者组只能有一个消费线程。 

      下图为2节点的Kafka集群维持4个分区(P0-P3),有2个消费者组。消费 者组A有2个消费者,消费者组B有4个消费者。 

    7>.broker

      Kafka是一个分布式消息队列。Kafka对消息保存是根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)成为broker。

      无论是kafka集群,还是producer和consumer都依赖于zookeeper集群保存一些meta信息,来保证系统可用性。因此Zookeeper在生产环境中最少建议部署3台,如果集群较大(100个节点以上)推荐配置5台。

    8>.ISR

      ISR的全称是in-sync replica,翻译过来就是与leader replica保持同步的replica集合。这是一个特别重要的概念。前面讲了很多关于Kafka的副本机制,比如一个partition可以配置N个replica,那么这是否就意味着对副本因子为N的topic,可容忍最多N-1个服务器失效,不会丢失任何已提交到broker的记录呢?答案是:“否”!(网上有资料说是可以保证数据不丢失,如果在min.insync.replicas使用默认值为1且unclean.leader.election.enable的值为true时,可能会导致数据丢失,而Producer的acks设置的为1时,也就是当leader的parition写入成功就认为数据是写入成功的,我们知道follower是有可能和leader节点不一致的!因此当leader节点挂掉的话,此时的数据就丢失啦!)
      
      副本数对Kafka的吞吐率是有一定的影响,但极大的增强了可用性。默认情况下Kafka的replica数量为1,即每个partition都有一个唯一的leader,为了确保消息的可靠性,通常应用中将其值(由broker的参数offsets.topic.replication.factor指定)大小设置为大于1,比如3。
    
      所有的副本(replicas)统称为Assigned Replicas,即AR。ISR是AR中的一个子集,由leader维护ISR列表,follower从leader同步数据有一些延迟(包括延迟时间replica.lag.time.max.ms和延迟条数replica.lag.max.messages两个维度, 当前最新的版本0.10.x中只支持replica.lag.time.max.ms这个维度),任意一个超过阈值都会把follower踢出ISR, 存入OSR(Outof-Sync Replicas)列表,新加入的follower也会先存放在OSR中。AR=ISR+OSR。相反的,当这些replicas重新“追上”了leader的进度时,那么Kafka会将他们加回到ISR中。这一切都是自动维护的,不需要用户进行人为干预,因而在保证了消息交付语义的同时,还简化了用户的操作成本。

    9>.高水位线

    High watermark(高水位线)以下简称HW,表示消息被commit的部分,leader(和followers,看情况)确认写入本地log并返回ack后这些数据才被认为是committed(kafka的参数min.insync.replicas可以控制多少个副本ack后才算commit,默认为1,即只需要leader写成功该段log就committed,此时ISR可能最小只有1,如果设为n,则表示leader和n-1个followers都确认写入才算committed),也就是可以被消费,所以在HW位置以下的消息都可以被消费。(在min.insync.replicas=1时,如果此时ISR只有1个replica,leader又挂掉,又设置了unclean.leader.election.enable=true,则会有一个非ISR内的follower成为新的leader,此时HW会缩小,因此consumer的lag值可能出现负数!这些配置在Apache Kafka 0.10都是默认值,因此很可能出现)
    
    Log end offset(日志结束位置)以下简称LEO,表示消息的最后位置。LEO>=HW,一般会有没提交的部分(除了ISR中最慢的follower,事实上HW=min(ISR中各副本的LEO))。uncommitted部分的消息,需要等待如何向producer进行ack,对应的请求会停留在一块称为purgatory(炼狱)的区域中。具体怎么ack,取决于producer的acks设置:如果设置为0,则producer发出消息后就不等待ack,是at most once的一致性;如果设为1(默认值,注意!),则只要leader的log成功写入请求就返回了,但如果在ack发送给producer前leader就挂了(或丢包了),producer仍然收不到ack,会对请求进行重发,此时数据就有可能重,是at least once的一致性。但在leader挂掉时,leader有但未被followers同步的数据又无法同步,会丢一部分数据;如果设置为all或-1,则需要所有ISR中的followers确认写入后请求才返回(注意,如果允许ISR最少只有1个成员,则和acks=1又没有实质区别了!),也是at least once的一致性,且基本不存在丢数据的可能。
    
    因此,对于producer来说,保证数据不丢(producer送出的消息均一直能被消费到)需要满足以下条件:min.insync.replicas至少设为2、unclean.leader.election.enable设为false、producer的acks设为all。但这种设置会对kafka的吞吐率、故障恢复时间造成巨大影响,究竟要不要保证这么强的一致性,就需要你评估了。

    以上讨论的都与producer相关。对于consumer一侧,情况则比较明朗,根据同步还是异步消费、commit调用的时机决定了at least once还是atmost once,大家可参考文档或图书进行理解。

    10>.reblance扫盲

    一.rebalance简介
      consumer group的rebalance本质上是一组协议,它规定了一个consumer group 是如何达成一致来分配订阅topic的所有分区的。假设某个组下有20个consumer实例,该组订阅一个有着100个分区的topic。正常情况下,Kafka会为每个consumer平均分配5个分区。这个分配过程就被称为rebalance。
      当consumer成功执行rebalance后,组订阅topic的每个分区只会分配给组内一个consumer实例。换句话说,同一个消费者组的消费者不能同时对同一个topic的同一个分区进行消费。
      和旧版本consumer依托于zookeeper进行rebalance不同,新版本consumer使用了Kafka内置的一个全新的协议(group coordination protocol)。对于每个组而言,Kafka的某个broker会被选举为组协调者(group coordinator)。coordinator负责对组对状态进行管理,他的主要责任就是当新成员到达时促成组内所有成员达成新对分区分配方案,即coordinator负责对组执行rebalance操作。
    
    二.rebalance触发条件
      组rebalance触发对条件有以下3个:
        第一:组成员发生变更,比如新consumer加入组,或已有consumer主动离开组,再或是已有consumer崩溃时则触发rebalance;
        第二:组订阅topic数发生变更,比如使用基于正则表达式对订阅,当匹配正则表达式对新topic被创建时则会触发rebalance;
        第三:组订阅topic时分区发生变更,比如使用命令行脚本增加了订阅topic的分区数;
      真实应用场景引发rebalance最常见的原因就是违背了第一个条件(比如flume的kafka source相对于broker集群来说就是consumer对象),特别是consumer崩溃的情况。这里的崩溃不一定就是指consumer进程“挂掉”或consumer进程所在的机器宕机。当consumer无法在指定的时间内完成消息处理,那么coordinator就认为该consumer已经崩溃,从而引发新一轮rebalance。
      我在生产环境中也使用flume消费kafka的数据到hdfs集群上,也遇到过这种rebalance的情况,最终分析原因是:该group下的consumer处理消息的逻辑过重,而且事件处理时间波动很大,非常不稳定,从而导致coordinator会经常行的认为某个consumer已经挂掉,引发rebalance。鉴于目前一次rebalance操作的开销很大,生产环境中用户一定要结合自身业务特点仔细调优consumer参数:“request.timeout.ms”,“max.poll.records”和“max.poll.interval.ms”这几个参数,以避免不必要的rebalance出现。
    
    三.rebalance协议
      前面我们提到过rebalance本质上是一组协议。group于coordinator共同使用这组协议完成group的rebalance。最新版本的Kafka中提供了下面5个协议来处理rebalance相关事宜。
        第一:JoinGroup请求:consumer请求加入组;
        第二:SyncGroup请求:group leader 把分配方案同步更新到组内所有成员中;
        第三:Heartbeat请求:consumer定期向coordinator汇报心跳表明自己依然存活;
        第四:LeaveGroup请求:consumer主动通知coordinator该consumer即将离组;
        第五:DescribeGroup请求:查看组的所有信息,包括成员信息,协议信息,分配方案以及订阅信息等。该请求类型主要供管理员使用。coordinator不使用该请求执行rebalance。
      在rebalance过程中,coordinator主要处理consumer发过来的joinGroup和SyncGroup请求。当consumer主动离组时会发送LeaveGroup请求给coordinator。
      在成功rebalance过程中,组内所有consumer都需要定期向coordinator发送Hearbeat请求。而每个consumer也是根据Heartbeat请求的响应中是否包含REBALANCE_IN_PROGRESS来判断当前group是否开启来新一轮rebalance。
      
      好啦~关于rebalance咱们了解到这里基本上就够用来,感兴趣的小伙伴可以查看rebalance genneration,rebalance流程,rebalance监听器等技术,我们这里就不用深入探讨啦~

    五.Kafka的使用场景

      Kafka以消息引擎闻名,因此它特别适合处理生产环境中的那些流式数据。以下就是Kafka在实际应用中一些典型的使用场景。

    1>.消息传输

      Kafka非常适合替代传统的消息总线(message bus)或消息代理(message broker)。传统的这类系统擅长于解耦生产者和消费者以及批量处理消息,而这些特点Kafka都具备。除此之外,Kafka还具有更好的吞吐量特性,其内置的分区机制和副本机制既实现了高性能的消息传输,同时还达到了高性能的高容错性。一次Kafka特别适合用于实现一个超大量级消息处理应用。

    2>.网站行为日志追踪

      Kafka最早就是用于重建用户行为数据追踪系统的。很多网站上的用户操作都会以消息的形式发送到Kafka的某个对应的topic上。这些点击流蕴含了巨大的商业价值,事实上,目前就有很多创业公司使用机器学习或其他实时处理框架来帮助收集并分析用户的点击流数据。鉴于这种点击流数据量是很大的,Kafka超强的吞吐量特性此时就有了用武之地。

    3>.审计数据收集

      很多企业和组织都需要对关键的操作和运维进行监控和审计。这就需要从各个方面运维应用程序处实时汇总操作步骤信息进行集中式管理。在这种使用场景下,你会发现Kafka是非常适合的解决方案,它可以便捷的对多路消息进行实时收集,同时由于其持久化的特性,是的后续离线审计称为可能。

    4>.日志收集

      这可能是Kafka最常见的使用方式了(日志收集汇总解决方案),每个企业都会产生大量的服务日志,这些日志分散在不同的机器上。我们可以使用Kafka对他们进行全量收集,并集中往下游的分布式存储中(比如HDFS等)。比起其他主流的日志抽取框架(比如Apache Flume),Kafka有更好的性能,而且提供了完备的可靠性解决方案,同时还保持 了低延迟的特点。

    5>.Event Sourcing

      Event Sourcing实际上是领域驱动设计(Domain-Driven Design,简称DDD)的名次,它使用事件序列来表示状态变更,这种思想和Kafka的设计特性不谋而合。还记得吧,Kafka也是用不可变更的消息序列来抽象化表示业务信息的,因此Kafka特别适合作为这种应用的后端存储。

    6>.流式处理

        很多用户接触到Kafka都是因为它的消息存储引擎。自0.10.0.0版本开始,Kafka社区推出了一个全新的流式组件 Kafka Streams。这标志着Kafka正式进入流式处理框架俱乐部。相比老牌流式处理框架Apache Storm,Apache Samza,或是最近风头正劲的Spark Streaming,抑或是Apache Flink,Kafka Streams的竞争力如何?让我们拭目以待吧!

    六.集群环境规划 

    1>.操作系统的选型

      我们知道Kafka依赖于Java环境,因此我们只要能在操作系统上安装jdk理论上就可以部署kafka环境了。没错,事实上kafka的确可以运行在主流的操作系统上,比如windows,Linux,mac OS等等。但是这么多操作系统我们究竟应该选择哪个操作系统去安装呢?为什么大家部署kafka集群都选择的是Linux环境呢?其实咱们是可以分析原因的:
        第一:Kafka新版本clients在设计底层网络库时采用了Java的Selecor机制(NIO),而后者在Linux实现机制就是epoll;但是在window平台上,Java NIO的Selector底层是使用select模型而非IOCP实现的,只有Java NIO2才是使用IOCP实现的。因此这一点上,在Linux部署Kafka要在比Windows上部署能够得到高效的I/O处理能力;
        第二:对于数据网络传输效率而言,Linux也更具有优势。具体来说,Kafka这种应用必然需要大量的通过网络于磁盘进行数据传输,而大部分这样的操作都是通过Java的FileChannel.transferTo方法实现的,在Linux平台上该方法底层会调用sendfile系统调用,即采用了Linux提供的零拷贝(Zero Copy)技术。
      
      Kafka可以在ext4或xfs上很好的工作。有一些挂载优化选项可用,例如noatime,data=writeback等,但除noatime强烈建议使用外,其余选项对效率的提升有限,可以参考官网文件进行配置。
      
      另一个问题是要不要调整 文件刷写行为。Kafka的写操作立即将数据写到文件系统层面,但文件系统是否马上将数据刷写的到磁盘上,则有一些选项可以控制。例如,Kafka的默认行为是应用内不调用fsync,而是由OS的后台线程进行刷写;也可以周期性地调用fsync确保数据已经刷写到磁盘。良好的事件是,不要改变默认的行为。在文件系统层面也不需要做额外的调整。

    2>.磁盘规划

      事实上,根据公开的资料显示,LinkedIn公司的Kafka集群就是使用RAID 10作为底层存储的。除了默认提供的数据冗余之外,RAID 10 还可以将数据自动的负载分布到多个磁盘上。由此可见,RAID作为Kafka的底层存储其实主要的优势有两个:
        第一:提供冗余的数据存储空间;
        第二:天然提供负载均衡;
      以上两个优势对于任何系统而言都是很好的特性。不过对于Kafka而言,Kafka在框架层面其实已经提供了这两个特性:通过副本机制提供冗余和高可靠性,以及通过分散到各个节点的领导者选举机制来实现负载均衡,所以从这方面来看,RAID的优势就显得不是那么明显了。事实上,LinkedIn公司目前正在计划将整个Kafka集群从RAID
    10 迁移到JBOD上,只不过在整个过程中JBOD方案需要解决当前的Kafka一些固有缺陷,比如:     第一:任意磁盘损坏都会导致broker宕机,普通磁盘损坏的概率是很大的,因此这个缺陷从某种程度上来说是致命的。不过社区正在改进这个问题,未来版本中只要为broker配置的多块磁盘中还有良好的磁盘,broker就不会挂掉。     第二:JBOD的管理需要更加细粒度化,目前Kafka没有提供脚本或其他工具用于在不同磁盘间进行手动分配,但这是使用JBOD方案中必要的功能。     第三:JBOD也应该提供类似于负载均衡的功能,目前只是间的依赖轮训的方式为副本数据选择磁盘,后续需要提供更加丰富的策略。
      结合JBOD和RAID之间的优劣对比以及LinkIn公司的实际案例,咱们可以给硬盘规划的结论性总结如下:     第一:追求性价比的公司可以考虑使用JBOD;     第二:使用机械硬盘完全可以满足Kafka集群的使用,SSD更好(尽量不要使用NAS(Network Attached Storage)这样的网络存储设备。);

    3>. 磁盘容量规划

    对于磁盘容量的规划和以下结果因素有关:
        第一:新增消息数;
        第二:消息留存时间;
        第四:平均消息大小;
        第五:副本数;
        第六:是否启用压缩;
    
    根据实际情况而定,比如我们线上的服务器磁盘使用率始终达不到50,尽管我默认保留了168小时,即数据保留了7天哟~就会存在资源浪费的情况,虽然说你可以为了使用这些存储空间,可以在该服务器上搭建其他网络存储服务,但你无法确定搭建的这个服务是否在高峰期时会影响到kafka集群的性能!
    
    [root@kafka116 ~]# df -h
    Filesystem                         Size  Used Avail Use% Mounted on
    /dev/mapper/centos_localhost-root   50G   27G   24G  53% /
    devtmpfs                            16G     0   16G   0% /dev
    tmpfs                               16G     0   16G   0% /dev/shm
    tmpfs                               16G  1.6G   14G  11% /run
    tmpfs                               16G     0   16G   0% /sys/fs/cgroup
    /dev/sda1                         1014M  143M  872M  15% /boot
    /dev/mapper/centos_localhost-home   80T   26T   55T  32% /home
    tmpfs                              3.2G     0  3.2G   0% /run/user/0
    tmpfs                              3.2G     0  3.2G   0% /run/user/1003
    [root@kafka116 ~]# 

    4>.内存规划

      Kafka对于内存对使用可称作其设计亮点之一。虽然在前面我们强调了Kafka大量依靠和磁盘来保存消息,但其实它还会对消息进行缓存,而这个消息换粗你得地方就是内存,具体来说是操作系统对页缓存(page cache)。Kafka虽然会持久化每条消息,但其实这个工作都是底层对文件系统来完成。Kafka仅仅将消息写入page cache而已,之后将消息“flush”到磁盘对任务完全交由操作系统来完成。
      
      一般情况下,broker所需的堆内存都不会超过6GB。所以对于一台16GB内存的机器而言,文件系统page cache的大小甚至可以达到10~14GB!总之对于内存规划的建议如下:     第一:尽量分配更多的内存给操作系统的page cache;     第二:不要为broker设置过大的堆内存,最好不超过6GB;     第三:page大小至少要大于一个日志段的大小;

    5>.CPU规划

      比起磁盘和内存,CPU于kafka而言并没有那么重要,严格来说,kafka不属于计算密集型(CPU-bound)的系统,因此对于CPU需要记住一点就可以了:追求多核而非高时钟频率。咱们对CPU资源规划如下:
        第一:使用多核系统,CPU核数最好大于8;
        第二:如果使用Kafka 0.10.0.0之前的版本或clients端消息版本不一致(若无显式配置,这种情况多半由clients和broker版本不一致造成),则考虑多配置一些资源以防止消息解压操作消耗过多CPU)。

    6>.带宽规划

        第一:尽量使用高速网络;
        第二:根据自身网络条件和带宽来评估Kafka集群机器数量;
        第三:避免使用跨机房网络;

    7>.关于JVM

     需要使用1.8以上的JDK。推荐使用G1GC,其次可选择ParNew+CMS的组合(但是做的相应的调整也比较多)。设置充足的堆大小。以下是一个示范例子:
      -Xmx6g -Xms6g -XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M -XX:MinMetaspaceFreeRatio=50 -XX:MaxMetaspaceFreeRatio=80

    8>.典型线上环境配置

    下面给出一份典型的线上环境配置,用户可以参考这份配置以及结合自己的是实际情况进行二次调整:
        CPU 24核心;
        内存 32GB;
        磁盘 1TB 7200转SAS盘2快;
        带宽:1Gb/s;
        ulimit -n 1000000;
        Socket Buffer 至少64KB,适合于跨机房网络传输;

     七.博主推荐阅读Kafka相关书籍

    《Kafka权威指南》
    
    《Apache Kafka实战》
  • 相关阅读:
    Getting Started with LINQ in C# 章节概况
    LA 2572 Viva Confetti (Geometry.Circle)
    uva 10652 Board Wrapping (Convex Hull, Easy)
    poj 2743 && LA 3403 Mobile Computing (mideasy Search)
    poj 3525 Most Distant Point from the Sea (DC2 + Half Plane)
    poj 3134 && LA 3621 Power Calculus (迭代加深深度优先搜索)
    LA 4728 Squares (二维凸包+旋转卡壳)
    uva 10256 The Great Divide (Convex Hull, Simple)
    hdu 2454 Degree Sequence of Graph G
    poj 1041 John's trip (Euler Circuit)
  • 原文地址:https://www.cnblogs.com/yinzhengjie/p/9780976.html
Copyright © 2020-2023  润新知