博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
kafka-一些我在学习中的理解
阅读量:7166 次
发布时间:2019-06-29

本文共 5882 字,大约阅读时间需要 19 分钟。

组件结构

  • 定位:流式处理平台
  • 作用:消息中间件、存储系统、实时流处理
  • kafka组件:消费者、生产者、topic、broker、streams、connecter
  • 一个典型的Kafka集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
  • broker : 经纪人,相当于一个kafka服务器,管理该服务器中的所有不同topic 的分区
  • topic:相当于一个消息队列,可以将topic进行分区,所有分区组合起来为一个topic的所有消息。分区可以分布在不同的服务器也可以分布在同一服务器。
  • 分区(partition):topic的物理上的分区,每个partition是一个有序的队列,每个分区又会有leader 分区 和 follower 分区,所有的消费请求全部由leader处理,follower被动备份leader中的数据分布在kafka集群中不同的服务器中,当leader挂了之后,在follower中选择一个作为leader提供服务。partition由多个 segment文件组成(log文件)。消费者可以指定分区,生产者也可以指定分区,这种设计风格,让消费者可以对敏感性的消息进行局部处理。
  • offset:partition中的每个消息都有一个连续的序列号叫做offset,用于partition唯一标识一条消息.
  • segment文件:partition物理上由segment文件组成,
  • 消费模式:点对点模式(队列模式,一个消费者对应一个消息队列,每个消费队列也只能对应一个),发布订阅模式(topic 与 消费者为多多对应关系 )
  • kafka中的消费模式实现,kafka为这两种的消费模型提供了统一的消费者抽象模型:消费者组
    • 点对点模式:消费组的概念,假设一个topic被消费,所有的消费这个topic的消费者都在一个消费组中,那么就可以看作是点对点模式了
    • 发布订阅模式:一个topic被消费,所有消费这个topic的消费者不再一个组中,多个组消费同一个topic 那么就是该模型
  • 消费者组:每个消费者都有消费者组标识,标识自己属于哪一个消费者组,消费者组去订阅topic,topic的数据被消费者组的消费者均分,一个分区对应一个消费者。如果一个消费者组有3个实例,一个topic有2个分区,则会有一个消费者永远获取不到数据。 一个消费者组只能对应一个topic,组中的每个消费者对应topic的一个分区,在初始化阶段,消费者随机选择分区(也可以手动分配不过较复杂)。一个topic可以被多个消费者组消费。topic和消费者组是一对多的关系。所以,注意相同的消费者组中不能有比分区更多的消费者,否则多出的消费者一直处于空等待,不会收到消息。
  • streams:负责处理消息在kafka中的流转,信息流的处理直接使用producer和consumer API进行简单的处理。对于复杂的转换,Kafka提供了更强大的Streams API。可构建聚合计算或连接流到一起的复杂应用程序。完成实时流处理。 Sterams API在Kafka中的核心:使用producer和consumer API作为输入,利用Kafka做状态存储,使用相同的组机制在stream处理器实例之间进行容错保障。
  • connecter:负责将kafka连接到不同的数据存储系统,比如mysql数据库,txt文件等
  • 消息顺序性:kafka只能保证一个topic中一个分区的消息顺序性,一个topic中的多个分区无法保证顺序一致性,要想保证一个topic的顺序一致性只能将topic 设定为一个分区。
  • 复制因子: 是相对于topic的分区来定义的,如果一个topic的复制因子设置为N,分区的leader+follower的数量为N,则该topic允许N-1服务器宕机而不丢失任何已经提交(committed)的消息。

存储结构

  • 文件系统具体看另一篇笔记
  • kafka文件系统:指的是partition中segment文件(log、index)的存储结构
  • kafka使用文件系统并依赖页缓存(page cache)来保证在宕机缓存保持可用、存储大量消息的情况下的常数复杂度获取消息数据。
  • 每个topic的partition是一个大文件夹,里面有许多segment文件夹,
  • 页缓存是将磁盘中的对应的数据页加载到预先分配好的内存中等待进程使用,具有预读和替换的功能。
  • 当内存空间耗尽时,可以将消息数据 flush到磁盘中,而不必要尽可能的把数据维持在内存中。
  • kafka的存储是以页缓存为中心进行设计的
  • 设计存储结构的主要目的:提高磁盘利用率和消息处理性能。
  • 事实上,kafka无需任何性能损失既可以访问几乎无限制的磁盘空间,这意味着可以提供一般消息传递系统无法提供的特性。 例如,在Kafka中,消息被消费后不是立马被删除,我们可以保留消息相对较长的时间(例如一个星期)。 这将为消费者带来很大的灵活性
  • 如何保证常数时间复杂度获取消息
    • 选择消费的topic
    • 负载均衡找到对应的partition,非初次则找到该消费线程对应的partition
    • 根据记录的offset二分法查找partition对应segment文件(log file and index file),并将其读入到页缓存中,采用零拷贝的方式将所需数据发送到网卡缓冲区 O(1)
    • 查找对应index文件,在index文件中找到对应的物理地址,找到消息

