在说Kafka的高可靠性之前,先在评论区说说RNGNB好吗?什么是可靠性?大家都知道系统架构有“高性能、高并发、高可用”三高。三者的重要性不言而喻。对于任何一个系统来说,同时满足三高都是非常困难的。大型业务系统或传统的中间件都会构建复杂的架构来确保它。除了以上三种模式,还有一个指标方向也很重要,那就是高可靠,你甚至可能会把它和“高可用”混淆。事实上,两者并不相同。高可用会更倾向于整体服务的可用性,防止系统宕机等。而高可靠性是指数据的可靠性保证。你可以理解为“高可靠性”是比三高系统更精细的概念。那么什么是数据的高可靠性呢?综上所述,系统必须提供可靠的数据支持,不能出现丢失、重复等错误。所以每一个开源中间件在发布版本的时候都会通过文档来声明自己是超可靠的,就像每个暖男在520那天说的一样。我们今天的主角卡夫卡就是这样一个例子。一些重要的概念是因为有一段时间没有讲消息队列了。为了帮助大家更好的理解文章,我们先来回顾一下Kafka的基本概念:记录:消息,消息队列的基本通信单元。topic:主题,目的是对消息进行分类,不同业务类型的消息通常会分发到不同的主题中。partition:分区,每个主题可以创建多个分区,每个分区由一系列有序且不可变的消息组成。复制品:复制品。每个分区都有一个或多个副本。它的主要功能是存储和保存数据,以日志(Log)对象的形式体现。副本又分为leader副本和follower副本。offset:偏移量,每条消息在日志文件中的位置对应一个依次递增的偏移量,可以理解为类似于数组的存储形式。producer:生产者,生产消息的一方。Consumer:消费者,通常不同的商家会有一个或多个消费者组成一个消费者集群。broker:代理,一个Kafka集群由一个或多个Kafka实例组成,每个Kafka实例称为一个代理。如上图,一共有topic1和topic2,topic1有两个partition,topic2只有一个partition,每个partition有一个leader副本和两个follower副本,分布在各个不同的proxy中节点上级。在分区中,只有leader副本负责与生产者和消费者的数据交互,follower副本会周期性地从leader副本拉取数据,保证整个集群的数据可用性。如何保证数据的高可靠性Kafka是通过副本机制来实现数据存储的,所以需要一些机制来保证跨集群的副本之间数据能够可靠传输。1、副本同步采集的业务数据封装成消息在系统中流通。由于每个组件分布在不同的服务器上,因此主题与生产者和消费者之间的数据同步可能存在一定的时间延迟。Kafka通过延迟范围分为几个不同的集合:AR(AssignedReplicas)是指分配数据的分区副本,通常是指leader副本+follower副本。ISR(InSyncReplicas)是指与领导副本数据保持同步的副本集。当follower副本数据和leader副本数据保持同步时,这些副本都在ISR中,ISR集合会根据数据的同步状态动态变化。OSR(OutSyncReplicas)一旦follower副本的数据同步进度跟不上leader,就会被放入一个叫OSR的集合中。也就是说,此集合包含不同步的分区副本。OK,那么判断是否同步的标准是什么呢?使用参数replica.lag.time.max.ms设置数据同步时间差,默认值为10s。一旦从分区副本和主分区副本之间的消息差异超过10s,则认为消息处于OSR失步状态。如果follower在OSR集合中,那么在选择新的leader时,它不会被选为新的leader。2、ACK响应机制刚才说了Kafka通过ack发送数据同步信号,那么信号发送频率有多少种设置呢?ack=0,生产者发送一次消息后不再发送。不管发送成功与否,如果发送的消息在通信途中丢失,或者没有进行磁盘持久化操作,都可能导致消息丢失。它的优点是性能非常高。想想看,不用等对方回复再发下一批消息,这样消息的等待时间就省了。它可以在相同的时间范围内处理比其他人更多的数据。缺点是它的可靠性真的很低,数据真的是一丢就丢。ack=1leader收到消息并写入本地磁盘后认为消息处理成功。这种方法的可靠性会比前一种好。当leader收到消息并写入本地磁盘时,就认为消息处理成功。不管follower有没有同步,消息都会返回给producer。但是如果此时partitionleader所在的broker宕机,那么数据也有可能丢失,所以follower副本的数据同步非常重要。Kafka默认使用这种方式。ack=-1生产者只有收到分区内所有副本的响应ACK才会认为消息推送成功。这种方式虽然在保证数据可靠方面做得很好,但是性能较差,影响吞吐量,所以一般不采用。那么它是万无一失的吗?不必要。最重要的还是要看副本数据是否同步。如果leader副本在producer收到response消息之前就挂了,producer会因为没有收到消息而重复发送消息,可能会造成数据重复。如何解决?只需确保业务是幂等的。我们可以通过request.required.acks参数来控制消息的发送频率。3.消息语义消息集群作为一个整体是一个复杂的系统,在这个过程中可能会因为各种原因导致消息传递错误。Kafka为这些可能的场景定义了相应的消息语义。atmostonce表示消息可能被消费者消费0次或1次。如果场景如下:消息从分区分发到消费者集群。消费者将收到的消息告诉集群,集群收到后偏移量会向后移动。消费者存储数据以实现持久性。你一定已经想到了。第三步,如果消费者A在消费者入库的时候因为某种原因挂掉了,那么这个消费者切换到集群的消费者B之后,数据还没有入库。这时候分区是完全未知的,所以这会造成一个问题:数据丢失。至少一次表示分区分发的消息至少被消费一次。通信过程如下:消息从分区分发到消费者集群。消费者存储数据以实现持久性。消费者将收到的消息告诉集群,集群收到后偏移量会向后移动。假设consumerA在consumergroup中存入数据后,在返回数据给partition的过程中挂了,那么partition会因为收不到响应ACK而重新发送数据。这时,消费者B可能会将原来的消息重新放入分区中。库,从而导致数据重复。在没有任何幂等性保护的情况下,比如重复转账、还款、叠加积分,结果可能是致命的。exactlyonce表示消息可以恰好被消费一次而不会丢失或重复。根据至少一次的情况,假设consumerA在向partition返回ack的过程中崩溃了。那么consumerB就不会按照partition的offset了,它会先去数据库查看最新消息对应的offset,然后根据这个offset返回到kafka集群从对应的offset开始,这样可以避免消息重复和消息丢失。4、数据截断机制我们一开始就说过,真正处理数据的是leader副本,follower副本只负责数据的同步和存储。如果因为leader挂了导致两者数据不一致怎么办?在讲一致性保证过程之前,还有必要了解一下Kafka用来表示副本数据同步的两个概念:HW(HighWatermark):中文翻译为高水位线,用于反映副本间数据同步的相对位置.消费者最多只能在HW所在的地方消费。通过HW我们可以判断数据是否对副本可见。LEO(LogEndOffset):下一条要写入的消息的记录位置。leader副本从producer获取消息,follower副本从leder实时同步数据。此时他们的同步数据是一致的,同步到2的位置,下一条写入的消息是offset4:假设意外的leader宕机,follower被选为新的leader,然后最新的offset4而5个是producer写的:一段时间后,原leader通过修复恢复服务,会发现自己和新leader之间的数据不一致的:为了保证数据的一致性,一方必须被迫妥协。因为数据在不断刷新,此时老leader的优先级会低于新leader,所以会把自己的数据截断到和新leader相同的HW和LEO位置,保证数据新领导者的身份必须相同。就是Kafka的数据截断机制。5、数据清洗机制与其他中间件相同。Kafka的主要功能是通信,所以即使数据保存在磁盘上,仍然会占用一定的空间。为了节省存储空间,它会通过一些机制清理过期数据。日志删除日志删除会直接删除日志段,Kafka会维护一个定时任务定期检查和删除“过期数据”。基于时间的日志删除在每个日志段文件中维护一个最大时间戳,以确认当前配置的删除时间,每当有新消息写入日志段时,该字段就会更新。一个日志段写满后,将不再接收新的消息,它会创建一个新的日志段文件向其中写入数据。每个日志段文件被填满后,其最大时间戳保持不变。Kafka可以通过比较当前时间和最大时间戳来判断日志段文件是否过期。Kafka默认配置log.retention.hours=168,也就是7天的日志保留时间。基于容量大小的日志删除和上面的方法是一样的,只是这次换了时间到空间。Kafka会根据每个日志段空间的大小计算一个总容量阈值,然后计算当前实际空间大小与总容量阈值的差值。如果差异大于单个日志段文件的大小,则最旧的文件将被删除。日志段文件,否则不做任何处理。同样,这个阈值也可以通过log.retention.bytes参数来设置。日志压缩Kafka的消息是由键值组成的。如果日志段中存在多条key相同但value不同的数据,它会选择性的清除旧的数据,保留最新的记录。具体的压缩方式是创建一个checkpoint文件,从日志的起始位置遍历到最大结束位置,然后将每条消息的key和key对应的offset保存在一个固定容量的SkimpyOffsetMap中。这样,前面的值就会被后面的值覆盖掉。如果日志文件中存在相同的键,则只保留最新的一个。总结Kafka通过ACK响应机制保证不同组件之间的通信效率,通过副本同步机制、数据截断和数据清洗机制实现数据管理策略,保证整个系统的运行效率。作为同时具有高性能和高可靠性的消息中间件,Kafka可以夸耀的地方太多了。如果本文对您有帮助,请点击右下角的小拇指,下次我们会详细讲解Kafka是如何实现副本间数据传输的。
