kafka 深入浅出

一、kafka 集群评估

搞定10亿请求,需要5台物理机,11 (SAS) * 7T ,需要64G的内存(128G更好),需要16个cpu core (32个更好)

1.1 内存评估

我们发现kafka读写数据的流程都是基于os cache,换句话说假设咱们的os cashe无限大
那么整个kafka是不是相当于就是基于内存去操作,如果是基于内存去操作,性能肯定很好。
内存是有限的。

  1. 尽可能多的内存资源要给os cache
  2. Kafka的代码用 核心的代码用的是scala写的,客户端的代码java写的。
    都是基于jvm。所以我们还要给一部分的内存给jvm。
    Kafka的设计, 没有把很多数据结构都放在j v m 里面。所以我们的这个j v m 不需耍太大的内存。
    根据经验,给个10G就可以了。
    NameNode: I
    jvm里面还放了元数据(几十G) , JVM一定要给得很大。比如给个100G。

假设我们这个10亿请求的这个项目 ,一共会有100个topic。
100 topic * 5 partition * 2 = 1000 partition
一个partition其实就是物理机上面的一个目录,这个目录下面会有很多个.log的文件。
.log就是存储数据文件.默认情况下一个.log文件的大小是1G。
我们如果要保证1000个partition的最新的.log文件的数据如果都在内存里面,这个时候
性能就是最好。1000 * 1G = 1000G内存.
我们只需要把当前最新的这个log保证里面的25%的最新的数据在内存里面。
250M * 1000 = 0.25 G* 1000 =250G的内存。
250内存/ 5 = 50G内存
50G+10G = 60G 内存
64G的内存,另外的4G,操作系统本生是不是也需要内存。
其实Kafka的jvm也可以不用给到10G这么多。
评估出来64G是可以的。
当然如果能给到128G的内存的服务器,那就最好。
我刚刚评估的时候用的都是一个topic是5个partition,但是如果是数据量比较大的topic,可能会有10个partition。
总结.
搞定10亿请求,需要5台物理机,11 (SAS) * 7T ,需要64G的内存(128G更好)

1.2 cpu 评估

依据就是看我们的服务里面有多少线程去跑。
线程就是依托cpu去运行的
如果我们的线程比较多,但是cpu core比较少的话.我们的机器负载就会很高,性能就不好
1)评估一下,kafka的一台服务器,启动以后会有多少线程
1.Acceptor线程 1
2.processor 线程 3 6-9个线程
3.处理请求线程 8个 32个线程
4.定时清理的线程,拉取数据的线程,定时检查ISR列表的机制等等。
所以大概一个kafka服务运行起来以后,会有100多个线程

cpu core = 4,一般来说,几十个线程就肯定把cpu打满了。
cpu core = 8,应该很轻松的能支持几十个线程。
如果我们的线程是100多个、或者差不多200多个;8个cpu core是搞不定了
所以我们这儿建议:cpu core 16个,32个更好

结论:
kafka集群,最低要16个cpu core,如果能到32个cpu core 那就更好。
2cpu * 8 = 16 cpu core
4cpu * 8 = 32 cpu core
总结.
搞定10亿请求,需要5台物理机,11 (SAS) * 7T ,需要64G的内存(128G更好),需要16个cpu core (32个更好)

1.3 网卡评估

一般要么是千兆的网卡 (IG/S),还有的就是万兆的网长(10G/S)
高峰期的时候每秒会有5.5万的请求涌入,5.5/5 =大约是每台服务器会有1万个请求涌入。
我们之前说的,
10000 * 50kb = 488M 也就是每条服务器,每秒要接受488M的数据。数据还要有副木,副本之间的同步
也是走的网络的请求。488 * 2 = 976m/s
说明一下:
很多公司的数据,一个请求里面是没有50kb这么大的,我们公司是因为主机在生产端封装了数据
然后把多条数据合并在一起了,所以我们的一个请求才会有这么大。
说明一下:
一般情况下,网卡的带宽是达不到极限的,如果是千兆的网卡,我们能用的一般就是700M左右。
但是如果最好的情况,我们还是使用万兆的网卡。
如果使用的是万兆的,那就是很轻松。

二、异常处理

1)LeaderNotAvailableException:
这个就是如果某台机器挂了,此时leader副本不可.用,会导致你写入失败,、
要等待其他follower副本切换为leader副本之后,才能继续写入,此时可以重试发送即可
如果说你平时重启kafka的broker进程,肯定会导致leader切换,一定会导致你写入报错,
是LeaderNotAvailableException
2) NotControllerException: 这个也是同理,如果说Controller所在Broker挂了,
那么此时会有问题,需耍等待Controller重新选举,此时也是一样就是重试即可
3) NetworkException: 网络异常 timeout
a.配置.retries参数,他会自动重试的
b.但是如果重试几次之后还是不行,
就会提供Exception给我们来处理了,我们获取到异常以后,再对这个消息进行单独处理,
我们会有备用的链路。

