Kafka笔记

一、概念

本质上是一个分布式消息中间件,用于构建实时数据管道和处理流式数据,横向扩展、容错、快速

1、作用

在实时数据生产、消费过程中起缓冲作用

(1)提供流发布、订阅功能

(2)数据容错

(3)流产生时即处理

2、存储结构

topic:主题,区分不同业务类型的流式数据

record:单条数据由key、value、timestamp构成

partition:分区,parition内数据有序(按照消息写入顺序给每个消息赋予一个递增的offset),partition间数据无序

每个分区可以有多个副本,分散在不同的broker上。

  • leader副本:被KafkaController选举出来的,作为该分区的leader,producer只对leader副本发送消息,consumer只从leader副本获得消息
  • 其他follower副本:其他副本都作为follower副本,只从leader副本复制数据
  • isr列表:简单描述就是,"跟得上"leader的副本列表(包含leader),最开始是所有副本。这里的跟得上是指
    • replica.lag.time.max.ms:在0.9.0.0之前表示follower如果在此时间间隔内没有向leader发送fetch请求,则该follower就会被剔除isr列表,在0.9.0.0之后表示如果该follower在此时间间隔内一直没有追上过leader的所有消息,则该follower就会被剔除isr列表
    • replica.lag.max.messages(0.9.0.0版本中已被废除):follower如果落后leader的消息个数超过该值,则该follower就会被剔除isr列表
      • 废除的主要原因是:目前这个配置是个统一配置,不同的topic速率生产速率不太一样,没办法来指定一个具体的值来应用到所有的topic上。将来可以将这个配置下放到topic级别,关于这个问题,可以见这里的讨论Automate replica lag tuning
  • min.insync.replicas:最少需要存活的副本数,默认是1。当acks=-1的时候,leader在处理新消息前,会先判断当前isr列表的的size是否小于这个值,如果小于的话,则不允许写入,返回NotEnoughReplicasException异常。同时,一旦允许写入了之后,在响应producer之前也会判断当前isr列表的size是否小于该值,如果小于返回NotEnoughReplicasAfterAppendException异常

(1)每个partition对应一个目录,目录名:topic名-有序序号(序号从0开始,若有3个partition,则依次是0、1、2)

每个partiton目录内有多个segment段文件,文件生命周期由配置决定

第一个segment的offset是0,后续每个segment文件名是前一个segment最后一条消息的offset值。20位数字,用0左填充

索引文件存储大量元数据,数据文件存储大量消息。索引文件中元数据指向对应数据文件中消息的物理偏移地址

下图 3,497表示此segment第3条消息在497偏移地址

(2)速度快的原因

  • 磁盘顺序存取,性能很高,规避了磁盘随机读取效率低的问题,发挥了磁盘容量大的优势。因为每个partition可以看做文件,追加写入文件是顺序的
  • MMAP内存映射技术,把文件映射到内存,提升读写效率,可配置producer.type是否在写入内存映射区后等待flush
  • 零拷贝,所有的数据读写都在内核态通过DMA完成,不需要等待cpu,也不需要把数据在内核态和用户态间来回拷贝
  • 数据批量处理,即使用户只需要一条,也把那条数据所在文件发过去,而且发送文件也方便进行压缩,能产生很高的吞吐量

二、架构

1、producer:生产者

request.required.acks

  • acks=0:表示producer不需要leader发送响应,即producer只管发不管发送成功与否。延迟低,容易丢失数据。
  • acks=1:表示leader写入成功(但是并没有刷新到磁盘)后即向producer响应。延迟中等,一旦leader副本挂了,就会丢失数据。
  • acks=-1:表示leader会等待isr列表中所有副本都写入成功才向producer发送响应。延迟高、可靠性高。但是也会丢数据

