ITPub博客

首页 > 数据库 > 数据库开发技术 > Kafka学习笔记(八)

Kafka学习笔记(八)

原创 数据库开发技术 作者:DBA修行之路 时间:2021-08-10 23:17:52 0 删除 编辑




点击上方“蓝字”,发现更多精彩。





Kafka学习笔记(八)

承前启后

前两篇中,学习了consumer端的consumer构建,topic订阅,消息轮询,位移管理,重平衡等知识点部分的学习。剩下的还有解序列化,多线程,独立consumer,新老consumer对比部分,接下来让我们开始吧。


解序列化

Consumer端的解序列化是与producer端的序列化相对应的,consumer端从broker端获取的消息格式是字节数组,需要通过解序列化器将其转换为指定的对象类型。

kafka提供了以下deserializer:

ByteArrayDeserializer:无须操作,因为已经是字节数组。

ByteBufferDeserializer:将消息解序列化成ByteBuffer。

BytesDeserializer:将消息解序列化为kafka自定义的Bytes类。

DoubleDeserializer:解序列化Double类型。

IntegerDeserializer:解序列化Integer类型。

LongDeserializer:解序列化Long类型。

StringDeserializer:解序列化String类型。


Consumer端通过设置key.deserializer和value.deserializer参数来指定解序列化类型。如果涉及复杂的类型,用户可以自定义deserializer。


自定义deserializer整体上需要以下三步:

1).自定义或复用serializer的数据对象格式。

2).创建自定义deserializer类,实现org.apache.kafka.common.serialization.Deserializer接口,在deserializer方法中实现deserialize逻辑。

3).在构造KafkaConsumer的Properties对象中设置key.deserializer和value.deserializer的值。




多线程

kafkaProducer是线程安全的,用户可以在多个线程中使用同一个KafkaProducer实例,但是Consumer是非线程安全的,如果consumer想实现多线程消费,有以下两种方法:

1).多KafkaConsumer线程:每个线程维护一个KafkaConsumer,用户创建多个线程消费topic数据,每个线程创建自己的KafkaConsumer实例。每个线程负责消费固定数目的分区。实际中一般包含三个相关类:

ConsumerRunnable类:消费线程类,执行真正的消费任务。

ConsumerGroup类:消费线程管理类,创建多个线程类执行消费任务。

ConsumerMain类:主方法类。


2).单KafkaConsumer实例+多worker线程:实现思想为将消息的获取与消息的处理过程分开处理,实现解耦,全局维护一个或若干个consumer实例执行消息获取任务,然后将消息处理交由单独的worker线程进行处理,worker线程完成处理之后上报位移状态,由全局consumer提交位移。实际中一般也包含以下三个相关类:

ConsumerThreadHandler类:consumer多线程管理类,用于创建线程池及为每个线程分配消息集合,同时还用于consumer位移提交。

ConsumerWorker类:执行真正的消费逻辑并将消费的位移状态上报给ConsumerThreadHandler。

ConsumerMain类:主方法类。


这两种方式各自有自己的优缺点,具体如下:

多KafkaConsumer线程:实现简单,速度较快,没有线程间交互开销,便于位移管理,方便维护分区键的消息消费顺序。但是Socket连接开销大,consumer数量受限制于topic分区数,扩展性差,broker负载高,容易导致rebalance。

单KafkaConsumer实例+多worker线程:消息获取和消息处理过程分离,便于扩展consumer和worker数。但是难以维护分区内的消息顺序,处理链路较长,位移管理复杂,如果worker线程异常还可能导致消费数据丢失。




独立consumer

对于由多个consumer共同读取某个topic的场景来说,使用group是非常好的选择,但是对于需要精准控制消费的需求场景,则consumer group便失效了,此时需要使用独立consumer,即standalone consumer,standalone consumer之间是彼此独立的,任意一个consumer发生的变化对其他consumer不会造成任何影响。

使用standalone consumer是通过调用KafkaConsumer.assign方法实现的,与consumer group中的subscribe方法类似,当发生多次assign调用时,之前的会被覆盖,只有最后一个assign的调用会生效,另外,在同一个consumer应用中不要混合使用standalone consumer和consumer group。





新旧版本

在kafka中,新旧版本存在以下差别:

1).旧版本consumer是是使用scala编写,新版本是使用Java编写。新版本使用的API包名为org.apache.kafka.clients.consumer.*,主要使用类为KafkaConsumer。而旧版本使用的API包名为kafka.consumer.*,主要使用的类是ZookeeperConsumerConnector,SimpleConsumer。

2).旧版本consumer group的组成员管理,错误检测等功能,尤其是位移的提交依赖于ZooKeeper完成,依赖Zookeeper下的znode节点写入位移信息完成位移提交动作,而新版本则是kafka自身维护了一个内部topic用来代替Zookeeper完成上述功能。

3).在旧版本中,与新版本中的consumer group和standalone consumer相对应的是high-level consumer和low-level consumer。其中,high-level consumer使用的是consumer group,low-level consumer使用的是standalone consumer。

high-level。

4).新版本中,Consumer使用单线程轮询的方式来消费消息,而在旧版本中,Consumer使用的是多线程方式,用户可以指定多个线程来消费订阅的topic,consumer内部会自动创建多个线程进行消费,而不是新版本中需要手动创建多个线程。


另外,如果用户逻辑简单,不关心位移提交,只是单纯想消费数据,则优先建议使用high-level consumer+自动提交位移方法,反之,使用low-level consumer方式,该方式优点为:

可以细粒度控制分区消费,适用于以下场景:

消息需要重复读取。

只需消费部分分区的数据。

实现"精确一次"处理语义。

缺点是:

用户需要自己处理为已提交。

用户需要寻找分区的leader broker。

用户需要自己处理leader变更。



自问自答

Q:
旧版本Kafka怎么判断consumer存活状态?

旧版本Kafka中consumer group依赖Zookeeper完成组成员管理,错误检测等功能,Consumer会在/consumers/groupId/ids/Consumer id目录下生成一个临时节点,即ephemeral node,如果Kafka和ZooKeeper的会话过期,该节点会自动被删除,可以通过这个来监听consumer的存活状态,一旦该节点失效,可以认为该consumer已经异常,会开启新一轮rebalance。

Q:
如何实现”精确一次"处理语义?

实现"精确一次"的核心思想是:将数据处理和位移提交放入一个事务中,在事务中,可以实现任何一个操作都可以回滚整个操作,从而实现精确一次的消息消费。

Q:
线程数不大于订阅的分区数?

通常在kafka中,consumer设置线程数的个数不大于订阅的分区总数,一方面,因为如果创建超过分区数的线程会有线程无法消费任何分区。另一方面,创建多余的线程会造成额外的资源浪费。


结尾小结

本篇中,继前两篇后总结了consumer的解序列化,多线程消费,独立consumer,及新老consumer对比这几个方面的知识点,到此consumer的基本学习告一段落。




END



扫码关注我
每天进步一点点,未来才可期,我们共同的目标是星辰大海。
赞一下你最美!



原文作者:DBA修行之路;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-05-25

  • 博文量
    64
  • 访问量
    23946