三、如何提升吞吐量

参数一:
buffer.memory:设置发送消息的缓冲区,默认值是33554432,就是32MB
参数二:
compression, type:默认是none,不压缩,但是也可以使用Iz4JK缩,效率还是不错的,
压缩之后可以减小数据量,提升吞吐量:,但是会加大producer端的cpu开销
参数三:
batch.size:设置.batch的大小,如果batch太小,会导致频繁网络请求,吞吐量下降;
如果batch太大,会导致一条消息需要等待很久才能被发送出去,而且会让内存缓冲区有很大压力,
过多数据缓冲在内存里, 默认值是: 16384 , 就是 16 k b , 也就是一个batch 满了 16 kb 就发送出去,
一般在实际生产环境,这个batch的值可以增大一些来提升乔吐量:

如果我们消息比较少
配合使用的参数linger.ms,这个值默认是0,意思就是消息必须立即被发送,但是这是不对的,
一般设置一个100亳秒之类的,这样的话就是说,这个消息被发送出去后进入一个batch,
如果100毫秒内,这个batch满了 16kb,自然就会发送出去

四、消息重复乱序处理

1、消息重复
有的时候一些leader切换之类的问题,需要进行重试,设置retries即可,
但是消息重试会导致,重复发送的问题,比如说网络抖动一下导致他以为没成功,就重试了,
其实人家都成功了.
两次重试的间隔默认是100毫秒,用“retry .backoff.ms”来进行设置
基本上在开发过程中,靠重试机制堪本就可以搞定95%的异常问题

2.消息乱序
消息重试是可能导致消息的乱序的,因为可能排在你后面的消息都发送出去了。
所以可以使用" m a x . i n . f l i g h t . r e q u e s t s . p e r . c o n n e c t i o n " 参数设置为1 ,
这样可以保证producer同一时间只能发送一条消息。

五、参数设置:

producer端

request.required.acks = 0;
只要请求已发送出去,就算是发送完了,不关心有没写成功。
性能很好,如果是对一些日志进行分析,可以承受丢数据的情况,用这个参数性能会很好
request.required.acks = 1;
发送一条消息,当leader partition写入成功以后,才算写入成功。
不过这种方式会有丢数据的可能。
request.required.acks = -1;
需要ISR列表里面所有副本都写完以后才算写入成功
ISR: 1 个副本。1 leader partition 1 follower partition

kafka服务端

min.insync.replicas: 1 如果我们不设置的话,默认这个值是1
一个leader partition会维护一个ISR列表 ,这个值就是限制ISR列表里面至少得有几个副本,
比如这个值是2,那么当ISR列表里只有一个副本的时候,往这个分区插入数据的时候就会报错

数据不丢失方案:
1.分区副本 >= 2 2 3
2.ack = -1
3.min.insync.replicas >= 2

heartbeat.interval.ms:
consumer心跳时间间隔,必须得与coordinator保持心跳才能知道consumer是否故障了
然后如果故障之后,就会通过心跳下发rebalance的指令给其他的comsumer通知他们进行rebalance操作

session.timeout.ms:
kafka长时间感知不到一个consumer就认为他故障了,默认是:10秒

max.poll.interval.ms:
如果住两次poll操作之间超过了这个时间,那么就认为这个comsumer处理能力太弱了,会被剔除消费组,分区分配给别人去消费,
需结合自己的业务处理的性能来设置

fetch.max.bytes:
获取一条消息最大的字节数,一般建议设置大一些,默认为 1m

producer发送的数据,一条消息最大多大 1m -> 10m
Broker 存储数据,一条消息最大能接受多大 1m -> 10m
Consumer
max.poll.records: 一次poll返回消息的最大条数,默认是500条
connection.max.idle.ms: consumer跟broker的socket连接如果空闲超过一定时间,
就会自动回收连接,但是下次消费就要重新建立socket连接,这个建议设置为 -1,不要去回收

enable.auto.commit
开启自动提交偏移量
auto.commit.interval.ms:
每隔多久提交一次偏移量,默认值5000 亳秒
auto.offset.reset:
1.earliest: 当各分区下有已提交的offset时,从提交的offset开始消费,无提交offset时,从头开始消费
2.latest: 当各分区下有已提交的offset时,从提交的offset开始消费,无提交offset时,消费新产生的该分区下的数据 ?
3.none: topic各分区都存在已提交的offset时,从offset后开始消费,只要有一个分区不存在已提交的offset,则抛出异常