高吞吐量

Kafka 高吞吐依赖的主要有三点:

  1. micro-batch处理方式,也就是当前Spark Streaming(实时流处理框架)所使用的模式,不是一条条的发送消息,而是一小批一小批的处理。通过微乎其微的延时消耗换取吞吐量几百倍的提升。这种方式也更让Kafka像是一个流处理框架,并且现在Kafka也已经被用于一些流处理的场景了。 Kafka通过递归消息集来支持这一点。 一批消息可以一起压缩并以此形式发送到服务器。 这批消息将以压缩形式写入,并将在日志中保持压缩,并且只能由消费者解压缩。Kafka支持GZIP和Snappy压缩协议
  2. 使用追加写的方式(顺序写):这一点至关重要,我们知道在普通的机械硬盘中随机读写和顺序读写速度差异完全不是一个数量级的,SSD顺序读、机械硬盘顺序读的速度甚至比内存的随机读速度还要高。熟知操作系统的话,都很清楚磁盘读写操作的主要时间消耗是因为机械操作的循道等消耗。
  3. Linux中的sendfile的零拷贝加持,所谓的零拷贝其实就是原本一份数据的IO是需要经过多次copy操作&内核态与用户态的上下文切换,读内核态缓存到应用程序缓存再从应用程序缓存到Socket缓存完成具体的IO操作,而sendFile系统调用零拷贝就是避免了上下文切换带来的copy操作,同时利用直接存储器访问技术(DMA)执行IO操作,避免了内核缓冲区之前的数据拷贝操作。上升到上层的语言操作,就是使用的Java中的FileChannel.transferTo方法进行实现的。(Kafka 1版本使用的是Scala,2版本开始就是使用Java 了这两者都是在JVM上执行的,本质.class 文件解析执行阶段其实是一致的)

非零拷贝:用户进程获取数据并且将数据通过网络发送到调用者方的过程需要四次数据拷贝,两次系统调用,两次用户态内核态上下文切换: 1:操作系统通过DMA将数据从磁盘拷贝到内核空间的页缓存中 2:通过系统调用将页缓存的数据拷贝到用户进程的空间缓存中 3:用户进程要通过网络将数据发送到目标方,则需要通过系统调用将用户进程的缓存数据发送到内核空间中的socket缓存中 4:内核空间中操作系统通过DMA将socket 缓存中的数据发送到网卡缓冲区中

零拷贝(zero-copy):kafka使用sendfile()方式,只有消费(pull)消息时才会适用零拷贝,push消息时可能会对消息进行一些处理,这必须要将数据拷贝到用户态进行处理。 数据传送只发生的内核空间,没有上下文切换,允许操作系统直接将页缓存中的数据发送到网络中,过程为: 1:操作系统通过DMA将数据从磁盘拷贝到内核空间的页缓存中 2:将带有文件位置和长度信息的缓冲区描述符添加socket缓冲区,这一步不复制页缓存中的数据 3:操作系统通过描述符信息直接将页缓存中的数据发送到网卡缓冲区 零拷贝技术有多种,每一种有其适用场景和局限性,比如上述kafka适用的零拷贝技术就是使用的sendfile,并且需要硬件已经驱动程序支持(比如上述的第三步就需要硬件和驱动程序来支持了),其他的还有mmap、splice等具体请看:linux中零拷贝技术笔记

  1. 标准化二进制数据格式,在consumer、products、broker中间的流转的消息数据块采用同一种二进制格式,减少格式转换,提高吞吐量
  2. 数据压缩传输,kafka支持多种数据压缩格式。

