1 核心概念
1.1 消息和键
信息基本传递单元-消息。如果需要将消息存储于指定分区,可以为消息设置键。 例如可以为键设置哈希值,并对分区其进行取模,这样相同键的消息就能存储到指定的分区中。
1.2 批次
批次包含了一组属于同一个主题和分区的消息。
1.3 模式
可以为消息统一格式(JSON或者XML)。或者使用Apache Avro
1.4 主题和分区
通过主题发送消息。一个主题可以拥有多个分区,分区可以布在不同节点中。Kafka可以保证不同分区的消息的有序性。分区可以在不同节点中创建副本,保证可靠性。
1.5 生产者和消费者
默认情况下,生产者将消息均衡地发布到主题的所有分区中。也可以通过消息键和分区器将消息写入指定分区(分区器为消息键生成哈希值)。 消费者生产者生产消息的顺序消费消息。生产者生产消息时,会为消息生成偏移量,偏移量具有唯一性,消费者会记录下个可能得偏移量,这样消费者重启后就可以恢复作业一个分区中的数据只会被消费者群组中的一个消费者消费,统一消费者群组中的消费者对分区具有唯一所有权
1.6 Broker及集群
单体Kafka被称为Broker。Broker为生产者和消费者提供服务。 Broker可以处理千个分区和百万级/秒的消息量。 多个Broker组成集群。集群中包含一个Leader和多个Follower。生产者生产消息只能连接Leader,但消费者可以从Leader或者Follower中读取消息。
1.7 多集群
Kafka默认的消息复制机制只能在单集群中。可以通过MirrorMaker,将消息复制到其他集群中。
2 安装
2.1 Zookeeper
集群中建议最多7个,如果访问实在过多,建议考虑增加额外的观察者节点来分摊只读流量
单机配置
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/mydata/zookeeper
# the port at which the clients will connect
clientPort=2181
# 四字命令
4lw.commands.whitelist=*
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
集群配置
配置文件
tickTime=2000
dataDir=/var/lib/zookeeper
clientPort=2181
initLimit=20
syncLimit=5
server.1=zoo1.example.com:2888:3888
server.2=zoo2.example.com:2888:3888
server.3=zoo3.example.com:2888:3888
-
initLimit:从节点和主节点连接时长(* tickTime)
-
syncLimit:从节点和主节点不同步时长(* tickTime)
-
server.id=hostname:peerPort:leaderPort
- id:主机ID
- peerPort:节点通信端口
- leaderPort:首领选举端口
2.2 Kafka
单机配置
server.properties
配置log.dirs目录
myid文件
在dataDir目录下配置myid文件,其中存放主机ID
测试
# 发布主题
./kafka-topics.sh --bootstrap-server localhost:9092 --create --replication-factor 1 --partitions 1 --topic test
# 查询主题
./kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic test
# 生产消息
./kafka-console-producer.sh --bootstrap-server localhost:9092 --topic test
Test Message 1
Test Message 2
^C
# 读取消息
./kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic test --from-beginning
Test Message 1
Test Message 2
^C
Processed a total of 2 messages
常规配置
broker.id
配置成和域名相关的整数最好
listerners
# 多个以逗号分隔
listeners = 监听器名称://host_name:port
# 为监听器指定安全协议
# 如果主机名是0.0.0.0,那么将绑定所有的网络接口地址。如果主机名为空,那么将绑定默认的网络接口地址。需要注意的是,如果指定的端口号小于1024,则必须用root权限启动Kafka
listener.security.protocol.map=监听器名称:PLAINTEXT,SSL://:9091
zookeeper.connect
保存broker元数据的zookeeper地址。
zookeeper.connect=localhost:2181/path
path:作为kafka集群的chroot最好指定,不指定用默认根路径。使用chroot路径是一种最佳实践,因为这样可以在不发生冲突的情况下将ZooKeeper群组共享给其他应用程序(包括其他Kafka集群)。
log.dirs
log.dirs是一组用逗号分隔的本地文件系统路径。如果指定了多条路径,那么broker会根据“最少使用”原则,把同一个分区的日志片段保存到同一条路径下。需要注意的是,broker会向分区数量最少的目录新增分区,而不是向可用磁盘空间最小的目录新增分区,所以并不能保证数据会被均匀地分布在多个目录中。
num.recovery.threads.per.data.dir
配置处理日志目录的线程数量,用于处理日志片段的线程,以便快速恢复。默认每个日志目录使用一个线程。
线程总数=log.dirs目录数量 * 配置数
auto.create.topics.enable
设为flase关闭自动创建主题。自动创建主题的情形:
- 当一个生产者开始向主题写入消息时。
- 当一个消费者开始从主题读取消息时。
- 当客户端向主题发送获取元数据的请求时。
auto.leader.rebalance.enable
设置true。使得主题的所有权在集群中保持均衡。
leader.imbalance.check.interval.seconds
定期检查分区分布情况时间
leader.imbalance.per.broker.percentage
如果不均衡超出了设置的百分比,则会启动一次分区首领再均衡
delete.topic.enable
设为false,防止主题被删除。
主题常规配置
num.partitions
设置新创建的主题将包含多少分区。通常设为n * broker集群数量。
影响分区数量的选择的因素
- 主题吞吐量
- 单个分区读取数据的最大吞吐量
- 生产者写入单个分区的吞吐量
- 如果消息是按照不同的键写入分区,那么就很难在未来为已有的主题新增分区,所以要根据未来的预期使用量而不是当前的使用量来估算吞吐量
- 每个broker包含的分区数、可用的磁盘空间和网络带宽
- 避免使用太多分区,因为每个分区都会占用broker的内存和其他资源,还会增加元数据更新和首领选举的时间
如果要向主题写入和从主题读取1 GBps的数据,并且每个消费者可以处理50 MBps的数据,那么至少需要20个分区。这样就可以让20个消费者同时读取这些分区,从而达到1 GBps的吞吐量。
如果你无法获得这些信息,那么根据经验,将分区每天保留的数据限制在6 GB以内可以获得比较理想的效果。先从小容量开始,再根据需要进行扩展,这比一开始就使用大容量要容易得多。
default.replication.factor
如果启用了自动创建主题功能,那么这个参数的值就是新创建主题的复制系数。
建议将复制系数设置为至少比min.insync.replicas大1的数。为了提升故障对抗能力,如果你有足够大的集群和足够多的硬件资源,则可以将复制系数设置为比min.insync.replicas大2的数(简写为RF++)对于典型的集群,这意味着每个分区至少要有3个副本。如果在滚动部署或升级Kafka或底层操作系统期间出现网络交换机中断、磁盘故障或其他计划外的问题,你可以保证仍然有1个副本可用。
日志消息有效期设置
log.retention.ms
log.retention.minutes、log.retention.hours、log.retention.ms。建议使用log.retention.ms。
数据保留时长。使用log.retention.hours参数来配置时间,默认为168小时。
log.retention.bytes
主题保留大小。针对的是分区的设置,而非主题。
如果配置了这个参数,那么当主题增加了新分区,整个主题可以保留的数据也会随之增加。如果这个值被设置为–1,那么分区就可以无限期地保留数据。
log.retention.ms、log.retention.bytes同时配置的话,只要一个满足,就会删除数据。
log.segment.bytes
默认1GB
当消息达到broker,消息会被追加到分区当前的日志片段中。日志片段大小达到设置值,该日志片段就会关闭,进入过期倒计时。
如果一个主题每天只接收100 MB的消息,并且log.segment.bytes使用了默认设置,那么填满一个日志片段将需要10天。因为在日志片段被关闭之前消息是不会过期的,所以如果log.retention.ms被设为604 800 000(也就是1周),那么日志片段最多需要17天才会过期。这是因为关闭日志片段需要10天,而根据配置的过期时间,还需要再保留数据7天
log.roll.ms
设置日志片段关闭时间的参数。
log.segment.bytes和log.roll.ms并不互斥。日志片段会在大小或时间达到上限时被关闭,就看哪个条件先得到满足。在默认情况下,log.roll.ms没有设定值,所以使用log.roll.hours设定的默认值——168小时,也就是7天。
min.insync.replicas
为了提升集群的数据持久性,可以将min.insync.replicas设置为2,确保至少有两个副本跟生产者保持“同步”。生产者需要配合将ack设置为all,这样就可以确保至少有两个副本(首领和另一个副本)确认写入成功,从而防止在以下情况下丢失数据:首领确认写入,然后发生停机,所有权被转移到一个副本,但这个副本没有写入成功。如果没有这些配置,则生产者会认为已经写入成功,但实际上消息丢失了。不过,这样做是有副作用的,因为需要额外的开销,所以效率会有所降低。因此,对于能够容忍偶尔消息丢失的高吞吐量集群,不建议修改这个参数的默认值。第7章会讨论这方面的更多内容。
message.max.bytes
broker通过设置message.max.bytes参数来限制单条消息的大小,默认值是1 000 000,也就是1 MB。如果生产者尝试发送超过这个大小的消息,那么不仅消息不会被broker接收,还会收到broker返回的错误信息。与其他broker配置参数一样,这个参数指的是压缩后的消息大小,也就是说,消息的实际大小可以远大于message.max.bytes,只要压缩后小于这个值即可。这个参数对性能有显著的影响。值越大,负责处理网络连接和请求的线程用在处理请求上的时间就越长。它还会增加磁盘写入块的大小,从而影响I/O吞吐量。
消费者客户端设置的fetch.max.bytes需要与服务器端设置的消息大小保持一致。如果这个参数的值比message.max.bytes小,那么消费者就无法读取比较大的消息,进而造成阻塞,无法继续处理消息。在配置broker的replica.fetch.max.bytes参数时,也遵循同样的原则。