leader副本的属性

  • highWatermarkMetadata:代表已经被isr列表复制的最大offset,consumer只能消费该offset之前的数据
  • logEndOffsetMetadata:代表leader副本上已经复制的最大offset
  • 其他副本的记录,保存着他们的如下属性:
    • lastCaughtUpTimeMs:记录该follower副本上一次追上leader副本的所有消息的时间
    • logEndOffsetMetadata:代表该follower副本已经复制的最大offset

follower副本的属性

  • highWatermarkMetadata:follower会获取到leader的highWatermarkMetadata更新到自己的该属性中
  • logEndOffsetMetadata:代表follower副本上已经复制的最大offset
  • 其中follower会不断的向leader发送fetch请求,如果没有数据fetch则被leader阻塞一段时间,等待新数据的来临,一旦来临则解除阻塞,复制数据给follower。

2、consumer:消费者

一个partition只能同时被不同group内的某个consumer消费

即当有N个consumer group,一个partition可以被N个consumer消费(但这N个分别在不同的group里,注意不是同时,从而不会出现重复消费的情况)

0.10开始偏移量直接从kakfa的__consumer_offsets取,不需要从zookeeper取

3、Consumer Group:消费组

一个topic可对应多个CG,topic可以提供相同消息给多个CG,但在一个CG内,每个partition只能发送消息给一个consumer

实现广播(topic消息发送给所有consumer):每个consumer单独一个CG

实现单播(topic消息发送给任一个consumer):所有的consumer在同一个CG

4、broker

所有broker会通过ZooKeeper选举出一个作为KafkaController,来负责:

  • 监控所有broker的存活,以及向他们发送相关的执行命令。
  • 分区的状态维护:负责分区的新增、下线等,分区副本的leader选举
  • 副本的状态维护:负责副本的新增、下线等

5、leader副本选举

若某个broker挂了,leader副本在该节点上的分区就要进行重新选举。KafkaController会监听zk的/brokers/ids节点路径,一旦发现有broker挂了,执行如下逻辑(当KafkaController挂了时,各个broker会先重新选出新的KafkaController再进行leader副本选举)

(1)leader副本在该broker上的分区都要重新进行leader选举

  • 优先从isr列表中选出第一个作为leader副本;如果isr列表为空,则查看该topic的unclean.leader.election.enable,若为true则允许非isr列表的副本作为leader,也意味着数据可能丢失,若为false则直接抛出NoReplicaOnlineException,leader副本选举失败

(2)一旦选举成功,则将选举后的leader和isr和其他副本信息写入到该分区的对应的zk路径上

(3)KafkaController向上述相关的broker上发送LeaderAndIsr请求,将新分配的leader、isr、全部副本等信息传给他们。同时将向所有的broker发送UpdateMetadata请求,更新每个broker的缓存的metadata数据

(4)如果是leader副本,更新该分区的leader、isr、所有副本等信息。如果自己之前就是leader,则现在什么操作都不用做。如果之前不是leader,则需将自己保存的所有follower副本的LE设置为UnknownOffsetMetadata,之后等待follower的fetch,就会进行更新

(5)如果是follower副本,更新该分区的leader、isr、所有副本等信息,然后将日志截断到自己保存的HW位置,即日志的LE等于了HW(若HW此时未及时更新即HW小于LE,而截断后LE等于HW即变小,相当于没同步成功,需要重新从leader同步,若此时新leader也挂了数据自然就丢失了)。最后创建新的fetch请求线程,向新leader不断发送fetch请求,初次fetch的offset是LE

6、消息完整性

即使当acks=-1,unclean.leader.election.enable=false也会出现数据丢失的情况

原因:一种比较简单的情况是isr列表为空导致leader副本选举失败,数据自然就丢失了;另一种情况是即使选举成功,发生极端情况时也会丢失数据,由于follower的highWatermarkMetadata相对于leader的highWatermarkMetadata是延迟更新的,当leader选举完成后,所有follower副本的LogEndOffsetMetadata都截断到自己的highWatermarkMetadata位置,则可能截断了已被老leader提交了的日志,这样的话,这部分日志仅仅存在新的leader副本中,在其他副本中消失了,一旦leader副本挂了,这部分日志就彻底丢失了