负载均衡&故障转移

  • 负载均衡

    • kafka是一种分布式的消息系统,要做负载均衡,也就是要做到均匀分配到所有参与工作的服务器。这里Kafka使用的是分区只能领导者选举,也就是来均匀的选择分区,保证各个分区收到的请求消息都是大致均匀的。
    • Kafka中有一个叫做partition的概念,也就是分区选择器,默认使用的是murmur2Hash 算法计算消息key值的hash值,然后对于总分区数进行求模得到对应的目标分区号,murmur2Hash是一种比较先进的Hash算法,并且在有规律的输入时也能保证分布较为均匀,使用这个算法的还有redis(当字典被用作数据库的底层实现或者hash键的底层实现时,来计算键的哈希值)、nginx、Hadoop。除此之外,使用者可以自定义对应的
  • 故障转移

    • 常见的故障转移实现策略的关键通常是故障发现,Kafka依赖的是zk的 心跳检测机制,当一台Kafka服务器启动后将会话注册到Zookeeper中,
    • zk不停的对节点进行心跳检测,故障发生时与Zookeeper的会话无法维持导致连接超时从而发现故障,此时请求就不再打到这台机器,并且选举出一台新的Kafka服务器来替代这台故障的Kafka服务器。 另外一种情况,如果节点为一个slave,那么不能落后leader太多。这也会导致将该节点判定为故障,这里的落后太多可能两个原因导致一个是网络太慢导致复制太慢从而落后太多,另外一个就是卡主好多次leader 向slave复制都没有作用。 落后是通过replica.lag.max.messages配置控制,卡住是通过replica.lag.time.max.ms配置控制的。

伸缩性

如何轻易的向kafka集群中增加计算资源,并且保证计算资源尽可能的线形叠加。在分布式系统中伸缩性一直是一个较大的问题,因为仅仅是增加机器资源通常会因为一些隐藏的单点瓶颈导致无法线线形扩容,比如说最大的因素就是服务状态的保存。“状态的处理”比如一致性,需要维持状态的一致性就需要浪费大量的cpu资源,所以为了降低这种消耗,Kafka将绝大部分的状态保存及维持相关的交给kafka controller(被zk选举出来的broker) 统一管理。Kafka 服务器内部仅维持少量暂时需要的状态。

每一条消息被发送到Kafka中,其会根据一定的规则选择被存储到哪一个partition中。如果规则设置的合理,所有的消息可以均匀分布到不同的partition里,这样就实现了水平扩展

消费与生产消息

  • 消费者消费消息所采用的方式:pull或者push?
    • 这里的pull和push都是相对于消费者来说的
    • 第一种是服务器push给消费者,优点是一点服务端数据有变消费者可以立马感知到变化,缺点是无法预估消费者消费能力可能造成消息堆积
    • 第二种消费者主动pull服务器,优点可以适应消费者自身的消费速度还可以消费者指定消费消息量批处理传输消息,缺点是如果服务器没有数据,则消费者会一直轮询服务器造成计算机资源浪费,优化的手段可以采用long poll长轮询。
    • 在kafka中使用的是pull加long poll的方式
  • 如何保证消费者与broker消费的消息达成一致?
    • kafka也无法达成完全一致,但是对一致性做了轻量化处理,其处理方式为,每个topic的partition完全有序,每一个partition只对应消费组中的一个消费者,这样消费者在每个分区中的位置只需要一个整数(offset)即可。这可以使得已经消费到哪里位置的状态变得特别小,每个分区只有一个数字,可以进行定期检查,这样的设计使得消息的应答(消费者消费消息的确认)更加轻量。
    • 这种非完全一致性可能导致消息的重复消费,比如:消费者消费了数据,但是应答数据在网络中丢失,这就会导致offset不是最新的,导致消息重复消费
  • kafka对于消费者和生产者默认支持的是“至少一次(at lwast once)”语义
    • 读取消息->处理消息->ack到broker->修改offset
    • 只要“ack到broker”这步丢失,那么消息可能会重复消费,这也满足最少一次语义。
  • broker如何保证生产者消息不重复?
    • 在版本0.11之后,kafka提供幂等性机制,broker为每个生产者分配一个ID,并通过生产者发送的序列号为每个消息进行去重。即使生产者push消息后“收到确认”在网络中丢失导致重新发送message,在broker中也不会保存重复的message。

如果您不同理解,请在评论出指出,共同学习!

如果感觉这篇文章对您有所帮助,请点击一下“喜欢”或者“关注”博主,您的喜欢和关注将是我前进的最大动力!

转载于:https://juejin.im/post/5c4700a7e51d4503834d834a

你可能感兴趣的文章