kafka-个人成长之路

378 阅读31分钟

先散乱的记录一些,过些时候系统整理成逻辑清晰的文章

资料记录:

www.iteye.com/blog/murray…

www.cnblogs.com/qiaoyihang/…

blog.csdn.net/chixushuchu…

blog.csdn.net/murray2081/…

blog.csdn.net/qq_27467601…

zhuanlan.zhihu.com/p/136799968

blog.csdn.net/liuxins/art…

zhuanlan.zhihu.com/p/54287819

blog.csdn.net/qq_38245668…

www.zhihu.com/question/35…

Kafka概述Apache Kafka由Scala和Java编写,基于生产者和消费者模型作为开源的分布式发布订阅消息系统。它提供了类似于JMS的特性,但设计上又有很大区别,它不是JMS规范的实现,如Kafka允许多个消费者主动拉取数据,而在JMS中只有点对点模式消费者才会主动拉取数据。Kafka对消息保存时根据topic进行归类,发送消息者称为producer,消息接收者称为consumer。Kafka集群由多个Kafka实例组成,每个实例称为broker。并且Kafka集群基于zookeeper保存一些meta信息,来保证系统的高可用性。
生产者可以直接把数据传递给broker,broker通过zookeeper进行leader和follower的选举管理;消费者可以通过zookeeper保存读取的位置offset以及读取的topic的分区信息。这样做有以下几个好处:
1.生产者和消费者的负载解耦2.消费者可以按照自己的“能力”拉取数据3.消费者可以自定义消费数量
Kafka与传统消息系统相比,有以下不同:1.Kafka是分布式的,易于水平扩展2.同时为发布和订阅提供高吞吐量3.支持多订阅者,当失败时能自动对消费者进行rebalance4.将消息持久化到磁盘,因此可用于批量消费,例如ETL以及实时应用程序Kafka中的重要概念

名称解释
brokerKafka集群中的实例进程,负责数据存储。在Kafka集群中每个broker都有一个唯一的brokerId。通过broker来接受producer和consumer的请求,并把消息持久化到磁盘。每个Kafka集群中会选举出一个broker来担任Controller,负责处理分区的leader选举,协调分区迁移等工作
topicKafka根据topic对消息进行归类(逻辑划分),发布到Kafka集群的每条消息都需要指定一个topic。落到磁盘上对应的是partition目录,partition目录中有多个segement组合(后缀为index、log的文件)。一个topic对应一个或多个partition,一个partition对应多个segment组合
producer向broker发送消息的生产者。负责数据生产和数据分发。生产者代码可以集成到任务系统中。数据分发策略默认为defaultPartition  Utils.abs(key.hashCode)%numPartitions
consumer从broker读取消息的消费者【实际上consumer是通过与zookeeper通信获取broker地址进行消息消费】
ConsumerGroup数据消费者组,ConsumerGroup(以下简称CG)可以有多个。可以把多个consumer线程划分为一个组,组里面所有成员共同消费一个topic的数据(一个topic的多个partition),组员之间不能重复消费
partition分区,一个topic可以分为多个partition(分布在多个broker上,实现扩展性),每个partition可以设置多个副本,会从多个副本中选取出一个leader负责读写操作。每个partition是一个有序的队列(partition中的每条消息都会被分配一个有序的id(offset)),Kafka只保证按每个partition内部有序并且被顺序消费,不保证一个topic的整体(多个partition间)的顺序
offset每条消息在文件中的偏移量。Kafka的存储文件都是按照offset.index来命名,方便查找
zookeeper保存meta信息,管理集群配置,以及在CG发生变化时进行rebalance
ReplicationKafka支持以partition为单位对消息进行冗余备份,每个partition都可以配置至少1个Replication(副本数包括本身)
leader partition每个Replication集合中的partition都会选出一个唯一的leader,所有的读写请求都由leader处理。其他Replicas从leader处把数据更新同步到本地,过程类似MySQL中的Binlog同步。
ISR(In-Sync Replica)Replicas的一个子集,表示目前"活着的"且与leader能够保持"联系"的Replicas集合。由于读写都是首先落到leader上,所以一般来说通过同步机制从leader上拉取数据的Replica都会和leader有一些延迟(包括了延迟时间和延迟条数两个维度),任意一个超过阈值都会把该Replica踢出ISR。每个partition都有它自己独立的ISR。

