概述
是什么
基于zookeeper协调的分布式日志系统(也可以当做MQ系统)
特点
- 快速持久化:可以在O(1)的系统开销下进行消息持久化;
- 高吞吐:在一台普通的服务器上既可以达到10W/s的吞吐速率;
- 完全的分布式系统:Broker、Producer和Consumer都原生自动支持分布式,自动实现负载均衡;
- 支持同步和异步复制两种高可用机制;
- 支持数据批量发送和拉取;
- 零拷贝技术(zero-copy):减少IO操作步骤,提高系统吞吐量;
- 数据迁移、扩容对用户透明;
- 无需停机即可扩展机器;
- 其他特性:丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制;
优点
- 客户端语言丰富:支持Java、.Net、PHP、Ruby、Python、Go等多种语言;
- 高性能:单机写入TPS约在100万条/秒,消息大小10个字节;
- 提供完全分布式架构,并有replica机制,拥有较高的可用性和可靠性,理论上支持消息无限堆积;
- 支持批量操作;
- 消费者采用Pull方式获取消息。消息有序,通过控制能够保证所有消息被消费且仅被消费一次;
- 有优秀的第三方KafkaWeb管理界面Kafka-Manager;
- 在日志领域比较成熟,被多家公司和多个开源项目使用。
缺点
- Kafka单机超过64个队列/分区时,Load时会发生明显的飙高现象。队列越多,负载越高,发送消息响应时间变长;
- 使用短轮询方式,实时性取决于轮询间隔时间;
- 消费失败不支持重试;
- 支持消息顺序,但是一台代理宕机后,就会产生消息乱序;
- 社区更新较慢。
性能分析
为什么用它
- 解耦
- 异步
- 削锋
使用场景
日志收集系统和消息系统。
kafka架构图
基本名词概念
clusters
集群,由多个broker组成
broker
集群下的服务器,一个服务器就是一个broker。
broker包含多个topic的partition(分区)数据
topic
主题,类别,类似于rabbitmq上的交换机
topic包含多partition,多个replica
partition
一个topic会被拆分成多个分区,分布在不同的broker上,
partition中的数据是有序的,对数据顺序有要求的,建议分区数设置为1
多个partition可以增加topic的吞吐率
partition的数量尽量小于broker的数量
replica
副本,用于数据备份,异常恢复切换的,如果replica设置的是2,一个patition,就有一个leader(用于数据读写)跟一个follower(用于备份恢复切换)
producer
生产者,生产者发送数据到topic的分区中,生产者可以指定发送分区,也可以使用默认的轮训写入分区,保证数据的均匀分布
Consumer Group
消费组,包含多个消费者,用于topic的广播跟单播,每个topic下的patition只会分配给一个消费组下的一个consumer
广播:建立不同CG消费
单播:使用一个CG消费
Consumer
消费者,隶属于CG消费组下面,一个消费组下面的Consumer数量建议跟topic的分区数一个,或者是小于分区数,不可大于分区数,因为每个topic下的patition只会分配给一个消费组下的一个consumer,如果消费组下的consumer多余分区数,多出来的消费者分配不到分区,属于资源浪费
Leader
针对分区的副本的,一个分区的多个副本中,仅有一个是Leader,负责读写,其他都属于Follower
Follower
分区副本中除Leader外的其他副本,用于Leader失效后选举为新的Leader提供读写
Offset
- 分区中消息的位置信息
- 每个分区的消息都是顺序的
- 消费者管理自己的offset,并主动提交给broker服务器记录并保存下来,存放地址:__consumer_offsets topic下,CG,topic,partition作为key存放
message
消息体,一般都会持久化到硬盘,消费后不会删除,不会出现像mq那样的消费后消息丢失的情况,但是会定期删除,默认是7天删除
zookeeper
- 一个分布式的,开放源码的分布式应用程序协调服务
- 干的事情:
- 配置管理,名字服务,提供分布式同步以及集群管理。
- 用于管理kafka集群
coordinator
- 用于管理消费组
- 记录消费组信息
reblance
分区跟消费组的分配
触发重新reblance
- 有新的consumer加入
- 旧的consumer挂了
- 消费处理时间超过了默认的5分钟
- coordinator挂了,集群选举出新的coordinator
- topic的partition新加
- consumer调用unsubscrible(),取消topic的订阅
kafka参数
生产者
acks = 1
batch.size = 16384
bootstrap.servers = [127.0.0.1:9092]
buffer.memory = 33554432
client.id =
compression.type = none
connections.max.idle.ms = 540000
enable.idempotence = false
interceptor.classes = []
key.serializer = class org.apache.kafka.common.serialization.StringSerializer
linger.ms = 0
max.block.ms = 60000
max.in.flight.requests.per.connection = 5
max.request.size = 1048576
metadata.max.age.ms = 300000
metric.reporters = []
metrics.num.samples = 2
metrics.recording.level = INFO
metrics.sample.window.ms = 30000
partitioner.class = class org.apache.kafka.clients.producer.internals.DefaultPartitioner
receive.buffer.bytes = 32768
reconnect.backoff.max.ms = 1000
reconnect.backoff.ms = 50
request.timeout.ms = 30000
retries = 0
retry.backoff.ms = 100
sasl.client.callback.handler.class = null
sasl.jaas.config = null
sasl.kerberos.kinit.cmd = /usr/bin/kinit
sasl.kerberos.min.time.before.relogin = 60000
sasl.kerberos.service.name = null
sasl.kerberos.ticket.renew.jitter = 0.05
sasl.kerberos.ticket.renew.window.factor = 0.8
sasl.login.callback.handler.class = null
sasl.login.class = null
sasl.login.refresh.buffer.seconds = 300
sasl.login.refresh.min.period.seconds = 60
sasl.login.refresh.window.factor = 0.8
sasl.login.refresh.window.jitter = 0.05
sasl.mechanism = GSSAPI
security.protocol = PLAINTEXT
send.buffer.bytes = 131072
ssl.cipher.suites = null
ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
ssl.endpoint.identification.algorithm = https
ssl.key.password = null
ssl.keymanager.algorithm = SunX509
ssl.keystore.location = null
ssl.keystore.password = null
ssl.keystore.type = JKS
ssl.protocol = TLS
ssl.provider = null
ssl.secure.random.implementation = null
ssl.trustmanager.algorithm = PKIX
ssl.truststore.location = null
ssl.truststore.password = null
ssl.truststore.type = JKS
transaction.timeout.ms = 60000
transactional.id = null
value.serializer = class org.apache.kafka.common.serialization.StringSerializer
消费者
auto.commit.interval.ms = 5000
auto.offset.reset = latest
bootstrap.servers = [127.0.0.1:9092]
check.crcs = true
client.id =
connections.max.idle.ms = 540000
default.api.timeout.ms = 60000
enable.auto.commit = true
exclude.internal.topics = true
fetch.max.bytes = 52428800
fetch.max.wait.ms = 500
fetch.min.bytes = 1
group.id = test
heartbeat.interval.ms = 3000
interceptor.classes = []
internal.leave.group.on.close = true
isolation.level = read_uncommitted
key.deserializer = class org.apache.kafka.common.serialization.StringDeserializer
max.partition.fetch.bytes = 1048576
max.poll.interval.ms = 300000
max.poll.records = 500
metadata.max.age.ms = 300000
metric.reporters = []
metrics.num.samples = 2
metrics.recording.level = INFO
metrics.sample.window.ms = 30000
partition.assignment.strategy = [class org.apache.kafka.clients.consumer.RangeAssignor]
receive.buffer.bytes = 65536
reconnect.backoff.max.ms = 1000
reconnect.backoff.ms = 50
request.timeout.ms = 30000
retry.backoff.ms = 100
sasl.client.callback.handler.class = null
sasl.jaas.config = null
sasl.kerberos.kinit.cmd = /usr/bin/kinit
sasl.kerberos.min.time.before.relogin = 60000
sasl.kerberos.service.name = null
sasl.kerberos.ticket.renew.jitter = 0.05
sasl.kerberos.ticket.renew.window.factor = 0.8
sasl.login.callback.handler.class = null
sasl.login.class = null
sasl.login.refresh.buffer.seconds = 300
sasl.login.refresh.min.period.seconds = 60
sasl.login.refresh.window.factor = 0.8
sasl.login.refresh.window.jitter = 0.05
sasl.mechanism = GSSAPI
security.protocol = PLAINTEXT
send.buffer.bytes = 131072
session.timeout.ms = 10000
ssl.cipher.suites = null
ssl.enabled.protocols = [TLSv1.2, TLSv1.1, TLSv1]
ssl.endpoint.identification.algorithm = https
ssl.key.password = null
ssl.keymanager.algorithm = SunX509
ssl.keystore.location = null
ssl.keystore.password = null
ssl.keystore.type = JKS
ssl.protocol = TLS
ssl.provider = null
ssl.secure.random.implementation = null
ssl.trustmanager.algorithm = PKIX
ssl.truststore.location = null
ssl.truststore.password = null
ssl.truststore.type = JKS
value.deserializer = class org.apache.kafka.common.serialization.StringDeserializer
使用方式
注意点
重复消费问题
max.poll.interval.ms 两次poll操作允许的最大时间间隔。单位毫秒。默认值300000(5分钟)。
如果设置的手动提交offset,消费端要控制处理时间在五分钟内,否则无法提交offset,导致重复消费
kafka manager是不支持数据推送的
kafka消费是通过消费者先从coordinator获取offset之后,进行offset偏移,请求kafka集群获取消息的,数据本身存在硬盘上,如果消费失败了又自动提交了offset。
通常想要补偿:是重新推送失败消息到kafka,但是目前无法kafka自带的kafka manager是没有补推功能的
所以大家消费kafka的时候要思考降级方案
消费失败降级处理
- 手动提交的话,反复失败,卡住其他正常数据
- 自动提交,失败的数据怎么重新消费,kafka是消费者根据offset进行消费的,coordinator中保存的消费组的offset已经是最新的offset了
阿里云:
消息队列Kafka版是按分区逐条消息顺序向前推进消费的,如果消费端拿到某条消息后执行消费逻辑失败,例如应用服务器出现了脏数据,导致某条消息处理失败,等待人工干预,那么有以下两种处理方式:
1. 失败后一直尝试再次执行消费逻辑。这种方式有可能造成消费线程阻塞在当前消息,无法向前推进,造成消息堆积。
2. 由于消息队列Kafka版没有处理失败消息的设计,实践中通常会打印失败的消息或者存储到某个服务(例如创建一个Topic专门用来放失败的消息),然后定时检查失败消息的情况,分析失败原因,根据情况处理。
提高消费速度
提高消费速度有以下两个办法:
- 增加Consumer实例个数。
- 可以在进程内直接增加(需要保证每个实例对应一个线程,否则没有太大意义),也可以部署多个消费实例进程;需要注意的是,实例个数超过分区数量后就不再能提高速度,将会有消费实例不工作。
- 增加消费线程。
- 增加Consumer实例本质上也是增加线程的方式来提升速度,因此更加重要的性能提升方式是增加消费线程,最基本的步骤如下:
- 定义一个线程池。
- Poll数据。
- 把数据提交到线程池进行并发处理。
- 等并发结果返回成功后,再次poll数据执行。