Kafka

KafKa是一个分布式的消息队列,既然说到了消息队列那么也就意味着它的出现是为了解决通信问题的。具体来说就是发送消息的一方(在这里我们称之为生产者)会将数据写到消息队列中然后消息的接收者(在这里我们称之为消费者)会从这个队列中读取数据进而达到通信的目的。

KafKa的设计非常的特别,它没有采用传统的基于内存读写的队列而是将整个队列读写的过程都放在了硬盘上。这里我们可能会担心硬盘的读写效率,但事实上硬盘的IO没有我们想的那么慢,在顺序IO的情况下硬盘的读写速度也是非常可观的。KafKa就是强制所有的读写操作都是顺序IO所以就保证了它的处理信息速度。由于这个特点消费者在消费掉一个数据单元的时候不会像传统队列一样把这些数据都删掉而是保留这些数据直到这些数据过时再进行集体删除。这样所有的IO就都成了顺序IO。同时由于所有操作都在硬盘上那么KafKa会有很好的持久性。

最后KafKa通过Zookeeper来保存自己的元数据进而保证了自己的可用性。在KafKa中每一个客户端都记录着自己的数据状态。它们在发布消息的时候都会在Zookeeper集群上面进行注册,将操作相关的元数据都发送上去。

KafKa的三大组件

Topic相关
如果把KafKa当做是一个图书馆那么,topic(主题)就是图书馆中按照不同的关键词所划分的区域,Patition(分区)就是每个区域上所放置的一个个的书架,如果把一本本的书看做是消息,那么offset就是这些书的编号,最后,每本书都有自己的备份(不过放在这里备份的是书架)。有了上面的这些比喻我们就好理解多了,在KafKa中会有多个topic它是KafKa资源管理的基本单位。每一个主题下面又会有不同的分区patition,所有的数据都放在分区上而且每个数据单位都会有自己的编号offset,最后每个分区都有自己的备份。

这里分别介绍他们的特点:

①topic,在KafKa中可以创建多个topic用来进行资源的管理,多个topic之间是互不影响的。

②patition,在topic中又可以划分出不同的分区来,每个分区可以单独的提供服务,这样的话就为topic并发服务与KafKa集群数据的负载均衡、失败恢复奠定了基础。

③offset,在KafKa中连续的消息都有连续的编号对他们唯一标识这样。而且每一个消息不会在消费者消费完成之后立即擦除而是等待这些消息全部过期之后在去整批擦除(顺序IO)。

④replication,分区备份在不同的机器上这是失败恢复与负载均衡的基础,而且这些分区散落在不同的机器上有的分区为leader有的分区是flowller。特别注意的是,这里没有以broker来分配leader与flowller(在KafKa中每一台机器被称作是一个broker),原因就是如果一台机器成为leader之后他就会受到很大的负载,而以分区去划分的话这些leader就会分布在不同的机器上这样的话就打到了负载均衡的目的。如下图所示(代圆圈的分区为leader):

KafKa可靠性的保障

KafKa的存储策略
Kafka通过topic来存放数据,而topic中又会有很多的分区我们知道分区是备份在多台主机上的。每一个分区从物理结构上来看就是一个个的文件夹,文件夹的名字就是topic名字加上分区的编号。每个分区下又会有很多的文件,确切的说是有xxx.log与xxx.index这样的两种文件。他们都是成对出现的xxx.index文件就是xxx.log文件(包括一条条的消息数据)的索引文件(包括offset在log中的开始位置以及每条日志的长度),这样一对文件就会组成一个segment,(这些segment是等大的)。当我们要查找一个消息的时候首先会根据文件树目录结构过滤掉大部分的数据,然后进入分区文件夹得到目标segment,再进入到这个segment的索引文件夹中确定offset在数据文件中开始的位置读取信息。

