摘要
我们上一节讲解了Kafka架构-底层原理:从基础的partition里面的offset引申到LEO和HW;以及对于Leader partition跟follower partition而言,他们的LEO跟HW是如何更新的的?以及高水位HW在leader选举切换时候,存在的数据丢失跟数据不一致问题,以及通过leader epoch版本号的概念引入解决上面问题,由于HW的变更还跟leadder partition里面的ISR列表有关联,又讲解了:ISR列表设计的0.9.x之前版本方式以及0.9.x之后的版本通过follower消息数比leader消息数目少方式更新LSR列表,以及根据时间来更新ISR列表。然后这些都讲解完毕之后,我们讲解了Kafka的日志是如何存储存储到对应目录列表的,然后怎样基于存储日志快速找到对应offset的数据的二分查找法;这些都讲解完毕之后,讲解了Kafka底层集群里面节点如何通信?以及集群节点如果进行管控,Leader选举,线上崩溃以及数据清理。
这些了解之后,我们便可进行Kafka实际线上生产规划以及搭建。主要说明以下:
1、基于实际业务进行kafka集群如何规划?需要考虑哪些问题?
2、Kafka集群生产参数配置:包括Kafka自己内核参数、操作系统参数、JVM和GC参数。
3、Kafka单点搭建用于测试、Kafka集群搭建
4、具备环境之后,我们需要进行一些运维工作:Kafka的测试。
思维导图
内容
1、基于实际业务进行Kafka集群生产规划(电商系统每日10亿数据量)
基于实际生产业务的时候,我们规划kafka集群的时候,思考方向是:基于每天数据量计算出系统的大概QPS,根据QPS可以计算出需要多少台物理机器;根据每天数据量预估整体数据量大小,在结合Kafka自身的原理,保留时间长短,以及多冗余副本机制防止数据丢失来预估整体数据量大小来规划机器测硬盘大小。这些都做完之后,就需要具体到一台物理机或者虚拟机了,主要考虑:cpu(每台机器的线程数)、内存(Kafka自身底层原理)、磁盘(kafka底层原理分析)、网络(根据数据量)
1、QPS
基于10亿数据量:10亿数据量 根据2-8原则,80%的数据是在非凌晨0-8点的16小时产生,并且其中80%的请求都是在20%的时间内,所以:100.80.8=6亿数据在:1620%=3小时;QPS=600000000/360*60近似为6万。
2、存储量
总存储量=时间*每天存储量。
每天10亿数据量,我们假设每条数据大小大概是1kb,其实很多时候平均是达不到1kb的数据。我们粗略估计下:假设一个英文字母是1个字节byte,1个中文汉子是2个字节byte,那么1kb可以容纳1024个英文字符/512个汉子;10亿数据量,是10亿kb大小数据,kafka假设我们按照2个副本,数据默认保留7天,则 整体数据量=1027=140亿kb近似按照12TB数据量算。
3、物理机
1、物理机数量
物理机数量一般根据qps计算,由于qps是6万左右,一般一台机器大概抗并发是3万左右,由于在机器资源充足情况下,一般为了应对秒杀这种场景,计算的qps=总体qps*30%;所以我们的最大qps为20万qps左右,所以我们的机器大概在6-7台机器,这里我们选择6台机器。
2、物理机cpu
经验值
是情况而定,我们为了增加kafka的吞吐量的话,我们将会把处理请求的线程数量调整为默认的2~3倍
Kafka Broker进程运行起来线程数预估
根据Kafka多路复用的Reeactor模型处理请求时候,是需要一个Accepptor线程,Processor线程用来将请求写入到请求队列,默认是3个;然后具体进行请求处理的是一个Handler线程池,默认线程数量是8个;以及后台数据清理线程几controller选举等线程。按照2~3倍原则,我们的线程数:24-36;然后加上日志清理线程,或者Controller感知线程等,我们将预估线程繁忙的时候,线程数量为一两百。
cpu核跟线程数的经验值
1、CPU 4核,一般来说几十个线程,在高峰期CPU几乎都快打满了,
2、CPU 8核,也就能够比较宽裕的支撑几十个线程繁忙的工作,
3、一般是建议16核,靠谱,基本上可以达到一两百线程的工作。
broker一般都会启动几十个甚至上百个线程,大家看过broker端的原理了,各种处理请求的线程,后台线程,几十个线程频繁工作,一般都建议是16核CPU,甚至32核CPU
3、物理机内存
由于Kafka写数据是:写入os_cache+磁盘顺序写,kafka读取数据:零拷贝+页缓冲技术(os_cache)
所以我们发现Kafka是大量依靠os_cache,而不是JVM里面的内存;所以我们规划机器内存的时候,主要是考虑os_cache内存大小,辅助考虑下机器内存大小。
kafka jvm堆内存,你觉得需要很大吗?
了解了kafka原理之后,kafka并没有在自己的jvm堆内存里放入过多的数据,他跟rabbitmq是不一样的,rabbitmq是数据过来优先写入jvm堆内存里去缓冲一下,一定时间之后,rabbitmq再一次性把jvm堆内存里的缓冲一批数据给刷入磁盘中。rabbitmq那种机制跟原理的话会导致jvm堆内存中存放大量的数据,需要给jvm堆内存开辟比较大的空间了。
但是kafka正好相反的,他接收到了这个数据之后不是直接写jvm堆内存的,而是采用自己的二进制紧凑的数据格式,给写入到磁盘文件里去,但是是先写入os cache(操作系统管理的一块内存缓冲空间)kafka并没有使用过多的jvm堆内存空间,所以的话你不需要给kafka jvm堆内存分配过大的空间,基本上来说几个G就够了。
充分利用os_cache提高性能:
我们考虑下os_cache分配多大;
原则: broker管理的每个parition的最新正在写的日志段文件的数据都可以驻留在os cache中,这样可以保证每个parition正在写的数据,最有可能被消费的数据,就可以直接从os cache里来读了。
分析:
假设整个Kafka的集群里面有100个topic,假设每个topic是6个partition,每个partiton是2个副本,但是其实不会从partition的副本读取数据,你只需要考虑的是leader partirion,所以一共100 * 6 =600个leader partition,平均到6台机器上去,假设每台机器是放100个partition,每个partition的最新正在写的日志段文件的大小是默认的1GB.
所以说单台机器上,最新的正在写的日志段文件的大小100个partition 1GB的日志段文件 = 600GB。如果最佳的情况,单台机器可以有600GB的内存的给os cache的话,就可以每个partiton最新的数据都在os cache里,比如说一个日志段文件有1GB,可能都对应几百万条数据了,这些数据我们也不会变频繁消费,最多也就是1%-10%数据消费,所以600(1%-10%)也就是6G以上所以我们大概64G的机器内存即可。
JVM的堆内存
kafka自身的jvm是用不了过多堆内存的,因为kafka设计就是规避掉用jvm对象来保存数据,避免频繁fullgc导致的问题,所以一般kafka自身的jvm堆内存,分配个6G左右就够了,剩下的内存全部留给os cache
4、物理机网络带宽
生产环境的机器规划,无论是MySQL、Redis、RocketMQ、Elasticsearch、Kafka、Hadoop、Flink,Yarn,其实规划的思路都是类似的,考虑的其实是从技术本质和底层的原理出发来考虑:
1、请求量有多大、
2、数据量有多大、
3、内存应该分配多大(需要了解底层工作机制,怎样使用内存的?)、
4、线程数量有多少、
5、网络数据传输量有多大(一般决定了你的网卡大小选择)
现在一般就是千兆网卡(1GB / s),还有万兆网卡(10GB / s),网卡决定了什么呢?比如你要计算一下,kafka集群之间,broker和broker之间是会做数据同步的,因为leader要同步数据到follower上去,他们是在不同的broker机器上的,broker机器之间会进行频繁的数据同步,传输大量的数据。
所以在这种情况下,需要计算下每秒两台broker机器之间大概会传输多大的数据量?高峰期每秒大概会涌入6万条数据,假设每一条数据大小是1kb,大概来说每秒就是60mb/s的数据量,每秒60mb/s的湖,他会在broker之间来传输,你就算多估算一些,每秒大概也就传输个几百mb/s的数据,就足够了。
实际上每台机器能用的网卡的带宽还达不到极限,因为kafka只能用其中一部分的带宽资源,比如700mb/s,但是一般不能允许kafka占用这么多带宽,因为避免说占用这么多带宽,万一再多一点,就容易把网卡打满
所以说,一般限制kafka每秒带宽资源就是300mb / s,如果你给物理机使用的千兆网卡,那么其实他每秒最多传输数据是几百mb,是够了,可能也就是几十mb,或者一两百mb,基本上都够了,可以足够传输。
5、物理机磁盘
磁盘类型
SSD磁盘的性能相比于机械硬盘的话主要在磁盘随即写上面,在磁盘顺序写的情况下,磁盘顺序写跟随即写差不多,所以由于Kafka是基于os cahce+磁盘顺序写机制,由于磁盘顺序写的性能跟写内存差不多,并且SSD的在磁盘随机写上会比机械硬盘效果好,在磁盘顺序写上SSD跟机械硬盘差不多,所以我们使用机械硬盘即可
磁盘大小
总共13T数据,由于很多数据是没有1kb大小的,所以我们预估是12TB数据,6台机器,则:每台机器加上2快1TB的磁盘,总共2TB。
2、Kafka集群生产参数设置
1、内核参数
内存参数的话主要是Kafka Broker应用的自身配置文件,下面是一些常规参数:
broker.id
:Kafka应用的唯一标示;这个是每个broker都必须自己设置的一个唯一id。
log.dirs
:这个极为重要
,kafka的所有数据就是写入这个目录下的磁盘文件中的,如果说机器上有多块物理硬盘,那么可以把多个目录挂载到不同的物理硬盘上,然后这里可以设置多个目录,这样kafka可以把数据写的压力分散到多块物理硬盘,多个硬盘的磁头可以并行写,这样可以提升吞吐量。
zookeeper.connect
: 因为你要知道的是,Kafka他一定要去使用zookeeper的。所以需要配置底层**这个是连接kafka底层的zookeeper集群的地址。
Listeners:这是broker监听客户端发起请求的端口号,在Kafka新版本里面,通过listeners去配置broker。通过哪个端口去监听客户端发送过来请求的。
unclean.leader.election.enable:1.0版本之前都是true:意思是允许非ISR列表的follower选举为新的leader。这个是什么意思呢?比如说你的一个follower,他可能落后于这个leader。但是呢?leader此时挂了。之前老版本也是允许非ISR列表里面的follower选举为leader。这个时候可能会丢失一些数据这样子。1.0之后。默认设置为false:意思就是只能选举ISR列表里的follower成为新的leader。unclean指代不干净的leader选举,也就是指代不在ISR列表里面的follower选举。
delete.topic.enable**:默认true,允许删除topic。
log.retention.hours
**: 设置一下日志要保留数据多少个小时,这个就是底层的磁盘文件,默认保留7天的数据。
log.retention.bytes: 这个设置如果分区的数据量超过了这个限制,就会自动清理数据,默认-1不按照这个策略来清理,这个一般不常用
min.insync.replicas
: 这个跟acks=-1配合起来使用,意思就是说必须要求ISR列表里有几个follower,然后acks=-1就是写入数据的时候,必须写入这个数量指定的follower才可以,一般是可以考虑如果一个leader两个follower,三个副本,那么这个设置为2,可以容忍一台机器宕机,保证高可用,但是写入数据的时候必须ISR里有2个副本,而且必须副本都写入才算成功。
如果你就一个leader和follower,也就是说你只有双副本,然后min.insync.replicas = 2。如果有一台机器宕机,导致follower没了,此时ISR列表里就一个leader的话,你就不能写入了,因为你设置的参数:min.insync.replicas = 2 也就是要求ISR列表里面至少有哦2个副本。但是我们将参数:min.insync.replicas = 2设置为2也是有道理的,如果你只有一个副本了,此时还可以写入数据的话,就会导致写入到leader之后,万一leader也宕机了,此时数据必然丢失。
一般来说为了避免数据占用磁盘空间过大,一般都是kafka设置双副本,一个leader和一个follower就够了,假如你设置的三副本的话,数据在集群里要乘以3倍的空间来存放,非常耗费磁盘空间,而且对你的整体集群的性能消耗也会更大。(因为leader收到写入数据时候,需要传给一个follower还是2个follower,你要传给2个follower的花,你需要占用更多的网络带宽,而且你传给两个follower的话,必然会导致你需要消费更多的cpu执行)。会加重更多数据资源的消耗,所以说:我们Kafak集群部署的时候,使用双副本就可以了。
双副本的情况下,我们就考虑下是否允许数据丢失?如果允许数据丢失的话,将参数:min.insync.replicas=1,acks=-1(一条数据必须写入ISR里所有副本才算成功),你写一条数据只要写入leader就算成功了,不需要等待同步到follower才算写成功。但是此时如果一个follower宕机了,你写一条数据到leader之后,假如leader也宕机,会导致数据的丢失。
所以说,一般来说在双副本的场景下,我们为了避免数据丢失,min.insync.replicas=2,acks=-1,每次写入必须保证ISR里2个副本都写成功才可以,如果其中一个副本没了,会导致你就没法写入,必须阻塞等待kafka恢复。这样就可以保证数据的不丢失。但是对吞吐量有一定影响。
业务场景考虑:
你是要计算为客户准备的财务数据报表,非常严格的数据必须精准的话,精准到每一分钱,你还是得设置成2;
假如做日志分析的话,你将其设置为1即可。只需要写入到一个副本之后,就认为写入成功。
num.network.threads
: 这个是负责转发请求给实际工作线程的网络请求处理线程的数量(也就是processor线程数),这个默认是3,高负载场景下可以设置大一些。
num.io.threads
: 这个是控制实际处理请求的Handler线程池里面线程数量,默认是8,高负载场景下可以设置大一些。
要自己做一些压测,在不同的线程数量的条件下,对整体的生产和消费的吞吐量分别是多少,还得看一下线程越多的时候,对CPU的复杂有多大,整体看一下,如果增加一些线程,吞吐量提升很多,而且CPU负载还能接受。
message.max.bytes
:(这个一定要设置,不然的话,写入数如果比较大的话,写入数据将会写失败)这个是broker默认能接受的消息的最大大小,默认是977kb,太小了,可以设置的大一些,一般建议设置大一些,很多大消息可能大到几mb,这个设置个10mb,可以考虑。
上面是一些比较常规的参数:,除了上面的参数外,我们还需要考虑os cache刷新数据到磁盘的参数。
log.flush.interval.messages:指代os cache中打到多少数据时候,刷新数据到磁盘。
log.flush.interval.ms: 指代每多长时间刷新数据到磁盘。
线上的常用的一个配合,高负载高吞吐的情况下,建议设置为1分钟
2、操作系统参数
我们现在需要设置操作系统参数:
文件个数估计: kafka会频繁的创建和修改文件,Kafka上文件的数量大约是:分区数量 (分区总大小 / 日志段大小) 3(.index .timeindex .log);
比如一个broker上大概有100个分区,每个分区大概是10G的数据,日志段大小是默认的1G,那么就是100 10(分区里的日志段文件数量) 3 = 3000个文件
这个说明Kafka会频繁地在机器上创建文件,所以你需要设置下一下参数: 把文件描述符开大一点。 这样的话可以允许Kafka创建大量的文件。
ulimit -n 100000: 这个可以设置很大的描述符限制,允许创建大量的文件
磁盘flush时间:默认是5秒,默认情况下,Kafka是先把数据写入到os cache;然后每隔一段时间,会将os cache里面的数据刷入到磁盘里面。 os cache刷入磁盘,可以设置为几分钟,比如1分钟才刷入磁盘,这样可以大幅度提升单机的吞吐量。
参考: https://www.cnblogs.com/zengk...。
sysctl -a | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 5
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 10
vm.dirty_writeback_centisecs = 500
vm.dirty_writeback_centisecs: 上面设置的是500,意思是每隔5秒唤醒一次刷磁盘的线程;cat /proc/sys/vm/dirty_writeback_centisecs查看这个值,默认一般是500(单位是1/100秒)。这个参数表示5s的时间pdflush就会被唤起去刷新脏数据。
vm.dirty_expire_centisecs: 上面设置的是3000 代表:os cache里的数据在3秒后会被刷入磁盘;这个可以设置大一些,设置为:1分钟~2分钟都可以,特别大规模的企业和公司,如果你可以接收机器宕机的时候,数据适当可以丢失一些,kafka里的数据可以适当丢失一些,但是为了提升集群的吞吐量的话。可以设置为1分钟~2分钟都可以,但是机器宕机的话,os cahce数据清空,然后数据丢失。
你大数据分析类的应用的话,主要是出一些数据报表,不要求数据100%精准的话,允许适当丢一些数据,此时的话,这里给他调大一些是没问题的。
3、JVM跟GC参数
Kafka除了自己内核参数外,还需要考虑下JVM参数设置:
修改bin/kafka-start-server.sh中的jvm设置
export KAFKA_HEAP_OPTS=”-Xmx6g -Xms6g -XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M -XX:MinMetaspaceFreeRatio=50 -XX:MaxMetaspaceFreeRatio=80”
-Xmx6g -Xms6g: 这个是设置堆内存大小的。这个一定要配置的。
-XX:+UseG1GC: 垃圾回收器使用G1垃圾回收器。
-XX:MetaspaceSize: 参考https://www.jianshu.com/p/b44...
3、Kafka集群搭建
1、zk集群搭建
zookeeper进行集群或者单点搭建的话,参考另一篇文章:https://segmentfault.com/a/11...
未完待续......
2、Kafka搭建
Kafka进程其实就是一个Broker;Kafka进程一旦启动之后就会往zk注册一个Broker节点
然后作为controller的那个broker就会感知到集群有一个broker进来了。然后他会把当前有哪些broker以及broker里面的元数据广播推送给集群里面的所有broker知道。
2.1 单机模式部署
如果是测试环境或需要在本地调试 Kafka 应用程序代码,则会以单 机模式部署一个 Kafka系统。
部署的步骤也非常简单:启动一个Standalone模式的Zookeeper,然 后启动一个Kafka Broker进程。
1. 下载Kafka安装包
我们以kafka_2.11-1.0.0为例.kafka_2.11-1.0.0--前面2.11指代的是scale版本,后面1.0.0使用的是Kafka自己的版本。
进入官网:http://kafka.apache.org/downl...。然后找到kafka_2.11-1.0.0,点击即可下载。
2. 解压Kafka安装包并重命名
下载了Kafka安装包后,在Linux操作系统指定位置进行解压操作。具体操作命令如下。
tar -zxvf kafka_2.11-1.0.0.tgz
然后重命名:
mv kafka_2.11-1.0.0 kafka
3. 配置Kafka全局变量
vim ~/.bash_profile
添加以下内容然后退出.
export KAFKA_HOME=/Users/xiexinming/software/kafka
export PATH=$PATH:$KAFKA_HOME/bin
接着,使用source命令使刚刚配置的环境变量立即生效:
source ~/.bash_profile
4. 配置Kafka系统
配置单机模式的Kafka系统步骤比较简单,只需要在 $KAFKA_HOME/conf/server.properties文件中做少量的配置即可。具体 操作命令如下。
broker.id=0
listeners=PLAINTEXT://:9092
log.dirs=/usr/kafka/data
zookeeper.connect=zk01:2181
zookeeper.connection.timeout.ms=6000
5、设置kafka的JVM启动参数设置
我们进入kafka-server-start.sh设置export KAFKA_HEAP_OPTS="-Xmx1G -Xms1G"
6、以配置文件形式后台启动kafka 确保zk已经启动
,然后我们启动kafka
bin/kafka-server-start.sh.sh -daemon config/server.properties
7、然后我们可以看到Kafka的一些日志
我们查看下kafkaServer.out启动情况;然后我们通过jps -l查看Kafka进程是否存在?
![上传中...]()
以下是kafka启动时候常见日志:
controller.log-----集群管理Controller的日志
kafka-request.log -----kafka请求的日志
kafkaServer.out---kafka服务启动日志
server.log
kafka-authorizer.log
kafkaServer-gc.log.0.current----gc日志
log-cleaner.log-----清理日志的日志
state-change.log
2.2 分布式模式部署
分布式情况下,我们采用3个kafka Broker实现。
在生产环境中,一般会以分布式模式来部署Kafka系统,以便组建 集群。在分布式模式中,不推荐使用Standalone模式的Zookeeper,这样具 有一定的风险。如果使用的是Standalone模式的Zookeeper,则一旦 Zookeeper出现故障则导致整个Kafka集群不可用。所以,一般在生产环 境中会以集群的形式来部署ZooKeeper。
1. 下载
和单机模式的下载步骤一致。
2. 解压
可参考单机模式的解压模式和重命名方法。
3. 配置Kafka全局变量
可参考单机模式的全局配置过程。
4. 配置Kafka系统
在分布式模式下配置 Kafka 系统和单机模式不一致。打开 $KAFKA_HOME/conf/server. properties文件,编辑相关属性. 第一台我们配置其broker.id=0;
5、配置其他Broker节点
我们将配置好的上一台kafka拷贝到其他两台机器上去,然后修改其broker.id分别是1,2
6、启动Zookeeper集群
在启动Kafka集群之前,需要先启动Zookeeper集群。
7、启动Kafka集群
Kafka系统本身没有分布式启动Kafka集群的功能,只有单个主机节 点启动Kafka进程的脚本。可以通过封装单个节点启动Kafka进程的步骤,来实现分布式启动Kafka集群。
8.验证
启动Kafka集群后,可以通过一些简单的Kafka命令来验证集群是否 正常。具体如下。
4、Kafka基本测试
1、命令方式:topic创建、消息生成、消息消费
1、创建一个topic:
zk集群模式:
bin/kafka-topics.sh --zookeeper zk01:2181,zk02:2181,zk03:2181 --create --topic test-topic --partitions 3 --replication-factor 2
单机模式:
bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic test-topic --partitions 1 --replication-factor 1
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。