**Kafka中的广播和单播
**

每个consumer属于一个特定的CG,一条消息可以发送到多个不同的CG,但是一个CG中只能有一个consumer能够消费该消息。目的:实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)。一个topic可以有多个CG。topic的消息会复制(逻辑概念)到所有的CG,但每个partition只会把消息发给该CG中的一个consumer。

【实现广播】每个consumer有一个独立的CG

【实现单播】所有的consumer在同一个CG

用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic。Kafka消息的分发****1.producer客户端负责消息的分发1)Kafka集群中的任何一个broker都可以向producer提供metadata信息,这些metadata中包含集群中存活的servers列表、partitions leader列表等信息2)当producer获取到metadata信息之后,producer将会和topic下所有partition leader保持socket连接3)消息由producer直接通过socket发送到broker,中间不会经过任何"路由层",消息被路由到哪个partition上由producer通过一些策略如随机、轮巡等决定如果一个topic中有多个partition,那么在producer端实现"消息均衡分发"是非常必要的。在producer端的配置文件中,开发者可以指定partition路由的方式,具体流程:

1)连接broker-list中任意一台broker服务器

2)发送数据时,需要知道topic对应的partition个数及leader partition所在节点。这些信息由broker提供,每一个broker都能提供一份元数据信息(如哪些broker是存活的,哪个topic有多少分区,哪个分区是leader)

3)数据生产,数据发送到哪个partition的leader由producer代码决定

4)数据通过socket连接,直接发送到partition所在的broker2.producer消息发送的应答机制设置发送数据是否需要服务端的反馈,由参数request.required.acks的值决定:0: producer不会等待broker发送ack。最低延迟,持久化保证弱,当server挂掉时会丢失数据1: 当leader接收到消息之后发送ack。当前leader接收到数据后,producer会得到一个ack,更好的持久性,因为在server确认请求成功后,client才会返回。如果数据刚写到leader还没来得及复制leader就挂了,消息可能会丢失

-1: 当所有的follower都同步消息成功后发送ack。最好的持久性,只要有一个replica存活,数据就不会丢失。但相对延迟高\

3.分发策略

默认分发策略:def partition(key: T, numPartitions: Int): Int = {

    Utils.abs(key.hashCode) % numPartitions}。

其他策略:轮询、随机等。

consumer与topic关系通常情况下,一个消费者组有多个consumer,并且一个consumer只会属于一个消费者组。这样不仅可以提高topic中消息的并发消费能力,还能提高"故障容错"。如消费组中的某个consumer挂掉,那么它消费的partition将会由同组内其他的consumer自动接管。一个CG中所有的consumer将会交错的消费整个topic,每个消费组中consumer消息消费互相独立,可以认为一个消费者组就是一个"订阅"者。注意:对于topic中的一条特定的消息,只会被订阅此topic的每个消费者组中的其中一个consumer消费。同时,Kafka的设计原理决定,对于一个topic,同一个消费者组中如果有多于partition个数的consumer,则意味着某些consumer将无法消费消息。consumer负载均衡
最好是一个partition对应一个consumer。如果consumer数量过多,必然有空闲的consumer。

当一个消费者组中,有consumer加入或者离开时,会触发partition消费的rebalance。均衡的最终目的为了提升topic的并发消费能力,步骤如下:

比如一个topic有4个分区:P0、P1、P2、P3,一个CG中有C1、C2两个consumer。\

首先根据partition索引号对partition排序:P0、P1、P2、P3,再根据consumer的id排序:C0、C1

计算倍数: M = [P0,P1,P2,P3].size / [C0,C1].size,本例值M=2(向上取整)