KafKa的同步策略
在KafKa中,判断数据写入成功的策略有两种:

  1. 只要在leader分区中写入成功就算是成功。
  2. 要等待写入leader中的数据同步到所有的fllower中才算是成功。
    实务上这两种策略都比较极端,一方案会导致leader中新的数据还没来得及同步而宕机进而丢失数据,二方案会导致只要有一个节点没有同步完成就会提示没有写入成功进而导致整个分区集群无法写入。在Zookeepr集群中采用的是过半选举的策略,即只要有一半以上的机器同步成功那么就认定这个写入已经成功。
    但是KafKa中采用了不同的策略。KafKa将分区的集合都维护在一个叫做AR的表中表中含有所有分区的副本编号,AR表中的分区(也就是所有的分区)在逻辑上又会分到两个子表中一个是ISR,另一个是OSR,即AR=ISR + OSR。它们的关系如下图所示:
    在这里插入图片描述
    其中ISR为同步列表,也就是说当我们写入一条消息之后只要ISR中的各个节点都得到同步就可以,OSR中为非同步列表我们在写入数据的时候无需关注他们,但是也会发生同步只是不作为判定条件而已。为什么会有这样的策略呢?原因就是ISR中的同步速度较快,而OSR中的同步速度较慢以至于超越了我们规定的阈值(replica.lag.time.max.ms)。这样的话KafKa集群就避免了Zookeeper集群的不过半无法启动集群的问题,即使只有一台KafKa主机就可以启动,但是牺牲了一部分可靠性。当然这对于KafKa消息队列来说是非常有意义的。

KafKa分区中的两个指针LEO与HW
KafKa分区中有LEO与HW两个指针,其中LEO指向新写入的数据,但是HW却指向的是已经同步好的数据,用户只能读到HW之前的数据。而HW到LEO之间的数据对于用户来说是不可见的。这样用户读到的数据非常可靠,他们的关系如下图所示:

在这里插入图片描述
在刚开始HW与LEO相等,当一条新的数据写入时LEO会指向新的数据,等到所有的ISO中的Fllower都同步完成之后HW就会将指针向LEO靠近,直到HW与LEO重合用户就又能够读取到全部的数据了。如下图示:
在这里插入图片描述
当然有一种特殊的情况就是当新的数据写到leader时此时leader突然宕机,此时数据还没有来得及同步,或者是同步未完全。这时我们会有两种策略

等待leader醒来
在ISR列表中新选择一个节点作为leader
这里强调的是如果选用了第二种策略的话那么就会出现不一致的问题。这时我们将会在新选的leader中以HW为界限截断其它所有新的数据,只有这样才能够保证数据的一致性。整个过程如下图所示:
在这里插入图片描述
截断机制虽然保证了数据的一致性,使得数据的访问变得可靠但是,如果ISR中只剩下leader一个人那么,这时如果选择了OSR中的一个人作为leader就会出现数据不一致的问题。

数据生产可靠性的几个层次

在KafKa集群中通过调整request.required.acks参数的值来跟换数据的可靠性。

会因为网络而丢数据:当生产者向KafKa集群发送一条消息时,就像以前的短信一样只负责发送而不负责得到对方的收到回复,这样的话效率会比较高(因为不会核实)但是数据得不到保障。此时的request.required.acks为0。
会因为leader宕机丢数据,或者会因为网络延迟而多数据: 当生产者向KafKa集群发送一条消息时,如果leader接收成功消息会给客户端发送一条消息告诉它发送成功。但是如果此时leader宕机而其它的机器没有来得及同步数据,如果我们等待宕掉的leader复活那么不会丢掉数据,如果我们选择其他的一台机器当做新的leader的话就会由于截断机制导致数据的丢失。
此时还可能出现的情况就是,如果网络延迟在客户端可能发了若干条消息之后才收到回应这时无疑会重发很多条数据。此时的request.required.acks为1。

会因为ISR中只有leader一台机子而丢失数据,会因为网络延迟而多数据。这个情况与②的情况相似。不同的就是当客户端发送一个消息之后会等待所有ISR中的机器都同步完成才发送ack给客户端,这样在一定程度上保证了数据的可靠性但是任然没有避免ISR中单节点的问题。与此同时此时还会发生数据重传。但是此时还有一个配置选项(min.insync.replicas=2),这个配置选项配置了当客户端检测到ISR中最小的机器数大于等于配置值时才能够发送消息。这样终于能够不丢数据了但是还不能够解决数据重传问题。实务上,为了解决数据重传的问题客户端再给KafKa传数据的时候会被标有一个唯一标志这条消息的GUID,但仅仅是标识KafKa不会自动去重(随机IOà顺序IO效率的保障)。这就要求我们在业务层面实现数据的去重。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值