一 .概述
Apache Kafka是一款开源的消息引擎系统,根据维基百科的定义,消息引擎系统是一组规范,业用这组规范在不同的系统之间传递语义准确的消息,实现松耦合的异步式数据传递。通俗来讲,就是系统A发送消息给消息引擎系统,系统B从消息引擎系统中读取A发送的消息
二.kafka 介绍
1.简介
Apache Kafka 是一款高可用,高可靠,高吞吐,低延时,可扩展,持久,可靠性容错性的分布式发布订阅消息系统
2.使用场景
- ① 日志收集:公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式 开放给各种consumer;
- ② 消息系统:解耦生产者和消费者、缓存消息以及消息的削峰填谷等
- ③ 用户活动跟踪:kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各 个服务器发布到kafka的topic中,然后消费者通过订阅这些topic来做实时的监控分析,亦可保存到数据库;
- ④ 流式处理:结合Flink, spark ,Storm做消息实时的指标计算
3.kafka中的术语解释
| Topic | 主题 | 主题是承载消息的逻辑容器,在实际使用中多用来区分具体的业务 |
|---|---|---|
| Partition | 分区 | 一个有序不变的消息序列。每个主题下可以有多个分区 |
| Record | 消息 | 这里的消息就是指 Kafka 处理的主要对象 |
| Offset | 消息位移 | 表示分区中每条消息的位置信息,是一个单调递增且不变的值 |
| Replica | 副本 | Kafka 中同一条消息能够被拷贝到多个地方以提供数据冗余,这些地方就是所谓的副本。副本还分为领导者副本和 追随者副本,各自有不同的角色划分。副本是在分区层级下的,即每个分区可配置多个副本实现高可用 (副本数不用超过broker的数量) |
| Leader | 领导者 | 每个partition有多个副本 其中有且仅有一个作为Leader Leader是当前负责数据 的读写的partition |
| Follower | 跟随者 | Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。当Follower与Leader挂掉、卡住或者同步太慢,leader会把这follower从“in sync replicas”(ISR)列表中删除,重新创建一个Follower |
| Producer | 生产者 | 向主题发布新消息的应用程序 |
| Consumer | 消费者 | 从主题订阅新消息的应用程序 |
| Broker | 中间者 | 集群包含一个或者多个服务器 服务器节点称为broker 同时存储topic的数据 |
| Controller | 控制器 | 是kafka的核心组件 他的主要作用是在zookeeper的帮助下管理和协调整个kafka集群的 主要作用:主题管理(创建,删除,增加分区),分区重分配 ,Preferred领导这选举,集群成员管理(新增broker,broker主动关闭,broker单机)元数据服务 |
| Consumer Offset | 消费者位移 | 表征消费者进度,每个消费者都有自己的消费者位移 |
| Consumer Group | 消费者组 | 多个消费者实例共同组成一个组,同时消费多个分区以实现高吞吐 |
| Reblance | 重平衡 | 重平衡 消费者组内消费实例挂掉后,其他消费者实例自动重新分配订阅主题分区的过程。Rebalance和kafka消费者 端实现高可用的重要手段 |
4 kafka对zk的依赖
- zookeeper增加运维成本
- Controller故障处理耗时,监听后选举broker,broker加载消息
- 分区瓶颈,zookeeper元数据过多
5 消息中间件的对比
| 特性 | Kafka | Pulsar | RocketMQ | RabbitMQ |
|---|---|---|---|---|
| 开发语言 | Scala | Java | Java | Erlang |
| 可用性 | 分布式 | 分布式 | 主从 | 主从 |
| 场景 | 大数据 | 金融,交易 | ||
| 模式 | 存算一体 | 存算分离 | 存算一体 | |
| 数据堆积 | 本地盘大小 | Bookeeper集群大小(无限扩展) | 本地磁盘大小 | |
| 支持协议 | kafka协议 | pulsar协议,可兼容kafka协议,MQ协议 | 自定义协议 | AMQP协议 |
| 选主 | 基于ZK从ISR中选主 | broker无状态 | 手动切换 | 假如集群的slave会成为master,slave不同步之前数据,导致数据小范围丢失 |
三. kafka的架构
1 集群的架构
Kafka集群中包含若干Producer(可以是web前端产生的Page View,或者是服务器日志,系统CPU、Memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干Consumer Group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息。
2 Topic和Partition
Topic在逻辑上可以被认为是一个queue,每条消费都必须指定它的Topic,可以简单理解为必须指明把这条消息放进哪个queue里。为了使得Kafka的吞吐率可以线性提高,物理上把Topic分成一个或多个Partition,每个Partition在物理上对应一个文件夹,该文件夹下存储这个Partition的所有消息和索引文件。创建一个topic时,同时可以指定分区数目,分区数越多,其吞吐量也越大,但是需要的资源也越多,同时也会导致更高的不可用性,kafka在接收到生产者发送的消息之后,会根据均衡策略将消息存储到不同的分区中。因为每条消息都被append到该Partition中,属于顺序写磁盘,因此效率非常高(经验证,顺序写磁盘效率比随机写内存还要高,这是Kafka高吞吐率的一个很重要的保证)
四.kafka集群的部署
1 kafka的集群部署规划
| 集群IP | 主机名 | 角色 | 配置 |
|---|---|---|---|
| 192.168.108.1 | kafka01 | Broker server | 8c/16g/500g |
| 192.168.108.2 | kafka02 | Broker server | 8c/16g/500g |
| 192.168.108.3 | kafka03 | Broker server | 8c/16g/500g |
| 192.168.108.4 | zookeeper01 | zookeeper-server | 4c/8g/50g |
| 192.168.108.5 | zookeeper02 | zookeeper-server | 4c/8g/50g |
| 192.168.108.6 | zookeeper03 | zookeeper-server | 4c/8g/50g |
备注:以上是最低资源配置,S级系统zookeeper和kafka不能混合部署,A级系统可以zk和kafka可以混合部署
2 搭建步骤
- hosts文件的配置(每台机器都需要添加):
vi /etc/hosts 末尾添加:
192.168.108.1 kafka01
192.168.108.2 kafka02
192.168.108.3 kafka03
192.168.108.4 kafka04
192.168.108.5 kafka05
192.168.108.6 kafka06
- jdk8 环境的准备 (虚拟机自带jdk8安装包):
上传jdk8安装包执行如下命令:
tar -zxvf jdk1.8.0_111.tar.gz -C /opt/
cd /opt/
mv dk1.8.0_111.tar.gz/ jdk8/
vi /etc/profile 末尾添加
export JAVA_HOME=/opt/jdk8/
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
source /etc/profile
java -version (环境变量的检查)
- zookeeper环境的搭建:
下载zookeeper安装包
wget downloads.apache.org/zookeeper/z…
1 解压安装包
tar -zxvf apache-3.7.0-bin.tar.gz -C /opt/
cd /opt/;mv apache-zookeeper-3.7.0 zookeeper
mkdir -p /opt/zookeeper/data
mkdir -p /opt/zookeeper/logs
2 修改配置文件
cp /opt/zookeeper/conf/zoo_sample.cfg /opt/zookeeper/conf/zoo.cfg
vi /opt/zookeeper/conf 添加如下配置
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper/data
dataLogDir=/data/zookeeper/log
clientPort=2181
server.4=192.168.108.4:2888:3888
server.5=192.168.108.5:2888:3888
server.6=192.168.108.6:2888:3888
maxClientCnxns=2000
echo 1 > /data/zookeeper/data/myid (192.168.108.4)
echo 2 > /data/zookeeper/data/myid(192.168.108.5)
echo 3 > /data/zookeeper/data/myid(192.168.108.6)
3 zookeeper启动:
./zkServer.sh start
- kafka环境的搭建:
下载kafka安装包 Apache Kafka
tar -zxvf kafka_2.11-2.4.1.tgz -C /opt
mv kafka_2.11-2.4.1/ kafka/
mkdir -p /opt/kafka/data(创建数据日志目录)
mkdir -p /opt/kafka/logs(创建服务日志目录)
vi /opt/kafka/config/server.properties (修改配置)
每一个Kafka的broker都有一个整数的标识。我们设置broker.id来标识它,推荐该值能对应上 broker 所在的主机名。
broker.id=1(每台broker都不一样)
#监听器 告诉外部连接者要通过什么协议访问指定主机名和端口开放的 Kafka 服务
listeners = PLAINTEXT://IP:9092(每台机器的IP)
#Kafka会保留消息到磁盘,并且这些日志片段会按目录区分存放在log.dirs配置的路径,多个配置使用英文逗号隔开
log.dirs= /opt/kafka/data
#broker进程日志保存路径
kafka.log4j.dir=/opt/kafka/logs
#接收线程会将接收到的消息放到内存中,然后再从内存中写入磁盘。
num.network.threads=4
#用来处理磁盘IO的线程数量
num.io.threads=8
#topic创建需要多少分区。默认是1。分区数量一旦设置,只能增加,不能减少
num.partitions=6
#连接zk的IP
zookeeper.connect= 192.168.108.4:2181, 192.168.108.5:2181, 192.168.108.6:2181,
#日志保留的时间,日志片段被关闭后开始计算,超过该时间,日志将被清理
log.retention.hours=72
#开启删除topic
delete.topic.enable=true
#关闭自动创建topic
auto.create.topics.enable=false
#默认副本数
default.replication.factor=3
#zookeeper连接超时
zookeeper.connection.timeout.ms=6000
#分区leader重平衡
auto.leader.rebalance.enable=false
#最大消息体
message.max.bytes=104857600
#日志segmet
log.segment.bytes=1073741824
#ISR最小副本数
min.insync.replicas=2
修改好的配置文件发送到另外2台机器上去
scp /opt/kafka/config/server.properties hostname@192.168.108.2:/opt/
登录到192.168.108.2 修改server.properties 的broker.id=2
scp /opt/kafka/config/server.properties hostname@192.168.108.3:/opt/****
****登录到192.168.108.3 修改server.properties 的broker.id=3
****分别登录到3台机器上面 启动kafka服务:
cd /opt/kafka/config/kafa
nohup ./kafka-server-start.sh -daemon /data/kafka/config/server.properties
五 集群如何选择Topics的Partitions数量
机器配置 4C/8G 500G 压测结果:
一 生产者:
| 单副本 | 1分区 | 3分区 | 6分区 | 9分区 | 12分区 | 15分区 |
|---|---|---|---|---|---|---|
| 生产M/s | 70 | 169 | 239 | 270 | 292 | 322 |
| 消费M/s | 330 | 484 | 458 | 438 | 443 | 441 |
六.kafka技术规范
1 基础环境选择
| 因素 | 考量点 | 建议 |
|---|---|---|
| 操作系统 | 操作系统I/O模型 | 将kafka部署在Linux上 |
| 磁盘 | 磁盘I/O性能 | 普通环境使用机械磁盘,不需要搭建RAID 特别环境 可以使用SSD 固态硬盘 |
| 磁盘容量 | 根据消息数,留存时间预估磁盘容量 | 实际使用中建议预留20%~30%的磁盘空间 |
| 带宽 | 根据实际带宽资源和企业SLA预估服务器数量 | 对于千兆网络,建议每台服务器按照700Mbs来计算,避免大流量下的丢包 |
2 版本号以及版本选择
① kafka版本命名:
Scala 2.11-kafka_2.11-2.2.1.tgz 前面的版本号是编译Kafka源代码的scala编译版本(2.11)后面的2.2.1是真正kafka的版本
② 版本选择
- 对于使用kafka Stram的用户 至少使用2.4.0版本
- 对于仅仅使用消息引擎的 至少是 0.11.0版本
3 kafka的重要参数
1 Kafka-server 的配置参数
| Broker端 | log.dirs | 指定了 Broker 需要使用的若干个文件目录路径。 |
|---|---|---|
| Broker端 | log.dir | 说明它只能表示单个路径,它是补充上一个参数用的。 |
| zookeeper端 | zookeeper.connect | 1 我可以指定它的值为zk1:2181,zk2:2181,zk3:2181。2181 是 ZooKeeper 的默认端口。2 两套kakfk集群共用一套zookeeper集群可以参考如下配置:zk1:2181,zk2:2181,zk3:2181/kafka1和zk1:2181,zk2:2181,zk3:2181/kafka2。 |
| broker端 | listeners | 学名叫监听器,其实就是告诉外部连接者要通过什么协议访问指定主机名和端口开放的 Kafka 服务 |
| num.replica.fetchers | 用来从主partion同步数据的线程数,默认为1,建议适当调大,数据量大的时候一个同步线程可能不够用 建议是3 | |
| advertised.listeners | 和 listeners 相比多了个 advertised。Advertised 的含义表示宣称的、公布的,就是说这组监听器是 Broker 用于对外发布的 | |
| broker端 | num.network.threads | 一般num.network.threads主要处理网络io,读写缓冲区数据,基本没有io等待,配置线程数量为cpu核数加1 |
| num.io.threads | 主要进行磁盘io操作,高峰期可能有些io等待,因此配置需要大些。配置线程数量为cpu核数2倍,最大不超过3倍. | |
| default.replication.factor | 消息日志备份因子,默认是1 生产上建议设置成3 | |
| auto.create.topics.enable | 自动创建topic(生产建议关闭 默认是开启) | |
| unclean.leader.election.enable | 指示是否启用不在ISR集中的副本作为最后选择 | |
| auto.leader.rebalance.enable | 是否允许定期进行 Leader 选举(默认是开启的 生产不建议开启 性能损耗大) | |
| broker端 | log.retention.hours | 都是控制一条消息数据被保存多长时间,默认是7天 |
| broker端 | log.retention.bytes | 这是指定 Broker 为消息保存的总磁盘容量大小(这个值默认是 -1 表明你想在这台 Broker 上保存多少数据都可以) |
| broker端 | message.max.bytes | 控制 Broker 能够接收的最大消息大小(默认值 不到1M) |
2 kafka JVM的参数
使用 Java 8,手动设置使用 G1 收集器。在没有任何调优的情况下,G1 表现得要比 CMS 出色,主要体现在更少的 Full GC,需要调整的参数更少等,所以使用 G1 就好了
KAFKA_HEAP_OPTS:指定堆大小。
KAFKA_JVM_PERFORMANCE_OPTS:指定 GC 参数。
即在启动 Kafka Broker 之前,先设置上这两个环境变量:
$> export KAFKA_HEAP_OPTS=--Xms6g --Xmx6g
$> export KAFKA_JVM_PERFORMANCE_OPTS= -server -XX:+UseG1GC - XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:+ExplicitGCInvokesConcurrent -Djava.awt.headless=true
$> bin/kafka-server-start.sh config/server.properties
3 操作系统参数
- 文件描述符限制 : ulimit -n 100000 通常情况下将它设置成一个超大的值是合理的做法
- 文件系统类型 : 根据官网的测试报告,XFS 的性能要强于 ext4,所以生产环境最好还是使用 XFS
- Swappiness: 可以设置成一个较小的值。为什么呢?因为一旦设置成 0,当物理内存耗尽时,操作系统会触发 OOM killer 这个组件,它会随机挑选一个进程然后 kill 掉,即根本不给用户任何的预警。但如果设置成一个比较小的值,当开始使用 swap 空间时,你至少能够观测到 Broker 性能开始出现急剧下降,从而给你进一步调优和诊断问题的时间。基于这个考虑,建议将 swappniess 配置成一个接近 0 但不为 0 的值,比如 1。
- 提交时间: 最后是提交时间或者说是 Flush 落盘时间。向 Kafka 发送数据并不是真要等数据被写入磁盘才会认为成功,而是只要数据被写入到操作系统的页缓存(Page Cache)上就可以了,随后操作系统根据 LRU 算法会定期将页缓存上的“脏”数据落盘到物理磁盘上。这个定期就是由提交时间来确定的,默认是 5 秒。一般情况下我们会认为这个时间太频繁了,可以适当地增加提交间隔来降低物理磁盘的写操作。当然可能会有这样的疑问:如果在页缓存中的数据在写入到磁盘前机器宕机了,那岂不是数据就丢失了。的确,这种情况数据确实就丢失了,但鉴于 Kafka 在软件层面已经提供了多副本的冗余机制,因此这里稍微拉大提交间隔去换取性能还是一个合理的做法。
4 kafka参数调优TPS
| broker端 | 适当增加num.replica.fetchers参数的值 但不要超过CPU核数 |
|---|---|
| 调优GC参数以避免经常性的Flull GC | |
| Producer端 | 适当增加batch.size参数的值,比如从默认的16KB增加到512KB或1MB |
| 适当增加liners.ms参数值,比如10~100 | |
| 设置acks=0或1 | |
| 设置retreies=0 | |
| 如果多线程共享一个Producer实例,就增加buffer.memory参数值 | |
| Consumer端 | 采用多Consumer进程同时消费数据 |
| 增加fetch.min.bytes参数值,比如设置成1KB或者更大 |
5 kafka分区的选择
- 多分区可以提高吞吐量
- 需要更多的Open File Handles
每个分区会映射到broker机器的其中一个目录,在每个日志目录每个日志段(segment)包含四个文件:
在kafka中,broker会为每个segment打开一个file handle ,不过这仅是一个配置问 题,通常在生产集群可以配置OS层面的打开文件数为30 thousand
- 多分区可能增加整体不可用性
- 可能增加端到端的延迟
- 更多的分区可能在客户端需要更多的内存
通常情况下,越多的partition会带来越高的吞吐量,但是同时也会给broker节点带来相应的性能损耗和潜在风险,虽然这些影响很小,但不可忽略,因此需要根据自身broker节点的实际情况来设置partition的数量以及replica的数量。
七.kafka常用的命令
1 生产消息:$ bin/kafka-console-producer.sh --broker-list kafka-host:port --topic test-topic --request-required-acks -1 --producer-property compression.type=lz4
2 消费消息:$ bin/kafka-console-consumer.sh --bootstrap-server kafka-host:port --topic test-topic --group test-group --from-beginning --consumer-property enable.auto.commit=false
3 测试生产者性能:$ bin/kafka-producer-perf-test.sh --topic test-topic --num-records 10000000 --throughput -1 --record-size 1024 --producer-props bootstrap.servers=kafka-host:port acks=-1 linger.ms=2000 compression.type=lz4
4 查看主题消息总数:$ bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list kafka-host:port --time -1 --topic test-topic
5 查看消息文件数据:$ bin/kafka-dump-log.sh --files ../data_dir/kafka_1/test-topic-1/00000000000000000000.log
6 深入看一下每条具体的消息:$ bin/kafka-dump-log.sh --files ../data_dir/kafka_1/test-topic-1/00000000000000000000.log --deep-iteration
7 查询消费者组位移:$bin/kafka-consumer-group.sh --bootstrap-server localhost:9092 --describle --group test-group
8 查看kafka的topic 列表:bin/kafka-topics.sh --zookeeper 172.20.170.96:2181 --list
9 查看topic的详细信息: bin/kafka-topics.sh --zookeeper 127.0.0.1:2181 --topic lx_test_topic --describe
10 查看consumer group的列表:bin/kafka-consumer-groups.sh --new-consumer --bootstrap-server 127.0.0.1:9292 --list
lx_test
11 查看特定 consumer group 详情: bin/kafka-consumer-groups.sh --new-consumer --bootstrap-server 127.0.0.1:9292 --group lx_test --describe
12 kafka的压测脚本生产者和消费者:
./kafka-producer-perf-test.sh --topic app-test --num-records 100000000 --record-size 512 --throughput 100 --producer-props bootstrap.servers=172.25.88.145:9092
./kafka-consumer-perf-test.sh --topic zms-test --broker-list 172.25.88.145:9092 --messages 100000000 --threads 4 --print-metrics --show-detailed-stats
八 kafka的监控
- 主机监控指标:
- 磁盘 I/O 使用率,包括读使用率和写使用率
- TCP 连接数
- 打开文件数
- JVM监控指标:
- Full GC 发生频率和时长。这个指标帮助你评估 Full GC 对 Broker 进程的影响。长时间的停顿会令 Broker 端抛出各种超时异常。
- 活跃对象大小。这个指标是你设定堆大小的重要依据,同时它还能帮助你细粒度地调优 JVM 各个代的堆大小。
- 应用线程总数。这个指标帮助你了解 Broker 进程对 CPU 的使用情况。
- 集群的监控指标:
- 查看 Broker 进程是否启动,端口是否建立
- 查看 Broker 端关键日志:主要涉及 Broker 端服务器日志 server.log,控制器日志 controller.log 以及主题分区状态变更日志 state-change.log。其中,server.log 是最重要的,
- 查看 Broker 端关键线程的运行状态:Log Compaction 线程,副本拉取消息的线程
- 查看 Broker 端的关键 JMX 指标:BytesIn/BytesOut,NetworkProcessorAvgIdlePercent,ActiveControllerCount
- 监控 Kafka 客户端:客户端所在的机器与 Kafka Broker 机器之间的网络往返时延(Round-Trip Time,RTT)