然后依次分配partition: C0 = [P0,P1],C1=[P2,P3],即Ci = [P(i * M),P((i + 1) * M -1)]
Kafka文件存储机制****1.文件存储基本结构在Kafka文件存储中,同一个topic下有一个或多个不同partition,每个partition为一个目录,partition命名规则为topic名称+有序序号,第一个partition序号从0开始,序号最大值为partition数量减1。每个partition相当于一个巨型文件被平均分配到多个大小相等segment段数据文件中。但每个段segment file消息数量不一定相等,这种特性方便老的segment file快速被删除即方便已被消费的消息的清理,提高磁盘利用率。segment文件生命周期由服务端配置参数(log.segment.bytes:当segment文件达到多大时滚动生成一个新的segment文件,log.roll.{ms,hours}:滚动生成新的segment的时间即使没有达到设置的segment文件最大值等若干参数)决定。 

segment的意义:当Kafka producer不断发送消息,必然会引起partition文件的无限扩张,这样对于消息文件的维护以及已经被消费的消息的清理带来严重影响。通过参数设置segment可以指定保留多长时间的数据,及时清理已经被消费的消息,提高磁盘利用率,目前默认保存7天数据。

2.partition segment1)segment file组成由2大部分组成,分别为index file和data file,两个文件一一对应,成对出现,后缀".index"和“.log”分别表示为segment索引文件、数据文件。数值大小为64位,20位数字字符长度,没有数字用0填充,如下:\

图片

2)segment 文件命名规则

partition全局的第一个segment从0开始,后续每个segment文件名为上一个segment文件最后一条消息的offset值。数值最大为64位long大小,19位数字字符长度,没有数字用0填充3)索引文件存储大量元数据,数据文件存储大量消息,索引文件中元数据指向对应数据文件中message的物理偏移地址
4)segment data file由许多message组成,物理结构如下:

关键字解释说明
8 byte offset在partition内每条消息都有一个有序的id号:offset,它可以唯一确定每条消息在partition内的位置。即offset表示partition的第多少个message
4 byte message sizemessage大小
4 byte CRC32用crc32校验message
1 byte "magic"表示本次发布Kafka服务程序协议版本号
1 byte "attributes"表示为独立版本、或标识压缩类型、或编码类型
4 byte key length表示key的长度,当key为-1时,K byte key字段不填
value bytes payload表示实际消息数据

Kafka查找message已知offset查找相应的message,需要通过下面2个步骤查找:1.查找segment file

00000000000000000000.index表示最开始的文件,起始偏移量为0

00000000000000000099.index的消息量起始偏移量为100=99+1

00000000000000000999.index的起始偏移量为1000=999+1

其他后续文件依次类推。以起始偏移量命名并排序这些文件,只要根据offset按照"二分查找"文件列表,就可以快速定位到具体文件。2.通过segment file查找message根据offset,依次定位index元数据物理位置和log的物理偏移位置,然后再在log中顺序查找直至找到对应offset位置即可。\

初始认识--kafka的基因:

  • 消息队列MQ,顾名思义就是有顺序的消息,排着队,一个一个进行消费
  • kafka的基因是天生分布式
  • kafka基因是天生持久化
  • 不管是生产者还是消费者连接上kafka的时候第一件事就是获取元数据

1635921592596_ADFC67E4-BEE5-43d8-BA44-2073F7BBBF40.png

基本概念:

zookeeper:一个注册中心,好比一个大章鱼,它就是章鱼本身,手多管得宽。

broker集群:一系列的kafka实例,一台机器上的多个实例,docker启动的kafka实例都算其中一个成员

生产者:spring war包中,springboot jar包中,springcloud 微服务中调用发送数据的接口的那一段代码

消费者:spring war包中,springboot jar包中,springcloud 微服务中调用获取数据的接口的那一段代码

一个springboot工程打包成一个jar,然后换个端口再打包成一个jar,这就是两个消费者了

注意:生产者,消费者,发布订阅,不要心里想着公众号这些东西。这是基于服务端的,不是明面上的玩意儿。

这个地方需要澄清一个事实,当你消费者的springboot程序持续集成的时候,该服务会先停止再更新,那么就会有消费者从消费者组中退出再进入的情况,那么该消费者正在处消费的topic,消费到哪一处(偏移量)都是非常关键的,如果不处理好就会重复消费,露掉消费等等,具体方法下文有部分解释,我的其他文章(引用别人的较多)会有详细的解释和解决方案

