一、集群环境规划
1、操作系统
Kafka为JVM的框架,Java为跨平台语言,理论上Kafka可以部署到任意支持Java的操作系统上。但是部署到不同系统还是有区别的。Linux比Windows等其他操作系统更适合部署Kafka。主要原因有I/O模型的使用和网络传输的效率。
Kafka新版本clients的设计底层使用了Java的Selector机制,而后者在Linux上的实现机制就是epoll模型。但在Windows上Selector实现的机制为select模型而非IOCP模型,只有在Java NIO2才是使用IOCP模型实现的。因此部署在Linux上比Windows上能得到更高效的I/O处理性能。
Kafka需要大量的通过网络与磁盘进行数据交互,这些操作都是通过Java的FileChannel.transferTo方法实现的,在Linux底层使用sendfile系统调用,即零拷贝Zero Copy技术。
综上,推荐生产环境使用Linux操作系统。
2、磁盘
Kafka大量使用磁盘。每条消息都必须被持久化存储在磁盘中,只有一定数据的broker成功接收后才统计clients消息发送成功,因此消息越快被保存到磁盘上clients的请求延时越低。
1)、机械硬盘HDD还是固态硬盘SSD
机械硬盘成本低容量大,固态硬盘性能好成本大。需要根据公司自身需求选择对应类型的硬盘。但是Kafka使用磁盘的方式,一定程度上缩小了两种硬盘的差距,Kafka是顺序写磁盘的,磁盘顺序I/O的性能,差距不大。追求性价比的公司可以使用机械硬盘,当然使用SSD性能更好。
2)、普通磁盘JBOD还是磁盘整列RAID
RAID作为Kafka底层存储有两个优势:提供冗余的数据存储空间和自带的负载均衡。不过Kafka自身已经提供这两种特性,我们可以通过副本机制提供冗余和高可靠性,通过分散到各个节点的领导选举机制实现负载均衡。建议选择JBOD。另外尽量不要使用网络存储NAS。
3)、磁盘容量规划
Kafka需要多大磁盘容量,需要根据自身业务场景和存储需要来计算。磁盘容量与新增消息数、消息存留时间、平均消息大小、副本数、是否启用压缩有关。
假如每天会产生1亿条数据,每条消息保存两份并保留1周的时间,平均一条消息的大小是1KB。那么1亿21KB/1000/1000=200GB 磁盘空间,还需预留10%磁盘空间存储其他文件,则为210GB,还要保存一周210GB*7=1.5TB。这是在五压缩的情况下,如果clients启用了消息压缩,可以预估一个压缩比,相乘即得到磁盘容量。
3、内存
Kafka虽然打了依靠文件系统和磁盘来保存消息,但还是会对消息进行缓存,缓存在操作系统的页缓存page cache。
Kafka持久化消息的时候,仅将消息写入page cache中,之后写到磁盘的操作是由系统完成的。consumer读取消息时也会先从page cache中读取,如果命中了则不需要物理I/O操作,提高consumer的整体性能。所以我们尽量分配给多的内存给操作系统的page cache。
Kafka对Java堆内存使用不是很多,Kafka中的消息会很快的进行垃圾回收GC,不要为broker设置过大的堆内存,最好不超过6GB。
另外需要把page cache的大小与实际环境的日志段大小做比较。page cache大小至少要大于一个日志段的大小。
4、cpu
Kafka不属于计算密集型系统,Kafka的CPU追求多核而非搞时钟频率。
使用多核系统,CPU核数最好大于8。
5、带宽
Kafka在网络间传输大量数据的分布式数据管道,带宽资源很容易造成瓶颈。快速且稳定的网络环境是搭建Kafka集群的前提。当前主流的网络环境千兆位网络和万兆位网络都满足Kafka集群的使用。
根据带宽和磁盘空间,可以计算出整个集群需要的broker数。
如在1GB/s的带宽中,业务目标是在1小时内处理1TB数据,这时需要多少个broker呢?1GB/s的带宽中,假如分配到Kafka的为70%带宽,则为710M/s,还需要预留2/3缓冲,即240M/s,如果要1小时内处理1TB数据,即每秒处理2336M数据,那么需要2336/240约10个broker。
尽量使用高速网络;根据自身带宽评估broker的数量;避免使用跨机房网络。
6、建议配置
操作系统:Linux
CPU:24核
内存:32GB
磁盘:1TB 7200转SAS盘两块
带宽:1GB/s
二、参数设置
1、系统参数
部署在Linux下,需要调整以下参数:
1)、文件描述符限制
ulimit -n 100000
2)、设置系统socket缓冲区大小,最好设置为128KB
3)、最好使用Ext4或者XFS文件系统,推荐XFS类型文件系统
查看文件系统类型
df -T
4)、关闭swap,将vm.swappiness设置为个较小的值。
查看swap值
cat /proc/sys/vm/swappiness
临时设置,重启后失效
sysctl vm.swappiness=10
永久设置
echo "vm.swappiness=9" >> /etc/sysctl.conf
/sbin/sysctl -p
5)、设置更长的flush时间。
这里初始值为3000,设置为原来的5倍。
echo "vm.dirty_expire_centisecs=15000" >> /etc/sysctl.conf
/sbin/sysctl -p
2、JVM参数
通常broker的内存设置不大于6G,Java8推荐使用G1垃圾收集器。
-Xmx6g -Xms6g -XX:MetaspaceSize=96m -XX:+UseG1GC -XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35 -XX:G1HeapRegionSize=16M -XX:MinMetaspanceFreeRatio=50 -XX:MaxMetaspanceFeeRatio=80
3、Broker参数
详细参数参考官网:http://kafka.apache.org/documentation/#brokerconfigs
broker.id:Kafka服务broker的ID,全局唯一整数,使用该参数表示broker。默认值为-1。如果不指定会自动生成一个唯一值。
log.dirs:Kafka持久化消息的目录。最好设置在磁盘空间足够的位置。如果使用多块磁盘,建议设置多个目录(用逗号分隔),这样可以把负载均匀的分布到各个目录,N个磁头可以同时工作,提高吞吐量。如果不设置默认保存到/tep/kafka-logs目录下。
zookeeper.connect:Kafka使用的Zookeeper集群地址,可以使用多个值(用逗号分隔)。如果一个Zookeeper管理多个Kafka,需要在后面添加目录,比如zk1:2181,zk2:2181,zk3:2181/kafka_cluster1 中/kafka_cluster1为添加的路径,如果不设置kafka的元数据信息默认保存在Zookeeper的根目录。
listeners:broker监听器位置,格式为[协议]😕/[主机名]:[端口]。可以配置多个值(用逗号分隔)。Kafka支持的协议类型包括PLAINTEXT、SSL、SASL_SSL等,使用PLAINTEXT就足够了,如果启用了安全协议使用SSL、SASL_SSL。
advertised.listeners:注册到Zookeeper中的主机名和端口,格式为[协议]😕/[主机名]:[端口],外网访问时需要注释掉listeners参数,启用该参数。
4、Topic参数
详细参数参考官网:http://kafka.apache.org/documentation/#topicconfigs
二、集群管理
2.1、broker管理
2.1.1、启动broker
默认启动,默认使用config/server.properties配置文件。
bin/kafka-server-start.sh
使用上面启动方式当会话关闭时会停止kafka服务,我们可以使用daemon参数或nohup方式后台启动。
-deamon参数启动:
bin/kafka-server-start.sh -daemon config/server.properties
nohup启动
nohup bin/kafka-server-start.sh config/server.properties &
启动后最好看一下日志是否报错,日志在logs目录下,文件名为server.log。如果发现started输出,则broker启动成功。
2.1.2、停止broker
如果为前台启动,即没加-daemon参数和nohup启动,在终端Ctrl+C,可以发出终止信号,关闭broker。
如果为后台启动,推荐使用kafka停止脚本停止broker。
bin/kafka-server-stop.sh
上面的停止脚本会停止该服务器上所有kafka进程。
如果使用kafka-server-stop.sh不能关闭broker。建议获取到kafka的pid后使用以下命令停止,不建议使用kill -9命令停止broker。
kill -s term PID
2.1.3、添加broker
只需要为新增的broker的配置文件中broker.id设置一个唯一值,然后启动即可。Kafka集群能自动发现启动的broker并同步元数据信息。
新添加的broker不会自动分配已有的topic分区,需要用户手动重新分配分区。后面新建的topic会使用新建的broker。
2.1.4、设置JMX端口
Kafka提供了JMX指标用户集群的监控,如果要使用这些指标需要指定JMX端口。
export JMX_PORT=9995 bin/kafka-server-start.sh -daemon config/server.properties
2.1.5、升级broker
以下示例将Kafka从0.10.0.0版本升级到0.10.2.0版本。
1、更新broker的通信版本和消息版本,需要更新所有broker的配置文件,版本为当前Kafka的版本。
inter.broker.protocol.version=0.10.0
log.message.format.version=0.10.0
2、更新代码,依次重启broker,将0.10.2.0版本的Kafka二进制文件覆盖原目录,依次重启broker。
3、再次更新broker的通信版本和消息版本,此次版本为升级后的Kafka版本。
inter.broker.protocol.version=0.10.2
log.message.format.version=0.10.2
4、依次重启broker。
2.2、topic管理
2.2.1、创建topic
1、kafka-topic.sh创建
创建3个分区两个副本的topic。
bin/kafka-topics.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --create --partitions 3 --replication-factor 2 --topic test-topic
相关参数:
–partitions:分区数
–replication-factor:副本数
–if-not-exists:是否存在topic,如果存在同名不会报错
–replica-assigment:手动指定分区分配,例如创建分区数为4,副本数为2的topic。0:1,1:2,0:2,1:2。
2、java代码创建
topic Bean类
package org.example.kafka.topic;
public class KafkaTopic {
private String topicName; // topic 名称
private Integer partition; // partition 分区数量
private Integer replication; // replication 副本数量
private String descrbe;
public String getTopicName() {
return topicName;
}
public void setTopicName(String topicName) {
this.topicName = topicName;
}
public Integer getPartition() {
return partition;
}
public void setPartition(Integer partition) {
this.partition = partition;
}
public Integer getReplication() {
return replication;
}
public void setReplication(Integer replication) {
this.replication = replication;
}
public String getDescrbe() {
return descrbe;
}
public void setDescrbe(String descrbe) {
this.descrbe = descrbe;
}
@Override
public String toString() {
return "KafkaTopic [topicName=" + topicName + ", partition=" + partition
+ ", replication=" + replication + ", descrbe=" + descrbe +"]";
}
}
创建topic工具类
package org.example.kafka.topic;
import kafka.admin.AdminUtils;
import kafka.admin.RackAwareMode;
import kafka.utils.ZkUtils;
import org.apache.kafka.common.security.JaasUtils;
import java.util.Properties;
public class KafkaTopicUtils {
public static void createKafaTopic(String ZkStr,KafkaTopic topic) {
ZkUtils zkUtils = null;
try {
zkUtils = ZkUtils.
apply(ZkStr, 30000, 30000, JaasUtils.isZkSecurityEnabled());
// 查看是否存在该Topic
if (!AdminUtils.topicExists(zkUtils, topic.getTopicName())) {
AdminUtils.createTopic(zkUtils, topic.getTopicName(), topic.getPartition(),
topic.getReplication(), new Properties(), new RackAwareMode.Enforced$());
}
}catch (Exception ex){
ex.printStackTrace();
}finally {
if(zkUtils!=null)
zkUtils.close();
}
}
public static void deleteKafaTopic(String ZkStr,KafkaTopic topic) {
ZkUtils zkUtils = null;
try {
zkUtils = ZkUtils.
apply(ZkStr, 30000, 30000, JaasUtils.isZkSecurityEnabled());
// 查看是否存在该Topic
if (!AdminUtils.topicExists(zkUtils, topic.getTopicName())){
AdminUtils.deleteTopic(zkUtils, topic.getTopicName());
}
}catch (Exception ex){
ex.printStackTrace();
}finally {
if(zkUtils!=null)
zkUtils.close();
}
}
}
启动类
package org.example.kafka.topic;
public class KafkaTopicTest {
public static void main(String[] args) throws Exception{
//zookeeper地址:端口号
String ZkStr = "zk1:2181,zk2:2182,zk3:2183";
//topic对象
KafkaTopic topic = new KafkaTopic();
topic.setTopicName("testTopic"); //topic名称
topic.setPartition(3); //分区数量设置为3
topic.setReplication(2); //副本数量设置为2
//创建topic
KafkaTopicUtils.createKafaTopic(ZkStr,topic);
System.out.println("create kafka topic success");
//暂停5分钟后删除,查看topic是否创建成功
Thread.sleep(1000*60*5);
//删除topic
KafkaTopicUtils.deleteKafaTopic(ZkStr,topic);
System.out.println("delete kafka topic success");
}
}
启动后,查看topic list
bin/kafka-topics.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --list
3、修改Zookeeper节点创建(不推荐使用)
在Zookeeper的/brokers/topics下写入topic名称命名的子节点。
2.2.2、删除topic
1、kafka-topic.sh删除
bin/kafka-topics.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --delete --topic test-topic
2、java代码删除
参见2.2.1
3、修改Zookeeper节点删除(不推荐使用)
在Zookeeper的/admin/delete_topics下写入子节点。
2.2.3、查看topic
1、查看topic列表
bin/kafka-topics.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --list
2、查看topic详情
bin/kafka-topics.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --describe --topic test
2.2.4、修改topic
1、修改topic
修改topic的分区数为4,topic新增分区当topic消息有key时,会重新计算分区,分区修改只能增加不能减少。
bin/kafka-topics.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --alter --partitions 4 --topic test
2、topic动态配置
使用kafka-configs.sh可以动态配置参数。
1)、添加topic配置
bin/kafka-configs.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --alter --entity-type topics --entity-name test --add-config preallocate = true,segment.bytes
2)、查看topic配置
bin/kafka-configs.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --describe --topic test
3)、删除topic配置
bin/kafka-configs.sh --zookeeper zk1:2181,zk2:2182,zk3:2183 --alter --entity-type topics --entity-name test --delete-config preallocate
2.3、consumer管理
2.3.1、创建消费者组
bin/kafka-console-consumer.sh --bootstrap-server=kafka1:9092 --topic test --from-beginning --consumer-property group.id=test-group1
2.3.2、查看消费者组
1、查看列表
bin/kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --list
2、查看详情
bin/kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --describe --group test-group1
2.3.3、重新设置消费者组位移
bin/kafka-consumer-groups.sh --bootstrap-server kafka1:9092 --group test-group1 --reset-offsets --all-topics --to-earliest --execute
设置消费组
–group test-group1:设置消费组名称
设置topic
–all-topics:消费组下所有topic设置
–topic test1,–topic test2:消费组下test1和test2 topic设置
–topic test1:0,1,2:指定topic名称下的分区设置
设置位移策略
–to-earliest:调整到分区当前最早位移处
–to-latest:调整到分区当前最新位移处
–to-current:调整到分区当前位移处
–to-offset 5000:调整到分区指定位移处
–shit-by N:调整到分区当前位移+N位移处
–to-datetime:调整到大于指定时间的最早位移处
–by-duration:调整到距离当前时间指定间隔的位移处
2.3.3、删除消费者组
bin/kafka-consumer-groups.sh --zookeeper zk1:2181 --delete--group test-group1