阿里P7之无中生有一
个人专题目录:
Kafka
(1)如何保证宕机的时候数据不丢失?
如果要想理解这个acks参数的含义,首先就得搞明白kafka的高可用架构原理。
比如下面的图里就是表明了对于每一个Topic,我们都可以设置他包含几个Partition,每个Partition负责存储这个Topic一部分的数据。
然后Kafka的Broker集群中,每台机器上都存储了一些Partition,也就存放了Topic的一部分数据,这样就实现了Topic的数据分布式存储在一个Broker集群上。
(2)多副本冗余的高可用机制
所以如果大家去分析任何一个分布式系统的原理,比如说zookeeper、kafka、redis cluster、elasticsearch、hdfs,等等,其实他都有自己内部的一套多副本冗余的机制,多副本冗余几乎是现在任何一个优秀的分布式系统都一般要具备的功能。
在kafka集群中,每个Partition都有多个副本,其中一个副本叫做leader,其他的副本叫做follower
这样的多副本冗余机制,可以保证任何一台机器挂掉,都不会导致数据彻底丢失,因为起码还是有副本在别的机器上的。
(3)多副本之间数据如何同步?
接着我们就来看看多个副本之间数据是如何同步的?其实任何一个Partition,只有Leader是对外提供读写服务的
也就是说,如果有一个客户端往一个Partition写入数据,此时一般就是写入这个Partition的Leader副本。
然后Leader副本接收到数据之后,Follower副本会不停的给他发送请求尝试去拉取最新的数据,拉取到自己本地后,写入磁盘中。
(4)ISR到底指的是什么东西?
既然大家已经知道了Partiton的多副本同步数据的机制了,那么就可以来看看ISR是什么了。
ISR全称是“In-Sync Replicas”,也就是保持同步的副本,他的含义就是,跟Leader始终保持同步的Follower有哪些。
大家可以想一下 ,如果说某个Follower所在的Broker因为JVM FullGC之类的问题,导致自己卡顿了,无法及时从Leader拉取同步数据,那么是不是会导致Follower的数据比Leader要落后很多?
所以这个时候,就意味着Follower已经跟Leader不再处于同步的关系了。但是只要Follower一直及时从Leader同步数据,就可以保证他们是处于同步的关系的。
所以每个Partition都有一个ISR,这个ISR里一定会有Leader自己,因为Leader肯定数据是最新的,然后就是那些跟Leader保持同步的Follower,也会在ISR里。
(5)acks参数的含义
铺垫了那么多的东西,最后终于可以进入主题来聊一下acks参数的含义了。
如果大家没看明白前面的那些副本机制、同步机制、ISR机制,那么就无法充分的理解acks参数的含义,这个参数实际上决定了很多重要的东西。
首先这个acks参数,是在KafkaProducer,也就是生产者客户端里设置的
也就是说,你往kafka写数据的时候,就可以来设置这个acks参数。然后这个参数实际上有三种常见的值可以设置,分别是:0、1 和 all。
第一种选择是把acks参数设置为0,意思就是我的KafkaProducer在客户端,只要把消息发送出去,不管那条数据有没有在哪怕Partition Leader上落到磁盘,我就不管他了,直接就认为这个消息发送成功了。
如果你采用这种设置的话,那么你必须注意的一点是,可能你发送出去的消息还在半路。结果呢,Partition Leader所在Broker就直接挂了,然后结果你的客户端还认为消息发送成功了,此时就会导致这条消息就丢失了。
第二种选择是设置 acks = 1,意思就是说只要Partition Leader接收到消息而且写入本地磁盘了,就认为成功了,不管他其他的Follower有没有同步过去这条消息了。
这种设置其实是kafka默认的设置,大家请注意,划重点!这是默认的设置
也就是说,默认情况下,你要是不管acks这个参数,只要Partition Leader写成功就算成功。
但是这里有一个问题,万一Partition Leader刚刚接收到消息,Follower还没来得及同步过去,结果Leader所在的broker宕机了,此时也会导致这条消息丢失,因为人家客户端已经认为发送成功了。
最后一种情况,就是设置acks=all,这个意思就是说,Partition Leader接收到消息之后,还必须要求ISR列表里跟Leader保持同步的那些Follower都要把消息同步过去,才能认为这条消息是写入成功了。
如果说Partition Leader刚接收到了消息,但是结果Follower没有收到消息,此时Leader宕机了,那么客户端会感知到这个消息没发送成功,他会重试再次发送消息过去。
此时可能Partition 2的Follower变成Leader了,此时ISR列表里只有最新的这个Follower转变成的Leader了,那么只要这个新的Leader接收消息就算成功了。
(6)最后的思考
acks=all 就可以代表数据一定不会丢失了吗?
当然不是,如果你的Partition只有一个副本,也就是一个Leader,任何Follower都没有,你认为acks=all有用吗?
当然没用了,因为ISR里就一个Leader,他接收完消息后宕机,也会导致数据丢失。
所以说,这个acks=all,必须跟ISR列表里至少有2个以上的副本配合使用,起码是有一个Leader和一个Follower才可以。
这样才能保证说写一条数据过去,一定是2个以上的副本都收到了才算是成功,此时任何一个副本宕机,不会导致数据丢失。
如果让你设计一个消息中间件,如何将其网络通信性能优化10倍以上
1、客户端与服务端的交互
假如我们用kafka作为消息中间件,势必会有客户端作为生产者向他发送消息,这个大家应该都可以理解。
对于Kafka来说,他本身是支持分布式的消息存储的,什么意思呢?
比如说现在你有一个“Topic”,一个“Topic”你就可以理解为一个消息数据的逻辑上的集合。
比如现在你要把所有的订单数据都发送到一个“Topic”里去,那么这个“Topic”就叫做“OrderTopic”,里面都放的是订单数据。
接着这个“Topic”的数据可能量很大很大,不可能放在一台机器上吧?
所以呢,我们就可以分散存储在多台Kafka的机器上,每台机器存储一部分的数据即可。
这就是Kafka的分布式消息存储的机制,每个Kafka服务端叫做一个Broker,负责管理一台机器上的数据。
一个“Topic”可以拆分为多个“Partition”,每个“Partition”存储一部分数据,每个Partition都可以放在不同的Kafka Broker机器上,这样就实现了数据分散存储在多台机器上的效果了。
然后客户端在发送消息到Kafka Broker的时候,比如说你限定了“OrderTopic”的订单数据拆分为3个“Partition”,那么3个“Partition”分别放在一个Kafka Broker上,那么也就是要把所有的订单数据分发到三个Kafka Broker上去。
此时就会默认情况下走一个负载均衡的策略,举个例子,假设订单数据一共有3万条,就会给每个Partition分发1万条订单消息,这样订单数据均匀分散在了3台Broker机器上。
2、频繁网络通信带来的性能低下问题
好了,现在问题来了,客户端在发送消息给Kafka Broker的时候,比如说现在要发送一个订单到Kafka上去,此时他是怎么发送过去呢?
是直接一条订单消息就对应一个网络请求,发送到一台Broker上去吗?
如果是这样做的话,那势必会导致频繁的跟一台broker进行网络通信,频繁的网络通信,每次都涉及到复杂的网络连接、传输的流程,那么进而会导致客户端性能的低下。
给大家举个例子,比如说每次通过一个网络通信发送一条订单到broker,需要耗时10ms。
那么如果一个订单就一次网络通信发送到broker,每秒最多就是发送100个订单了,大家想想,是不是这个道理?
但是假如说你每秒有10000个订单要发送,此时就会造成你的发送性能远远跟不上你的需求,也就是性能的低下,看起来你的系统发送订单到kafka的速度就是特别的慢。
3、batch机制:多条消息打包成一个batch
所以首先针对这个问题,kafka做的第一个优化,就是实现了batch机制。
这个意思就是说,他会在客户端放一个内存缓冲区,每次你写一条订单先放到内存缓冲区里去,然后在内存缓冲区里,会把多个订单给打包起来成为一个batch。
比如说默认kafka规定的batch的大小是16kb,那么意思就是,你默认就是多条订单凑满16kb的大小,就会成为一个batch,然后他就会把这个batch通过网络通信发送到broker上去。
假如说一个batch发送到broker,同样也是耗费10ms而已,但是一个batch里可以放入100条订单,那么1秒是不是可以发送100个batch?
此时,1秒是不是就可以发送10000条订单出去了?
而且在打包消息形成batch的时候,是有讲究的,你必须是发送到同一个Topic的同一个Partition的消息,才会进入一个batch。
这个batch里就代表要发送到同一个Partition的多条消息,这样后续才能通过一个网络请求,就把这个batch发送到broker,对应写入一个Parititon中。
4、request机制:多个batch打包成一个request
比如现在我们要是手头有两个Topic,每个Topic都有3个Partition,那么每个Broker是不是就会存放2个Partition?其中1个Partition是Topic01的,1个Partition是Topic02的。
现在假如说针对Topic01的Partition02形成了一个batch,针对Topic02的Partition02也形成了一个batch,但是这两个batch其实都是发往同一个Broker的,如上图的第二个Broker。
此时,还是一个网络请求发送一个batch过去吗?
其实就完全没必要了,完全此时可以把多个发往同一个Broker的batch打包成一个request,然后一个request通过一次网络通信发送到 那个Broker上去。
假设一次网络通信还是10ms,那么这一次网络通信就发送了2个batch过去。
通过这种多个batch打包成一个request一次性发往Broker的方式,又进一步提升了网络通信的效率和性能。
其实 batch机制 + request 机制,都是想办法把很多数据打包起来,然后一次网络通信尽量多发送一些数据出去,这样可以提升单位时间内发送数据的数量。
这个单位时间内发送数据的数量,也就是所谓的“吞吐量”,也就是单位时间内可以发送多少数据到broker上去。
比如说每秒钟可以发送3万条消息过去,这就是代表了客户端的“吞吐量”有多大。
因此,通过搞清楚这个原理,就可以学习到这种非常优秀的设计思想。而且在面试的时候,如果跟面试官聊到kafka,也可以跟面试官侃侃kafka底层,是如何有效的提升网络通信性能的。