消费者

消费者的时候-群组的概念

群组:消费者动态加入,动态扩容消费者,就好比docker一样

群组:分区变化了,群组(再均衡监听器,事件的方式),动态,一般推荐群组消费

独立消费者--没有群组的消费者

独立消费者不需要订阅主题,只需要分配主题中分区即可

独立消费者:没有再均衡监听器,消费群组变化没有,简单粗暴;一般适用于所有东西都定好了,再也不会变了;消费者有多少个,分区有多少个这种情况,考虑的因素会非常多,最原始的消费者,了解一下就可以了。

集群

zookeeper:

blog.csdn.net/zhaoshengli…

blog.csdn.net/enlyhua/art…

配置文件上诉有详解

重点提示几个地方

每一个broker在集群中的唯一标示,要求是正数。在改变IP地址,不改变broker.id的话不会影响consumers

broker.id = 1

zookeeper集群的地址,可以是多个,多个之间用逗号分割 hostname1:port1,hostname2:port2,hostname3:port3

zookeeper.connect = localhost:2181/kafka   zookeeper也可以做别的事情,不是只管kafka这一家,因此加一个节点进行区分

集群的成员关系

控制器

选举出来的,主要任务就是分区首领的选举

先加入zookeeper的broker(节点)是控制器角色。

复制kafka的核心

首领副本

分配首领的时候,遵循最少使用原则;首领副本不是跟着主机走的,是跟着kafka的某一个主题的某个分区走的

跟随者副本。

上面讲的其他副本则成为跟随者,也就是跟随者副本。

优先副本

三台以及以上的服务器,有三个副本,一个是首领副本,其他的都称为跟追着副本。

皇帝-太子-其他皇子的关系

配置文件中

优先副本参数设置为true

深入理解kafka

不管是生产者(springboot jar包这些东西或者其他语言开发的工程中的代码段)

还是消费者(springboot jar包这些东西或者其他语言开发的工程中的代码段)

第一个内部机制:

当他们在application.yml配置文件中配置好kafka的地址,然后代码调用到真正连接上的时候,会首先随机连接一个broker,随机的一个kafka,看看这个节点下面的目标主题的分区是不是“太子”,拿到一张元数据清单,该清单上面会明明白白写的谁是“太子”

1635925253255_8024D459-FFD8-4f84-9DB3-EF26AF117329.png

把这个清单先缓存下来,然后后续如果分区再均衡就会重新搞一份这种清单,因此一个消费者或者生产者,连接上kafka的时候都会走一遍分区再均衡的代码过程,因此每个工程的日志都会看到这一段处理日志(和kafka的topic中是否有消息无关,先认清家门再使劲干活)。

第二个内部机制:

就是往往希望你是发一个批次的,操作:

1.设置一个长一点的时间,条数达到就发出去

2.时间比较短,条数不够也给你发出去

1635925565057_B69BFAAA-8C30-4a7b-A7E4-6D5ED4C3110D.png

第三个内部机制:

ACKS--对于生产者的概念

ACKS = 1 (领导确认,发送出去的时候领导确认)

ACKS = 0 (不用确认,忘kafka里面发消息,有可能丢了,还是会认为,我已经发送成功了。好比删库跑路)

ACKS = -1,all 全部确认,就是消息经过分区0之后,它还会把这个消息往另一台机器上的副本中再发送一次,都好了才算真正发送成功;一般不这样设置,会比较消耗性能,先发送出去,再慢慢复制到副本中。

一般设置0或者1

第四个内部机制:

ISR

kafka进行副本复制的时候,是允许大部分的数据写入副本的;不一定要求必须选取都写入副本中再进行下一步,可以不完美,不完美不一定意味着不安全。

动态复制方案 会维护一个列表(每个副本,offset,消息相关的信息)

打个比方 A B C 三台kafka,同一个分区的3个副本

A 数据100 B 数据90 C 数据80

如果A机器挂了,选举优先选举B,但是有个问题,A中的10条还没来得及给B复制过去,所以这种方案中是允许有适当的数据丢失的。