六、消费者是如何实现rebalance的?

1、什么是coondinator

每个consumer group 都会选择一个broker作为自己的coondinator
他是负责监控这个消费组里的各个消费者的心跳,以及判断是否宕机,然后开启rebalance的

2.如何选择coondinator机器

首先对groupid进行hash(数字) ,接着对 __consumer_offets的分区数量取模,默认为50,
(__consumer_offets 的分区数可以通过offset.topic.num.partitions来设置,)
找到分区以后,这个分区所在的broker机器就是coondinator机器。
如:
consumer group:
group.id: my_conumser -> my consumer hash -> 求出来就是—个数字
数字/50 =>获取到 0-49的这样的一个数。比如数就是8,
然后就在找到 __consumer_offets的partition 8 的leader partition在
哪台服务器上面。那台就是coondinator服务器。|
__consumer_offets 默认有50个分区

3、rebalance流程

  1. 每个consumer都发送JoinGroup请求到Coordinator
  2. 然后 Coordinator 从一个consumer group 中选择一个 consumer 作为 leader
    (谁先注册上来,谁就是leader consumer)
  3. Coordinator 把consumer group情况发送给这个leader
  4. 接着这个leader会负责制定消费方案
  5. 通过SyncGroup发给Coordinator
  6. 接着Coordinator 就把消费方案下发给各个consumer,他们会从指定的分区的
    leader broker开始进行socket连接以及消费消息

4、三种rebalance策略

range> round-robin> sticky
比如我们消费的一个主题有12个分区:
p0,pl,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11

  1. range 策略
    range策略就是按照partiton的序号范围
    partition 0~3 给consumer1
    partition 4~7 给一个 consumer2
    partition 8~11给一个 consumer3
    默认就是这个策略;

2、round-robin 策略
就是轮询分配
consumer1:0,3,6,9
consumer2:1,4,7,10
consumer3:2,5,8,11

但是前面的这两个方案有个问题:
12 -» 2每个消费者会消费6个分区
假设consumer1 挂了,p0 - 5 分配给consumer2, p6-11分配给consumer3?
这样的话,原本在consumer2上的的p6,p7分区就被分配到consumer3上

3.sticky 策略
最新的一个stick策略,就是说尽可能的保证在rebalance的时候,让原本属于这个consumer的分区还是属于他们
然后把多余的分区再均匀分配过去, 这样尽可能维持原来的分区分配的策略.
consumer1:0-3
consumer2:4-7
consumer3:8-11
假设comsumer3挂了
consumer1:0-3 +8,9
consumer2:4-7 +10,11

七、kafka延迟调度机制

第一类延时的任务:
比如说producer的acks=-1,必须等待leader 和follower都写完了才能返回响应。
有一个超时时间,默认是30秒(request.timeout.ms)。
所以需要在写入一条数据到leader磁盘后就必须有一个延时任务,
到期时间是30s延时任务放到(DelayedOperatitonPurgatory(延时管理器))中,在30s之前如果所有follower都写入副本到本地磁盘了
那么这个任务就会被自动触发苏醒,就可以返回响应结果给客户端了,
否则的话,这个延时任务自己指定了最多是30s到期,如果到了超时时间都没等到,就直接超时返回异常。

第一类延时的任务:
follower往leader拉取消息时,如果发现是空的,此时会创建一个延时拉取任务,
延时时间到了之后(比如到了100ms),就给follower返回一个空的数据,然后follower再次发送请求读取消息,
但是如果延时的过程中(还没到100ms),leader写入了消息,这个任务就会自动苏醒,自动执行拉取任务

为什么要设计时间轮?
kafka内部有很多延时任务没有基于JDK Timer来实现,那个插入和删除任务的时间复杂度是O(nlogn)
而是基于了自己写的时间轮来实现的,时间复杂度是O(l),依靠时间轮机制,延时任务插入和删除

时间轮是什么
tickMs: 时间轮间隔
wheelSize:时间轮大小
interval:tickMs * wheelSize,一个时间轮的总的时间跨度
currentTime:当前时间的指针

a: 因为时间轮是一个数组,所以要获取里面数据的时候,靠的是index,时间复杂度是O(l)
b: 数组某个位置上对应的任务,用的是双向链表存储的,往双向链表里面插入和删除任务,时间复杂度也是O(l)

举例:插入一个8ms以后要执行的任务

3、多层级的时间轮
比如:比如要插入一个110ms以后运行的任务
第一层级时间轮: 1ms * 20
第二层级时间轮: 20ms * 20
第三层级时间轮: 400ms * 20
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值