7、消息顺序性

(1)需满足如下条件:

  • 生产者按照消息的顺序进行发送

不能使用多线程方式发送,为了效率,通常采用单线程异步发送的方式,此时还需要配置:max.in.flight.requests.per.connection=1(producer发现一旦还有未确认发送成功的消息,后面的消息不允许发送),即生产者异步把消息放到队列,但队列得确认前面消息发送成功后再发送后面的

  • 存储端
    • 相同key的消息能hash到相同的分区。kafka原生支持,但要设定合适的key(适用于只要求局部有序的场景,如某用户最后一笔订单)
    • 一个topic只有一个partition,且只能同步复制(适用于全局有序)
  • 消费者按照消息的顺序进行消费

只能使用单线程来保证

(2)实际使用

实现严格的全局有序性,会极大影响效率

所以要从业务角度分析:

  • 对于顺序不敏感场景,则不保证顺序
  • 对于只要求局部有序场景,只要将相同key的数据发送到同一个partition即可
  • 对于要求全局有序场景,只能满足上述要求,单线程异步生产、单partition同步复制、单线程消费

三、常用命令

1、启动

kafka-server-start.sh -daemon config/server.properties

2、创建topic

kafka-topics.sh --create --zookeeper spark:2181 --replication-factor 3 --partitions 3 --topic test

3、查看topic

kafka-topics.sh --describe --zookeeper spark:2181 --topic test

Isr 存活的broker id

Leader 主副本所在的broker id

4、列出所有的topic

kafka-topics.sh --list --zookeeper spark:2181

5、模拟消息生产者

kafka-console-producer.sh --broker-list spark:9092 --topic test

6、模拟消息消费者

--低版本

kafka-console-consumer.sh --zookeeper spark:2181 --topic test --from-beginning

--高版本直接从kafka取偏移量,不用经过zk

kafka-console-consumer.sh --bootstrap-server spark:9092 --topic test --from-beginning

|---------------------------|---------|--------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
| 参数 | 值类型 | 说明 | 有效值 |
| --topic | string | 被消费的topic | |
| --whitelist | string | 正则表达式,指定要包含以供使用的主题的白名单 | |
| --partition | integer | 指定分区 除非指定'--offset',否则从分区结束(latest)开始消费 | |
| --offset | string | 执行消费的起始offset位置 默认值:latest | latest earliest |
| --consumer-property | string | 将用户定义的属性以key=value的形式传递给使用者 | |
| --consumer.config | string | 消费者配置属性文件 请注意,[consumer-property]优先于此配置 | |
| --formatter | string | 用于格式化kafka消息以供显示的类的名称 默认值:kafka.tools.DefaultMessageFormatter | kafka.tools.DefaultMessageFormatter kafka.tools.LoggingMessageFormatter kafka.tools.NoOpMessageFormatter kafka.tools.ChecksumMessageFormatter |
| --property | string | 初始化消息格式化程序的属性 | print.timestamp=true|false print.key=true|false print.value=true|false key.separator= line.separator= key.deserializer= value.deserializer= |
| --from-beginning | | 从存在的最早消息开始,而不是从最新消息开始 | |
| --max-messages | integer | 消费的最大数据量,若不指定,则持续消费下去 | |
| --timeout-ms | integer | 在指定时间间隔内没有消息可用时退出 | |
| --skip-message-on-error | | 如果处理消息时出错,请跳过它而不是暂停 | |
| --bootstrap-server | string | 必需 (除非使用旧版本的消费者),要连接的服务器 | |
| --key-deserializer | string | | |
| --value-deserializer | string | | |
| --enable-systest-events | | 除记录消费的消息外,还记录消费者的生命周期 (用于系统测试) | |
| --isolation-level | string | 设置为read_committed以过滤掉未提交的事务性消息 设置为read_uncommitted以读取所有消息 默认值:read_uncommitted | |
| --group | string | 指定消费者所属组的ID | |
| --blacklist | string | 要从消费中排除的主题黑名单 | |
| --csv-reporter-enabled | | 如果设置,将启用csv metrics报告器 | |
| --delete-consumer-offsets | | 如果指定,则启动时删除zookeeper中的消费者信息 | |
| --metrics-dir | string | 输出csv度量值 需与[csv-reporter-enable]配合使用 | |
| --zookeeper | string | 必需 (仅当使用旧的使用者时)连接zookeeper的字符串。 可以给出多个URL以允许故障转移 | |
| --message.timestamp.type | | 定义消息中的时间戳是消息创建时间还是日志附加时间。默认是"CreateTime"或"LogAppendTime" | CreateTime LogAppendTime |