为什么宕机有可能数据丢失还是要使用ISR

如果你采用全复制的方案,要不同步,要不就是异步,性能肯定会差,例子(出去玩,等女朋友出门的生活案例)

怎么避免呢,上面的ACKS 中的值设置为1或者0就行,如果你想要任何情况下都不丢失就设置为all或者-1

ACKS设置all或者-1 ,银行场景

ACKS设置1或者0,日志啊,这些基本操作,无所谓

推荐做法就是ISR ACKS结合会保证数据不会丢失

kafka存储机制

前言

创建一个主题的时候,有两个参数,一个是副本,一个是分区。

对于物理文件来说,总的文件数就是;两者的乘积。

1635996657738_F582B109-A462-46ac-A8B3-E56195638D95.png

分区

topicC-1 这个-1代表的就是分区

lQLPDhrWAjac6xDNAajNAmOwMVU699npklUBi9_jdABrAA_611_424.png

1635997981884_C09422A5-6593-42da-81E5-3B62CC764706.png

进入到该topic之后的文件结构

1635998009774_6EE5AAC7-D565-4e79-BA80-88ACC8D5E7F8.png

文件解释

  • .log文件就是所有的数据所在文件
  • .index是索引文件
  • .timeindex时间戳索引文件
  • leader-epoch-checkpoint 是进行lead的校验

文件是怎么写入的

  • 每一条消息写入的时候会有一个偏移量,魔术数,键的大小,键,值的大小,值。

  • 有些比较大的消息会进行包装

生产环境中的时候,大数据推荐使用压缩,只是会消耗一点点cpu的性能,减少网络开销

1636006655977_5FE1EA7E-F699-4e3e-9ADD-8F201C529C41.png

索引

索引消费 顺序消费 (1-100)

消费的时候:指定位置 比如:13 类似于数据库,要想快速定位到数据,就会使用到索引文件

超时数据的清理机制

超时数据的清理机制不等于文件文件过期

业务场景: kafka设计的时候 会有key(分区),压缩的处理

消费:电商场景,user表,

李老师 ID(13) NAME(如花技师)

王老师 ID(13) NAME(如梦技师)

同样的key值在kafka中会有多个值,那么就会把kafka中的数据中的超时数据进行清理,只保留最新的数据,类似于压缩的技术

小总结

kafka提供了上面的那么多的机制,kafka设计上其实想做成一个大数据系统,可以流处理的

1.消息顺序 同一个生产者必须往同一个分区写消息

消息B 应该在消息A之后写入 B的偏移量 一定大于A的偏移量

消息被写入所有副本的时候,应该同步(ack=all)

必须至少保证一个副本是活跃的

这种情况下就不要跟我谈性能,又要顺序又要性能,别闹

1636007788322_30571D71-6303-4cc4-AAC0-0127D9333CC4.png

不完全选举:就是数据没有完整复制一模一样的时候也可以选择leader,保证正常运行,

可以通过修改一个配置

server配置文件 unckear.leader.election=true 将这个改为false,必须等你挂了的那个(数据最全),不管你报错,报错了等待就行,再启动起来才行

银行,金融行业会采取这种方法(牺牲可用性,保证一致性) 等价于设置ack=all

一句话总结:宁愿宕机一个消息也不能丢失数据

同步副本

和leader中数据一摸一样的数据称为同步副本,最小同步副本。

配置mini.insync.replicas=2 最小同步副本,必须至少有两个同步副本可以存在的情况下,kafka才会写入数据

在一个可靠的系统里面包括可靠消费者和可靠生产者

可靠的生产者

我们一般设置ack=all或者-1(至少写入一次);

一些对应的参数要配置:重试机制配置,会有可能多写,需要一些额外的处理,关联一些数据库,或者redis或者其他手段;

代码:异常(这些需要重试的消息,写入一个特殊的主题),类似于一个死信消息;在代码里面写入一个一场主题,业务代码在处理就行

消费者可靠

提交偏移量(自动<时间间隔>,手动)

再均衡监听器(发生分区再均衡的时候,我们需要额外的机制,事务或者统一的存储,代码里面在写一些东西)

