Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
sh: get /kafka/consumers/consumer-group/offsets/my-topic/0
① Broker配置
②Consumer主要配置
③Producer主要配置
下表列举了部分重要的配置参数,更多配置请参考官网文档
参数 | 默认值 | 描述 |
---|---|---|
broker.id | -1 | 每一个boker都有一个唯一的id作为它们的名字。当该服务器的IP地址发生改变时,broker.id没有变化,则不会影响consumers的消息情况 |
port | 9092 | broker server服务端口 |
host.name | “” | broker的主机地址,若是设置了,那么会绑定到这个地址上,若是没有,会绑定到所有的接口上,并将其中之一发送到ZK |
log.dirs | /tmp/kafka-logs | kafka数据的存放地址,多个地址的话用逗号分割,多个目录分布在不同磁盘上可以提高读写性能 /data/kafka-logs-1,/data/kafka-logs-2 |
message.max.bytes | 1000012 | 表示消息体的最大大小,单位是字节 |
num.network.threads | 3 | broker处理消息的最大线程数,一般情况下数量为cpu核数 |
num.io.threads | 8 | 处理IO的线程数 |
log.flush.interval.messages | Long.MaxValue | 在数据被写入到硬盘和消费者可用前最大累积的消息的数量 |
log.flush.interval.ms | Long.MaxValue | 在数据被写入到硬盘前的最大时间 |
log.flush.scheduler.interval.ms | Long.MaxValue | 检查数据是否要写入到硬盘的时间间隔。 |
log.retention.hours | 168 (24*7) | 控制一个log保留多长个小时 |
log.retention.bytes | -1 | 控制log文件最大尺寸 |
log.cleaner.enable | false | 是否log cleaning |
log.cleanup.policy | delete | delete还是compat. |
log.segment.bytes | 1073741824 | 单一的log segment文件大小 |
log.roll.hours | 168 | 开始一个新的log文件片段的最大时间 |
background.threads | 10 | 后台线程序 |
num.partitions | 1 | 默认分区数 |
socket.send.buffer.bytes | 102400 | socket SO_SNDBUFF参数 |
socket.receive.buffer.bytes | 102400 | socket SO_RCVBUFF参数 |
zookeeper.connect | null | 指定zookeeper连接字符串, 格式如hostname:port/chroot。chroot是一个namespace |
zookeeper.connection.timeout.ms | 6000 | 指定客户端连接zookeeper的最大超时时间 |
zookeeper.session.timeout.ms | 6000 | 连接zk的session超时时间 |
zookeeper.sync.time.ms | 2000 | zk follower落后于zk leader的最长时间 |
参数 | 默认值 | 描述 |
---|---|---|
groupid | groupid | 一个字符串用来指示一组consumer所在的组 |
socket.timeout.ms | 30000 | socket超时时间 |
socket.buffersize | 64*1024 | socket receive buffer |
fetch.size | 300 * 1024 | 控制在一个请求中获取的消息的字节数。 这个参数在0.8.x中由fetch.message.max.bytes,fetch.min.bytes取代 |
backoff.increment.ms | 1000 | 这个参数避免在没有新数据的情况下重复频繁的拉数据。 如果拉到空数据,则多推后这个时间 |
queued.max.message.chunks | 2 | high level consumer内部缓存拉回来的消息到一个队列中。 这个值控制这个队列的大小 |
auto.commit.enable | true | 如果true,consumer定期地往zookeeper写入每个分区的offset |
auto.commit.interval.ms | 10000 | 往zookeeper上写offset的频率 |
auto.offset.reset | largest | 如果offset出了返回,则 smallest: 自动设置reset到最小的offset. largest : 自动设置offset到最大的offset. 其它值不允许,会抛出异常. |
consumer.timeout.ms | -1 | 默认-1,consumer在没有新消息时无限期的block。如果设置一个正值, 一个超时异常会抛出 |
rebalance.retries.max | 4 | rebalance时的最大尝试次数 |
参数 | 默认值 | 描述 |
---|---|---|
producer.type sync | 指定消息发送是同步还是异步 | 异步asyc成批发送用kafka.producer.AyncProducer, 同步sync用kafka.producer.SyncProducer |
metadata.broker.list | boker list | 使用这个参数传入boker和分区的静态信息,如host1:port1,host2:port2, 这个可以是全部boker的一部分 |
compression.codec | NoCompressionCodec | 消息压缩,默认不压缩 |
compressed.topics | null | 在设置了压缩的情况下,可以指定特定的topic压缩,未指定则全部压缩 |
message.send.max.retries | 3 | 消息发送最大尝试次数 |
retry.backoff.ms | 300 | 每次尝试增加的额外的间隔时间 |
topic.metadata.refresh.interval.ms | 600000 | 定期的获取元数据的时间。当分区丢失,leader不可用时producer也会主动获取元数据,如果为0,则每次发送完消息就获取元数据,不推荐。如果为负值,则只有在失败的情况下获取元数据。 |
queue.buffering.max.ms | 5000 | 在producer queue的缓存的数据最大时间,仅仅for asyc |
queue.buffering.max.message | 10000 | producer 缓存的消息的最大数量,仅仅for asyc |
queue.enqueue.timeout.ms | -1 0 | 当queue满时丢掉,负值是queue满时block,正值是queue满时block相应的时间,仅仅for asyc |
batch.num.messages | 200 | 一批消息的数量 |
request.required.acks | 0 | 0表示producer无需等待leader的确认,1代表需要leader确认写入它的本地log并立即确认,-1代表所有的备份都完成后确认。 |
request.timeout.ms | 10000 | 确认超时时间 |
./kafka-topics.sh --list --zookeeper ip:2181
列出该zookeeper中记录在案的topic列表,只有名字
./kafka-topics.sh --describe --zookeeper 127.0.0.1:2181 --topic test0
Topic: test0 PartitionCount:16 ReplicationFactor:3 Configs:
Topic: test0 Partition: 0 Leader: 0 Replicas: 0,2,1 Isr: 1,0,2
Topic: test0 Partition: 1 Leader: 1 Replicas: 1,2,0 Isr: 1,0,2
Topic: test0 Partition: 2 Leader: 2 Replicas: 2,0,1 Isr: 1,0,2
Topic: test0 Partition: 3 Leader: 1 Replicas: 1,2,0 Isr: 1,0,2
Topic: test0 Partition: 4 Leader: 2 Replicas: 2,0,1 Isr: 1,0,2
Topic: test0 Partition: 5 Leader: 0 Replicas: 0,1,2 Isr: 1,0,2
Topic: test0 Partition: 6 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: test0 Partition: 7 Leader: 2 Replicas: 2,1,0 Isr: 1,0,2
Topic: test0 Partition: 8 Leader: 2 Replicas: 2,0,1 Isr: 0,1,2
Topic: test0 Partition: 9 Leader: 0 Replicas: 0,2,1 Isr: 0,1,2
Topic: test0 Partition: 10 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: test0 Partition: 11 Leader: 2 Replicas: 2,1,0 Isr: 1,0,2
Topic: test0 Partition: 12 Leader: 0 Replicas: 0,2,1 Isr: 0,1,2
Topic: test0 Partition: 13 Leader: 1 Replicas: 1,0,2 Isr: 1,0,2
Topic: test0 Partition: 14 Leader: 2 Replicas: 2,1,0 Isr: 1,0,2
Topic: test0 Partition: 15 Leader: 0 Replicas: 0,1,2 Isr: 0,1,2
第一行显示partitions的概况,列出了Topic名字,partition总数,存储这些partition的broker数
以下每一行都是其中一个partition的详细信息:
举例:
比如上面结果的第一行:Topic: test0 Partition:0 Leader: 0 Replicas: 0,2,1 Isr: 1,0,2
Partition: 0
该partition编号是0
Replicas: 0,2,1
代表partition0 在broker0,broker1,broker2上保存了副本
Isr: 1,0,2
代表broker0,broker1,broker2都存活而且目前都和leader保持同步
Leader: 0
代表保存在broker0,broker1,broker2上的这三个副本中,leader是broker0
leader负责读写,broker1、broker2负责从broker0同步信息,平时没他俩什么事
当producer发送一个消息时,producer自己会判断发送到哪个partiton上,如果发到了partition0上,消息会发到leader,也就是broker0上,broker0处理这个消息,broker1、broker2从broker0同步这个消息
如果这个broker0挂了,那么kafka会在Isr列表里剩下的broker1、broker2中选一个新的leader
./kafka-topics.sh --create --topic test0--zookeeper 127.0.0.1:2181 --config max.message.bytes=12800000 --partitions 5 --replication-factor 1
说明:
--topic后面的test0是topic的名称
--zookeeper应该和server.properties文件中的zookeeper.connect一样
--partitions指定topic的partition数量,如果不指定该数量,默认是server.properties文件中的num.partitions配置值
--replication-factor指定每个partition的副本个数,默认1个
删除kafka的topic
./kafka-topics.sh --delete --zookeeper 127.0.0.1:2181 --topic test0
如果server.properties中没有把delete.topic.enable设为true,那么此时的删除并不是真正的删除,而是把topic标记为:marked for deletion
删除kafka中该topic相关的目录。
在server.properties中找到配置log.dirs,把该目录下test0相关的目录删掉
登录zookeeper client。
sh /bin/zkCli.sh
删除zookeeper中该topic相关的目录
rm -r /kafka/config/topics/test0
rm -r /kafka/brokers/topics/test0
rm -r /kafka/admin/delete_topics/test0 (topic被标记为marked for deletion时需要这个命令)
重启zookeeper和broker
sh bin/zkServer.sh restart
bin/kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list 127.0.0.1:9092 --topic test0 --time -1
或者可去zk上去查看offset值
bin/kafka-run-class.sh kafka.tools.ConsumerOffsetChecker
运行结果:
GROUP | TOPIC | PID | OFFSE | LOGSIZE | LAG |
---|---|---|---|---|---|
消费者组 | topic名字 | partition id | 当前已消费的条数 | 总条数 | 未消费的条数 |
bin/kafka-topics.sh --alter --zookeeper 127.0.0.1:2183 --partitions 10 --topic test0
{
"version": 1,
"partitions": [
{
"topic": "test0",
"partition": 0,
"replicas": [
1,2
]
},
{
"topic": "test0",
"partition": 1,
"replicas": [
1,2,3
]
},
{
"topic": "test0",
"partition": 2,
"replicas": [
1,2,3
]
}
]
}
bin/kafka-reassign-partitions.sh --zookeeper 127.0.0.1:9092 --reassignment-json-file addReplicas.json --execute
bin/kafka-server-start.sh -daemon config/server.properties
如下线broker0:
bin/kafka-run-class.sh kafka.admin.ShutdownBroker --zookeeper 127.0.0.1:2181 --broker 0 --num.retries 3 --retry.interval.ms 60 shutdown broker
producer 采用 push 模式将消息发布到 broker,每条消息都被 append 到 patition 中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障 kafka 吞吐率)。
producer 发送消息到 broker 时,会根据分区算法选择将其存储到哪一个 partition。其机制为:
物理上把 topic 分成一个或多个 patition(对应 server.properties 中的 num.partitions 配置),每个 patition 物理上对应一个文件夹(该文件夹存储该 patition 的所有消息和索引文件),如下:
无论消息是否被消费,kafka 都会保留所有消息。有两种策略可以删除旧数据:
同一个 partition 可能会有多个 replica(对应 server.properties 配置中的 default.replication.factor=N)。没有 replica 的情况下,一旦 broker 宕机,其上所有 patition 的数据都不可被消费,同时 producer 也不能再将数据存于其上的 patition。引入replication 之后,同一个 partition 可能会有多个 replica,而这时需要在这些 replica 之间选出一个 leader,producer 和 consumer 只与这个 leader 交互,其它 replica 作为 follower 从 leader 中复制数据。
Kafka 分配 Replica 的算法如下:
当 partition 对应的 leader 宕机时,需要从 follower 中选举出新 leader。在选举新leader时,一个基本的原则是,新的 leader 必须拥有旧 leader commit 过的所有消息。
kafka 在 zookeeper 中(/brokers/…/state)动态维护了一个 ISR(in-sync replicas),只有 ISR 里面的成员才能选为 leader。对于 f+1 个 replica,一个 partition 可以在容忍 f 个 replica 失效的情况下保证消息不丢失。
当所有 replica 都不工作时,
等待 ISR 中的任一个 replica 活过来,并选它作为 leader。可保障数据不丢失,但时间可能相对较长。
min.insync.replicas=1
rerplica.lag.time.max.ms=10000
# 如果leader发现follower超过10秒没有向它发起fech请求,那么leader考虑这个flower是不是程序出了点问题
# 或者资源紧张调度不过来,它太慢了,不希望它拖慢后面的进度,就把它从ISR中移除。
rerplica.lag.max.messages=4000
# 相差4000条就移除
# follower慢的时候,保证高可用性,同时满足这两个条件后又加入ISR中,
# 在可用性与一致性做了动态平衡 亮点
request.required.asks=0
# 0:相当于异步的,不需要leader给予回复,producer立即返回,发送就是成功,
那么发送消息网络超时或broker crash(1.Partition的Leader还没有commit消息 2.Leader与Follower数据不同步),
既有可能丢失也可能会重发
# 1:当leader接收到消息之后发送ack,丢会重发,丢的概率很小
# -1:当所有的follower都同步消息成功后发送ack. 丢失消息可能性比较低
kafka有同步(sync)、异步(async)以及oneway这三种发送方式,某些概念上区分也可以分为同步和异步两种,同步和异步的发送方式通过“producer.type”参数指定,而oneway由“request.require.acks”参数指定
底层根本原因:已经消费了数据,但是offset没提交。
try {
consumer.unsubscribe();
} catch (Exception e) {
}
try {
consumer.close();
} catch (Exception e) {
}
上面代码会导致部分offset没提交,下次启动时会重复消费。● 高级API写起来简单
● 不需要去自行去管理offset,系统通过zookeeper自行管理
● 不需要管理分区,副本等情况,系统自动管理
● 消费者断线会自动根据上一次记录在 zookeeper中的offset去接着获取数据(默认设置5s更新一下 zookeeper 中存的的offset),版本为0.10.2
● 可以使用group来区分对访问同一个topic的不同程序访问分离开来(不同的group记录不同的offset,这样不同程序读取同一个topic才不会因为offset互相影响)
缺点
● 不能自行控制 offset(对于某些特殊需求来说)
● 不能细化控制如分区、副本、zk 等
● 能够开发者自己控制offset,想从哪里读取就从哪里读取。
● 自行控制连接分区,对分区自定义进行负载均衡
● 对 zookeeper 的依赖性降低(如:offset 不一定非要靠 zk 存储,自行存储offset 即可,比如存在文件或者内存中)
缺点
● 太过复杂,需要自行控制 offset,连接哪个分区,找到分区 leader 等
partition数量由topic的并发决定,并发少则1个分区就可以,并发越高,分区数越多,可以提高吞吐量。,但是我们必须意识到集群的partition总量多大或者单个broker节点partition过多,都会对系统的可用性和消息延迟带来潜在的影响.
创建topic时指定topic数量
bin/kafka-topics.sh --create --zookeeper 10.25.58.35:2181 --replication-factor 3 --partitions 3 --topic test8
当kafka broker的被写入海量消息后,会生成很多数据文件,占用大量磁盘空间,kafka默认是保留7天,建议根据磁盘情况配置log.retention.hours,避免磁盘撑爆。段文件配置1GB,有利于快速回收磁盘空间,重启kafka加载也会加快。
为了大幅度提高producer写入吞吐量,需要定期批量写文件。建议配置:
每当producer写入10000条消息时,刷数据到磁盘 log.flush.interval.messages=10000
每间隔1秒钟时间,刷数据到磁盘 log.flush.interval.ms=1000
buffer.memory:在Producer端用来存放尚未发送出去的Message的缓冲区大小。缓冲区满了之后可以选择阻塞发送或抛出异常,由block.on.buffer.full的配置来决定。
compression.type:none:默认发送不进行压缩,可以配置一种适合的压缩算法,可以大幅度的减缓网络压力和Broker的存储压力。
num.consumer.fetchers:启动Consumer的个数,适当增加可以提高并发度。