7、关闭

kafka-server-stop.sh config/server.properties

8、topic设定partition数

kafka-topics.sh --alter --topic test --zookeeper spark:2181 --partitions 3

9、查看offset

kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list spark:9092 --topic test

kafka-run-class kafka.tools.ConsumerOffsetChecker --zookeeper hostname:2181 --group group123 --topic test

10、删除topic

kafka-topics.sh --zookeeper my:2181 --delete --topic test

11、修改topic

kafka-topics --zookeeper my:2181 --alter --topic test --partitions 4

12、查看消费组

kafka-consumer-groups --bootstrap-server hostname:9092 --list

kafka-consumer-groups --bootstrap-server hostname:9092 --describe --group test-group1

四、API使用

1、生产者

复制代码
Properties properties = new Properties();

properties.put("metadata.broker.list","spark:9092");

properties.put("serializer.class","kafka.serializer.StringEncoder");

properties.put("request.required.acks","1");

Producer producer = new Producer(new ProducerConfig(properties));

producer.send(new KeyedMessage(topic, message));

2、消费者

复制代码
Properties properties = new Properties();

properties.put("zookeeper.connect","spark:2181");

properties.put("group.id","test_group");

ConsumerConnector consumer = Consumer.createJavaConsumerConnector(new ConsumerConfig(properties));

Map topicCountMap = new HashMap<>();

topicCountMap.put(topic, 1);

//String: topic名, List:数据流

Map> > messageStream = consumer.createMessageStreams(topicCountMap);

KafkaStream stream = messageStream.get(topic).get(0); //获取收到的topic数据流

ConsumerIterator iterator = stream.iterator();

while(iterator.hasNext()){

String message = new String(iterator.next().message());

System.out.println("recv: "+message);

}
相关推荐
kkkkkkkkk_12013 小时前
【强化学习】05周博磊强化学习纲要学习笔记——第三课上
笔记·学习·强化学习
淳杰3 小时前
【Androidstudio】学习/采坑笔记-冷重启和热重启(reboot)
笔记·学习
思成不止于此4 小时前
MySQL 数据操作:增删改核心语法全解析
数据库·笔记·学习·mysql
回家路上绕了弯4 小时前
Vavr 工具实用指南:Java 函数式编程的高效落地方案
分布式·后端
RaLi和夕4 小时前
硬件电路设计学习笔记1.三极管开关电路设计
笔记·嵌入式硬件·学习
小龙5 小时前
【理论知识】主流测井技术优缺点对比笔记
笔记·石油勘探·测井技术·测井
QT 小鲜肉5 小时前
【孙子兵法之下篇】010. 孙子兵法·地形篇深度解析与现代应用
人工智能·笔记·读书·孙子兵法
QT 小鲜肉5 小时前
【孙子兵法之下篇】010. 孙子兵法·地形篇
人工智能·笔记·读书·孙子兵法
阿恩.7706 小时前
化学前沿:科技革新与跨学科应用
人工智能·经验分享·笔记·科技·计算机网络·数学建模