幂等:写入一次,而且有且仅只处理一次

零COPY,就是kafka的硬盘会映射到内存中,页面缓存,当你处理一些即发即收的消息的时候,一个程序发了消息进入内存,另一个程序直接读内存,感觉kafka"摆设"一般,因此处理消息还是比较快的。

增加和删除集群中的topic

1635921270816_10E29B51-560F-4eb4-8D64-968CCAB8A41F.png

kafka中数据的删除跟有没有消费者消费完全无关。数据的删除,只跟kafka broker上面上面的这两个配置有关:

log.retention.hours=48 #数据最多保存48小时

log.retention.bytes=1073741824 #数据最多1G

故障总结

kafka主题消费积压问题总结

kafka主题消费积压问题

故障描述:

某个主题的一个分区消费积压,由于这个主题非常重要,且已经有用户投诉所以运维很紧张,紧急打印堆栈并Dump堆内存后,就重启了这台机器。   故障分析1:

消费这个主题的集群的业务逻辑相对比较简单,主要就是读取某些主题,然后逻辑判断+DB操作后,分流写入到另外某些主题。运维通过kafka监控平台找到积压的主题,发现主题的某个分区积压了几万消息后,找到了消费这个分区的应用程序结点。

故障分析2:

通过对结点堆栈信息分析并未看出什么问题,又通过MAT(Eclipse Memory Analyzer)打开堆Dump文件,通过积压主题名称找到所有和此主题相关的线程,发现这些消费线程中有一个线程运行到(堵塞在?)mysql查询的网络返回阶段。通过这个线程上下文信息看到了SQL语句,这个SQL语句是查询某个表且两个查询字段也已经建立了索引,没有道理因为这个SQL语句引起堵塞!难道这个SQL语句是个慢查询?

注:

此问题已经通知运维找DBA,确认下在那个时间段有没有SQL慢查询,下周看情况再总结相关信息。

Kafka丢失数据问题优化总结

数据丢失是一件非常严重的事情事,针对数据丢失的问题我们需要有明确的思路来确定问题所在,针对这段时间的总结,我个人面对kafka 数据丢失问题的解决思路如下:

  • 是否真正的存在数据丢失问题,比如有很多时候可能是其他同事操作了测试环境,所以首先确保数据没有第三方干扰。
  • 理清你的业务流程,数据流向,数据到底是在什么地方丢失的数据,在kafka 之前的环节或者kafka之后的流程丢失?比如kafka的数据是由flume提供的,也许是flume丢失了数据,kafka 自然就没有这一部分数据。
  • 如何发现有数据丢失,又是如何验证的。从业务角度考虑,例如:教育行业,每年高考后数据量巨大,但是却反常的比高考前还少,或者源端数据量和目的端数据量不符
  • 定位数据是否在kafka之前就已经丢失还事消费端丢失数据的
  1. kafka支持数据的重新回放功能(换个消费group),清空目的端所有数据,重新消费。
  2. 如果是在消费端丢失数据,那么多次消费结果完全一模一样的几率很低。
  3. 如果是在写入端丢失数据,那么每次结果应该完全一样(在写入端没有问题的前提下)。
  • kafka环节丢失数据,常见的kafka环节丢失数据的原因有:
  1. 如果auto.commit.enable=true,当consumer fetch了一些数据但还没有完全处理掉的时候,刚好到commit interval出发了提交offset操作,接着consumer crash掉了。这时已经fetch的数据还没有处理完成但已经被commit掉,因此没有机会再次被处理,数据丢失。
  2. 网络负载很高或者磁盘很忙写入失败的情况下,没有自动重试重发消息。没有做限速处理,超出了网络带宽限速。kafka一定要配置上消息重试的机制,并且重试的时间间隔一定要长一些,默认1秒钟并不符合生产环境(网络中断时间有可能超过1秒)。
  3. 如果磁盘坏了,会丢失已经落盘的数据
  4. 单批数据的长度超过限制会丢失数据,报kafka.common.MessageSizeTooLargeException异常
    解决:
