文章目录
概念
Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群来提供实时的消息。
特点
- 高吞吐:即使是非常普通的硬件Kafka也可以支持每秒数百万的消息,不要因为将消息持久化的磁盘怀疑它的性能。
- 消息持久化:通过O(1)的磁盘数据结构提供消息的持久化,这种结构对于即使数以TB的消息存储也能够保持长时间的稳定性能。
- 完全分布式:Producer、Broker、Consumer支持水平拓展。
- 同时满足适应在线流处理(Storm)和离线批量处理(Hadoop)。
架构
-
Broker和Consumer都以来Zookeeper。其中Broker和Zookeeper作为后台服务,Consumer和Producer作为SDK,编写自己的Producer和Consumer时可以将这些SDK引入到工程中。
-
Producer将数据push到Broke中,Consumer从Broker中pull数据1。
-
Procucer获取Broker集群元信息:初始化Producer连接的时候可以指定Broker集群信息,加入集群有300个Broker,Producer指定2个Broker地址,只要有一个连接成功,Producer就会从Broker中获取整个集群存活的Broker列表信息,然后将该元信息存到内存中。如果某个Brocker挂掉Producer就需要刷新元信息,有两种方式:
- Producer向Broker发送信息失败会主动触发刷新元信息,重新获取元信息。
- Producer周期性刷新元信息,刷新周期可以在Producer实例化时设置。
-
Consumer是通过连接Zookeeper获取Broker集群元信息的。
1 Topic&Partition
- 逻辑概念,一个Topic可以分布在一个或者多个Broker上。
- 一个Topic包含一个或者多个Partition。
- 每条消息都有一个Topic。
- Producer发布消息时必须指定一个Topic。
- Consumer订阅消息时,必须指定订阅哪个主题的消息。Consumer也可以通过黑名单或者白名单去指定希望不消费或者消费哪些数据。
由上图可知,Kafka会平均分配Broker上的Partition以便每个Broker负载尽量均衡,每个Topic的Partition不要求一样多。
1.1 Partition
- 物理概念,一个Partition只分布于一Broker上(不考虑备份)
- 一个Partition物理上对应一文件夹
- 一个Partition包含多个Segment ( Segment对用户透明)
- —个Segment对应一个文件
- Segment由—个个不可变记录组成
- 记录只会被append到Segment中,不会被单独删除或者修改
- 清除过期日志时,直接删除一个或多个Segment,清理日志策略如下:
- 基于时间,Kafka默认保存日志168小时(7天)。
- 设置Partition保留日志的容量,比如10GB,超过10GB就会从Partition开头开始以Segment为单位逐一删除,直到Partition容量10GB大于等于0。由于是删除整个文件,所以效率很高。。
1.2 创建主题加以分析
1.2.1 主题创建
- 创建一个名称为“topic1”3个partition的主题:
.\kafka-topics.bat --zookeeper localhost:2181 --create --topic topic1 --replication-factor 1 --partitions 3
- 查看主题
创建的主题以及分区和预期的一致,三个Leader的id都是0是因为环境中只有一个Broker且id为0.\kafka-topics.bat --zookeeper localhost:2181 --describe --topic topic1 Topic: topic1 PartitionCount: 3 ReplicationFactor: 1 Configs: Topic: topic1 Partition: 0 Leader: 0 Replicas: 0 Isr: 0 Topic: topic1 Partition: 1 Leader: 0 Replicas: 0 Isr: 0 Topic: topic1 Partition: 2 Leader: 0 Replicas: 0 Isr: 0
1.2.2 数据查看分析
- 查看server.properties配置文件找出数据存放位置。
由上图看出,如果要指定多个目录可以用逗号分隔。如果kafka服务器有多个硬盘分区就可以配置多个目录。 - 进入数据目录。
可以看到有三个文件夹,对应topic三个分区(以topic名字+partition号命名),也应证了每个partition对应一个文件夹。进入topic1-0:
00000000000000000000.index是segment索引,00000000000000000000.log是保存数据的文件,它们共同组成一个segment。在查找某个offset下的数据时,就可以利用index找到数据在log文件中的起始位置。- 为什么名字是00000000000000000000呢?
是因为segment是按需创建的,这是创建的第一个。每个segme都会以一条数据中offset最小或者第一天数据offset为文件名。
- 为什么名字是00000000000000000000呢?
1.3. 总结
再次回到这个图,可以得出以下结论:
- 每个partition都相当于由segment追加的数组。
- 每个partition中每条数据都有自己唯一的编号,该编号就是offset,并且是自增的,每条新的数据过来都有一个新的offset。
- 不同的partition之间,offset是可以重复的。
因此要定位一条消息的过程是这样的:
4. 需要知道这条消息在哪个patition
5. 需要知道这条消息在partition中的offset是多少
2 Producer数据分发
Producer如何知道数据应该发往哪个Broker或者partition呢?
- 实现Partitioner接口。
public interface Partitioner{ int partition(Object key, int numPartitions); }
- 在Producer创建时置入属性中。
props.put("partitioner.class","com.kafka.myparitioner.CidPartitioner");
一般的算法有:
- hash算法,根据key进行hash。相同的key会发送到同一个partition。
- 轮询算法。
- 随机算法。
3 Sysc Producer&Async Producer
3.1 Sysc Producer(同步发送)
发送成功才发送下条消息,否则一直retry知道三次发送都不成功就会发送失败然后抛出异常,至于异常就可以根据需要随意处理了。
特点
- 低延迟
- 低吞吐
- 无数据丢失
3.2 Asysc Producer(异步发送)
不是对每条数据实时发送,二十先放进queue里面,达到一定要求后发送线程才会将数据批量发送给Broker,如果queue饱和或者延迟到一定程度,kafka就会将新的数据丢弃。
特点
- 高延迟
- 高吞吐
- 可能有数据丢失
4 Producer参数配置
属性 | 默认值 | 描述 |
---|---|---|
metadata.broker.list | Broker地址配置,格式:host1:port1,host2:port2 | |
request.timeout.ms | 10000 | Producer等待Broker响应时间 |
producer.type | syns | Producer发送数据方式,async或者sync |
serializer.class | kafka.serializer.DefaultEncoder | 消息序列化方式 |
key.serializer.class | key序列化方式,不指定就默认使用消息指定的序列化方式 | |
partitioner.class | kafka.producer.DefaultPartitioner | 指定Producer发送消息到Broker的策略 |
compression.codec | none | 数据压缩策略,备选值:none、gzip、snappy |
compressed.topics | null | 在设定了压缩的情况下,设置对指定的主题进行压缩,如果没设置就对所有的主题进行压缩 |
message.send.max.retries | 3 | 由于网络等原因,Producer向Broker发送消息可能会失败,Producer在发送消息失败后会自动重试发送,此参数就是设置重试次数的 |
retry.backoff.ms | 100 | 两次重试之间时间间隔 |
topic.metadata.refresh.interval.ms | 600*1000 | 仅当出现分区丢失、leader不可用等情况时并且Producer发送消息后(发送失败),Producer将轮询刷新Broker集群信息元数据,默认事件我i10分钟;如果将此值设置为0则每次发送消息后(无论成与败)都会刷新元数据;如果设置为负值则仅会在发送失败后触发刷新 |
queue.buffering.max.ms | 5000 | 该参数仅在异步发送模式下生效,Producer会将在设置时间内的消息积攒起来一次发送,增加了吞吐,也增导致了消息发送延迟 |
queue.buffering.max.messages | 10000 | 使用异步发送时,Broker等待Producer消息数量最大值,如果指定该值没有满足要求时间到了也会发送出去,如果时间没到该值达到要求了也会发送出去 |
queue.enqueue.timeout.ms | -1 | 使用异步发送时,queue不是无限大的,如果将该值设置为0数据一道Broker就会被处理,如果queue饱和那就马上删掉它,这时候Producer永远不会发送阻塞;如果设置为-1,Procuder将永远等待直到数据被Broker处理掉 |
batch.num.messages | 200 | 指定一次发送多少条 |
send.buffer.bytes | 100*1024 | Socket写数据buffer大小 |
client.id | “” | 是用户特定的字符串,用来在每次请求中帮助跟踪调用。它应该可以逻辑上确认产生这个请求的应用 |
数据复制&failover
回顾CAP理论
Replica(副本)
1 什么是Replica
- 当某个Topic的replication-factor为N且N大于1时,每个Partition都会有N个副本(Replica )
- Replica的个数小于等于Broker数,即对每个Partition而言每个Broker上只会有一个Replica ,因此可用Broker ID表示Replica
为何这么设置?下图中,假如Partiton0的两个副本都在Broker 1上,那么如果Broker 1挂掉就达不到提高数据可用性。
- 所有Partition的所有Replica默认情况会均匀分布到所有Broker上
这里说到的Replica一定是某个Partition的Replica,对某个Topic来讲,Partition副本数是一样的,说到底Replica是Topic级别的定义,复制是以Partition为单位实际上Topic也是被复制
2 Data Replication要解决的问题
2.1 如何Propagate(传播)消息
kafka集群类似于master-slave,但是并不是说一个集群只有一个中心节点,而是对于每个Partition而言有一个leader而其他的副本叫做follower,可以认为这个learder是这个Partition中心节点。可以认为每个Partition是一个小的集群,都有属于自己的leader/marster,有好几个副本/slave。上图可以知晓,topic1-part1的leader在broker1上,两个副本在broker2、broker3上,写入数据只会写入broker1然后复制到broker2和broker3上;topic2-part1的leader在broker2上,两个副本在broker3、broker4上;…
注意
- 数据的复制是由follower周期性的从leader拉取(pull)数据
- 上面说到写数据是向leader上写,读数据是否像数据库读写分离那样从slave上读呢?答案是否定的,读依然是从leader上读。follower只是保证leader挂掉的时候顶上去,并不能保证自己一边复制数据一边向外提供服务。
2.2 何时Commit
同步复制一致性高,可用性差;异步复制一致性差可用性高。kafka默认既不是同步复制也不是异步复制,而是使用IRSR机制。
2.2.1 ISR(in-sync Replica )
- Leader爸维护一个与其基本保持同步的Replica列表,该列表称为ISR
- 如果一个Follower比Leader落后太多,或者超过一定时间未发起翻gg制请求,则Leader将其从ISR中移除
- 当ISR中所有Replica都向Leader发送ACK时 r
Leader即Commit
2.2.2 Commit策略
-
ServerSfiM
follower从ISR删除策略
- replica.lag.time.max.ms=10000
follower在10000/配置时间内没有向leader发送确认请求 - replica.lag.max.nnessages=4000
follower和leader之间数据相差4000/配置条 - follower宕机
当follower和leader之间的差距变小后,leader又会重新将follower重新加入到ISR中。这就在可用性和一致性之间做了动态的平衡。这是kafka和其他好多系统区别所在
- replica.lag.time.max.ms=10000
-
Topic配罝,指定ISR最小值
- min.insync.replicas=1
-
Producer配置
- request.required.acks=0,是异步的不需要leader给它任何的ack,立马返回。
- request.required.acks=1,必须等待leader发送ack才认为发送成功
- request.required.acks=-1,不需要自己决策,交给broker进行决策
如果要使用ISR就需要将 request.required.acks的值设置为-1
2.3 如何处理Replica恢复
- 第一步:只有A、B、C同时拥有m1所以m1才会被commit
- 第二步:A宕机,B变成新的leader,新到达的数据m2会被commit
- 第三步:A依然宕机,新来的数据m4、m5被提交
- 第四步:A恢复,由于知道自己之前commit到m1,所以需要删除m1之后的数据,然后进行追赶B、C,直到将B、C节点commit的数据拿过去之后才被B重新添加到ISR中。
这其中有个问题,m3去哪了?
因为m3从来没有被commit过。Producer会retry,尝试3次之后依然失败,那么、就会丢失。假如m3在B节点commit数据m4、m5之后被retry成功了,那么就不会造成数据丢失,但是造成数据的发送顺序和接收顺序会不一样,所以kafka数据顺序消费里面的顺序指的是数据被接收或者被commit的顺序,而不是发送的顺序
2.4 如何处理Replica全部宕机
kafka提供以下两种方式供配置选择:
- 等待ISR中任一Replica恢复,并选它为Leader
- 等待时间较长,降低可用性
- 或ISR中的所有Replica都无法恢复或者数据丟失,则该Partition将永不可用
- 选择第一个恢复的Replica为新的Leader,无论它是否在ISR中
- 并未包含所有已被之前Leader Commit过的消息,因此会造成数据丟失
- 可用性较高
未完…
如果Consumer处理数据慢,Broker使用push方式将数据推送给Consumer会造成Consumer延迟、数据丢失、数据积压,甚至造成Consumer宕机。Comsumer使用pull的方式获取数据消费多少拿多少,对抗峰值压力效果显著。Broker也不需要感知Consumer的存在。 ↩︎