kafka传递消息的三种方式
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kafka传递消息的三种方式相关的知识,希望对你有一定的参考价值。
参考技术A 1、发后即忘(fire-and-forget)只管往kafka发送消息而并不关心消息是否正确到达。正常情况没什么问题,不过有些时候(比如不可重试异常)会造成消息的丢失。这种发送方式性能最高,可靠性最差。
2、同步发送(sync)
其实kafkaTemplate.send方法并不是返回void,而是ListenableFuture<SendResult<K, V>>,该类继承了jdk concurrent包的Future。
3、异步发送(async)
在send()方法里指定一个Callback的回调函数,Kafka在返回响应时调用该函数来实现异步的发送确认。有读者或许会有疑问,send()方法的返回值类型就是Future,而Future本身就可以用作异步的逻辑处理。这样做不是不行,只不过Future里的 get()方法在何时调用,以及怎么调用都是需要面对的问题,消息不停地发送,那么诸多消息对应的Future对象的处理难免会引起代码处理逻辑的混乱。使用Callback的方式非常简洁明了,Kafka有响应时就会回调,要么发送成功,要么抛出异常。
Kafka的三种客户端线程模型和一个小惊喜
Kafka 作为一个流式数据平台,对开发者提供了三种客户端:生产者 / 消费者、连接器、流处理。本文着重分析这三种客户端的线程模型。看到最后的通常都有惊喜。
消费者的线程模型
0.8 版本以前的消费者客户端会创建一个基于 ZK 的消费者连接器,一个消费者客户端是一个 Java 进程,消费者可以订阅多个主题,每个主题也可以多个线程。为了让消息在多个节点被分布式地消费,提高消息处理的吞吐量,Kafka 允许多个消费者订阅同一个主题,这些消费者需要满足“一个分区只能被一个消费者中的一个线程处理”的限制条件。通常,我们会将同一份相同业务处理逻辑的应用程序部署在不同机器上,并且指定一个消费组编号。当不同机器上的消费者进程启动后,所有这些消费者进程就组成了一个逻辑意义上的消费组。
消费组中的消费者数量是动态变化的,当有新消费者加入消费组,或者旧消费者离开消费组,都会触发基于 ZK 的消费组“再平衡”操作。当“再平衡”操作发生时,每个消费者都会在客户端执行分区分配算法,然后从全局的分配结果中获取属于自己的分区。它的缺点是消费者会和 ZK 产生频繁的交互,造成 ZK 集群的压力过大,并且容易产生羊群效应和脑裂等问题。
在 0.8 版本以后,Kafka 重新设计了客户端,并且引入了“协调者”和“消费组管理协议”。新的消费者将“消费组管理协议”和“分区分配策略”进行了分离。协调者负责消费组的管理,而分区分配则会在消费组的一个主消费者中完成。采用这种方式,每个消费者都需要发送下面两种请求给协调者。
加入组请求:协调者收集消费组的所有消费者,并选举一个主消费者执行分区分配工作。
同步组请求:主消费者完成分区分配,由协调者将分区的分配结果传播给每个消费者。
新版本的消费者客户端引入了一个客户端协调者的抽象类,它的实现除了消费者的协调者,还有一个连接器的实现。
连接器的线程模型
Kafka 连接器的出现标准化了 Kafka 与各种外部存储系统的数据同步。用户开发和使用连接器就变得非常简单,只需要在配置文件中定义连接器,就可以将外部系统的数据导入 Kafka 或将 Kafka 数据导出到外部系统。如图 1 所示,中间部分都是 Kafka 连接器的内部组件,包括源连接器(Source Connector)和目标连接器(Sink Connector)。
图 1 Kafka 连接器的源连接器与目标连接器
Kafka 连接器的单机模式会在一个进程内启动一个 Worker 以及所有的连接器和任务。分布式模式的每个进程都有一个 Worker,而连接器和任务则分别运行在各个节点上。图 2 列举了连接器和任务在不同 Worker 上的四种分布方式:
一个 Worker,一个源任务、一个目标任务
一个 Worker,两个源任务、两个目标任务
两个 Worker,两个源任务、两个目标任务
三个 Worker,两个源任务、两个目标任务
图 2 分布式模式的 Kafka 连接器集群
分布式模式下,不同 Worker 进程之间的协调工作类似于消费者的协调。消费者通过协调者获取分配的分区,Worker 也会通过协调者获取分配的连接器与任务。如图 3 所示,消费者客户端和 Worker 客户端为了加入到组管理中,分别通过客户端的协调者对象来和服务端的消费组协调(GroupCoordinator)通信。
图 3 消费者和 Worker 的工作都是通过协调者分配的
流处理的线程模型
Kafka 流处理的工作流程简单来看分成三个步骤:消费者读取输入分区的数据、流式地处理每条数据、生产者将处理结果写入输出分区,这里面步骤 1 也充分利用了“消费组管理协议”。Kafka 流处理的输入数据源基于具有分布式分区模型的 Kafka 主题,它的线程模型主要由下面三个类组成:
流实例(KafkaStreams):通常一个节点(一台机器)只运行一个流实例。
流线程(StreamThread):一个流实例可以配置多个流线程。
流任务(StreamTask):一个流线程可以运行多个流任务,根据输入主题的分区数确定任务数。
如图 4 所示,输入主题有六个分区,Kafka 流处理总共就会产生六个流任务。流实例可以动态扩展,流线程的个数也可以动态配置。图中一共有三个流线程,则每个流线程会有两个流任务,每个流任务都对应输入主题的一个分区。
图 4 Kafka 流处理的线程模型
Kafka 的流处理框架使用并行的线程模型处理输入主题的数据集,这种设计思路和 Kafka 的消费者线程模型非常类似。消费者分配到订阅主题的不同分区,流处理框架的流任务也分配到输入主题的不同分区。如图 5 所示,输入主题 1 的分区 P1 和输入主题 2 的分区 P1 分配给流线程 1 的流任务,输入主题 1 的分区 P2 和输入主题 2 的分区 P2 分配给流线程 2 的流任务。流处理相比消费者,还会将拓扑的计算结果写到输出主题。
图 5 消费者模型与流处理的线程模型
消费者和流处理的故障容错机制也是类似的。如图 6 所示,假设消费者 2 进程挂掉,它所持有的分区会被分配给同一个消费组中的消费者 1,这样消费者 1 会分配到订阅主题的所有分区。对于流处理而言,如果流线程 2 挂掉了,流线程 2 中的流任务会分配给流线程 1。即流线程 1 会运行两个流任务,每个流任务分配的分区仍然保持不变。
图 6 消费者与流处理的故障容错机制
小 结
Kafka 客户端抽象出来的的“组管理协议”充分运用在消费者、连接器、流处理三个使用场景中。客户端中的消费者、连接器中的工作者、流处理中的流进程都可以看做“组”的一个成员。当增加或减少组成员时,在这个协议的约束下,每个组成员都可以获取到最新的任务,从而做到无缝的任务迁移。一旦理解了“组管理协议”,对于理解 Kafka 的架构设计是很有帮助的。
以上是关于kafka传递消息的三种方式的主要内容,如果未能解决你的问题,请参考以下文章