Consumer side:fetch.message.max.bytes- this will determine the largest size of a message that can be fetched by the consumer. 

Broker side:replica.fetch.max.bytes- this will allow for the replicas in the brokers to send messages within the cluster and make sure the messages are replicated correctly. 

If this is too small, then the message will never be replicated, and therefore, the consumer will never see the message because the message will never be committed (fully replicated).

Broker side:message.max.bytes- this is the largest size of the message that can be received by the broker from a producer. Broker side (per topic):max.message.bytes- this is the largest size of the message the broker will allow to be appended to the topic. 

This size is validated pre-compression. (Defaults to broker'smessage.max.bytes.)

  1. partition leader在未完成副本数follows的备份时就宕机的情况,即使选举出了新的leader但是已经push的数据因为未备份就丢失了!
    kafka是多副本的,当你配置了同步复制之后。多个副本的数据都在PageCache里面,出现多个副本同时挂掉的概率比1个副本挂掉的概率就很小了。(官方推荐是通过副本来保证数据的完整性的)

  2. kafka的数据一开始就是存储在PageCache上的,定期flush到磁盘上的,也就是说,不是每个消息都被存储在磁盘了,如果出现断电或者机器故障等,PageCache上的数据就丢失了。
    可以通过log.flush.interval.messages和log.flush.interval.ms来配置flush间隔,interval大丢的数据多些,小会影响性能但在0.8版本,可以通过replica机制保证数据不丢,代价就是需要更多资源,尤其是磁盘资源,kafka当前支持GZip和Snappy压缩,来缓解这个问题 是否使用replica取决于在可靠性和资源代价之间的balance

    同时kafka也提供了相关的配置参数,来让你在性能与可靠性之间权衡(一般默认):

当达到下面的消息数量时,会将数据flush到日志文件中。默认10000

log.flush.interval.messages=10000

当达到下面的时间(ms)时,执行一次强制的flush操作。interval.ms和interval.messages无论哪个达到,都会flush。默认3000ms

log.flush.interval.ms=1000

检查是否需要将日志flush的时间间隔

log.flush.scheduler.interval.ms = 3000

Kafka的优化建议

producer端:

  • 设计上保证数据的可靠安全性,依据分区数做好数据备份,设立副本数等。
    push数据的方式:同步异步推送数据:权衡安全性和速度性的要求,选择相应的同步推送还是异步推送方式,当发现数据有问题时,可以改为同步来查找问题。
  • flush是kafka的内部机制,kafka优先在内存中完成数据的交换,然后将数据持久化到磁盘.kafka首先会把数据缓存(缓存到内存中)起来再批量flush.
    可以通过log.flush.interval.messages和log.flush.interval.ms来配置flush间隔
  • 可以通过replica机制保证数据不丢.
    代价就是需要更多资源,尤其是磁盘资源,kafka当前支持GZip和Snappy压缩,来缓解这个问题
    是否使用replica(副本)取决于在可靠性和资源代价之间的balance(平衡)
  • broker到 Consumer kafka的consumer提供两种接口.
  1. high-level版本已经封装了对partition和offset的管理,默认是会定期自动commit offset,这样可能会丢数据的
  2. low-level版本自己管理spout线程和partition之间的对应关系和每个partition上的已消费的offset(定期写到zk)
    并且只有当这个offset被ack后,即成功处理后,才会被更新到zk,所以基本是可以保证数据不丢的即使spout线程crash(崩溃),重启后还是可以从zk中读到对应的offset
  • 异步要考虑到partition leader在未完成副本数follows的备份时就宕机的情况,即使选举出了新的leader但是已经push的数据因为未备份就丢失了!
  1. 不能让内存的缓冲池太满,如果满了内存溢出,也就是说数据写入过快,kafka的缓冲池数据落盘速度太慢,这时肯定会造成数据丢失。
  2. 尽量保证生产者端数据一直处于线程阻塞状态,这样一边写内存一边落盘。
  3. 异步写入的话还可以设置类似flume回滚类型的batch数,即按照累计的消息数量,累计的时间间隔,累计的数据大小设置batch大小。
  • 设置合适的方式,增大batch 大小来减小网络IO和磁盘IO的请求,这是对于kafka效率的思考。
  1. 不过异步写入丢失数据的情况还是难以控制
  2. 还是得稳定整体集群架构的运行,特别是zookeeper,当然正对异步数据丢失的情况尽量保证broker端的稳定运作吧

kafka不像hadoop更致力于处理大量级数据,kafka的消息队列更擅长于处理小数据。针对具体业务而言,若是源源不断的push大量的数据(eg:网络爬虫),可以考虑消息压缩。但是这也一定程度上对CPU造成了压力,还是得结合业务数据进行测试选择

  • 结合上游的producer架构,

broker端:

topic设置多分区,分区自适应所在机器,为了让各分区均匀分布在所在的broker中,分区数要大于broker数。分区是kafka进行并行读写的单位,是提升kafka速度的关键。

  1. broker能接收消息的最大字节数的设置一定要比消费端能消费的最大字节数要小,否则broker就会因为消费端无法使用这个消息而挂起。
  2. broker可赋值的消息的最大字节数设置一定要比能接受的最大字节数大,否则broker就会因为数据量的问题无法复制副本,导致数据丢失

comsumer端:

关闭自动更新offset,等到数据被处理后再手动跟新offset。
在消费前做验证前拿取的数据是否是接着上回消费的数据,不正确则return先行处理排错。
一般来说zookeeper只要稳定的情况下记录的offset是没有问题,除非是多个consumer group 同时消费一个分区的数据,其中一个先提交了,另一个就丢失了。


问题:
kafka的数据一开始就是存储在PageCache上的,定期flush到磁盘上的,也就是说,不是每个消息都被存储在磁盘了,如果出现断电或者机器故障等,PageCache上的数据就丢失了。

这个是总结出的到目前为止没有发生丢失数据的情况

//producer用于压缩数据的压缩类型。默认是无压缩。正确的选项值是none、gzip、snappy。压缩最好用于批量处理,批量处理消息越多,压缩性能越好
     props.put("compression.type", "gzip");
     //增加延迟
     props.put("linger.ms", "50");
     //这意味着leader需要等待所有备份都成功写入日志,这种策略会保证只要有一个备份存活就不会丢失数据。这是最强的保证。,
     props.put("acks", "all");
     //无限重试,直到你意识到出现了问题,设置大于0的值将使客户端重新发送任何数据,一旦这些数据发送失败。注意,这些重试与客户端接收到发送错误时的重试没有什么不同。允许重试将潜在的改变数据的顺序,如果这两个消息记录都是发送到同一个partition,则第一个消息失败第二个发送成功,则第二条消息会比第一条消息出现要早。
     props.put("retries ", MAX_VALUE);
     props.put("reconnect.backoff.ms ", 20000);
     props.put("retry.backoff.ms", 20000);
     
     //关闭unclean leader选举,即不允许非ISR中的副本被选举为leader,以避免数据丢失
     props.put("unclean.leader.election.enable", false);
     //关闭自动提交offset
     props.put("enable.auto.commit", false);
     限制客户端在单个连接上能够发送的未响应请求的个数。设置此值是1表示kafka broker在响应请求之前client不能再向同一个broker发送请求。注意:设置此参数是为了避免消息乱序
     props.put("max.in.flight.requests.per.connection", 1);

Kafka重复消费原因

强行kill线程,导致消费后的数据,offset没有提交,partition就断开连接。比如,通常会遇到消费的数据,处理很耗时,导致超过了Kafka的session timeout时间(0.10.x版本默认是30秒),那么就会re-blance重平衡,此时有一定几率offset没提交,会导致重平衡后重复消费。
如果在close之前调用了consumer.unsubscribe()则有可能部分offset没提交,下次重启会重复消费

kafka数据重复 kafka设计的时候是设计了(at-least once)至少一次的逻辑,这样就决定了数据可能是重复的,kafka采用基于时间的SLA(服务水平保证),消息保存一定时间(通常为7天)后会被删除
kafka的数据重复一般情况下应该在消费者端,这时log.cleanup.policy = delete使用定期删除机制

分类: kafka