冰解的破-KAFKA

冰解的破-KAFKA_第1张图片
kafka

Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。该项目的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。其持久化层本质上是一个“按照分布式事务日志架构的大规模发布/订阅消息队列”,[3]这使它作为企业级基础设施来处理流式数据非常有价值。此外,Kafka可以通过Kafka Connect连接到外部系统(用于数据输入/输出),并提供了Kafka Streams——一个Java流式处理库 (计算机)。

学习整理:

  • zookeeper在kafka中起到的作用?

先聊聊zookeeper的作用。

zookeeper在分布式集群的作用主要集中在:

  1. 数据发布与订阅(配置中心)
    发布与订阅模型,即所谓的配置中心,顾名思义就是讲发布者将数据发布到zk节点上,共订阅者动态获取数据,实现配置的集中式管理和动态更新。例如,全局的配置信息,服务服务框架的地址列表就非常适合使用。
  2. 负载均衡
    即软件负载均衡。最典型的是消息中间件的生产、消费者负载均衡。
  3. 命名服务(Naming Service)
    常见的是发布者将自己的地址列表写到zookeeper的节点上,然后订阅者可以从固定名称的节点获取地址列表,链接到发布者进行相关通讯。
  4. 分布式通知/协调
    这个利用的是zookeeper的watcher注册和异步通知机制,能够很好的实现分布式环境中不同系统间的通知与协调,实现对数据变更的实时处理。
  5. 集群管理与Master选举
    集群管理,比如在线率,节点上线下线通知这些。Master选举可以使用临时顺序节点来实现。
  6. 分布式锁
    分布式锁,这个主要得益于zookeeper数据的强一致性,利用的是临时节点。锁服务分为两类,一个是独占锁,另一个是控制时序。
  • 独占,是指所有的客户端都来获取这把锁,最终只能有一个获取到。用的是临时节点。
  • 控制时序,所有来获取锁的客户端,都会被安排得到锁,只不过要有个顺序。实际上是某个节点下的临时顺序子节点来实现的。
  1. 分布式队列
  • 一种是FIFO,这个就是使用临时顺序节点实现的,和分布式锁服务控制时序一样。
  • 第二种是等待队列的成员聚齐之后的才同意按序执行。实际上,是在队列的节点里首先创建一个/queue/num节点,并且赋值队列的大小。这样我们可以通过监控队列节点子节点的变动来感知队列是否已满或者条件已经满足执行的需要。这种,应用场景是有条件执行的任务,条件齐备了之后任务才能执行。

Kafka使用zookeeper作为其分布式协调框架,很好的将消息生产、消息存储、消息消费的过程结合在一起。同时借助zookeeper,kafka能够生产者、消费者和broker在内的所以组件在无状态的情况下,建立起生产者和消费者的订阅关系,并实现生产者与消费者的负载均衡。

以kafka 0.8.22 来看看其中zookeeper结构:

  1. kafka在zookeeper上的目录结构
val ConsumersPath = "/consumers"
val BrokerIdsPath = "/brokers/ids"
val BrokerTopicsPath = "/brokers/topics"
val TopicConfigPath = "/config/topics"
val TopicConfigChangesPath = "/config/changes"
val ControllerPath = "/controller"
val ControllerEpochPath = "/controller_epoch"
val ReassignPartitionsPath = "/admin/reassign_partitions"
val DeleteTopicsPath = "/admin/delete_topics"
val PreferredReplicaLeaderElectionPath = "/admin/preferred_replica_election"

如图:


冰解的破-KAFKA_第2张图片
kafka在zookeeper的存储结构
  1. kafka的zkclient

Kafka使用的是I0Itec.zkclient,https://github.com/sgroschupf/zkclient,github地址,与zk进行通信。

Zkclient对zookeeper的listener实现总共有四种:

  • IZkStateListener(监听会话状态,是否进行了超时重连等);
  • IZkDataListener(监听节点数据的变动);
  • IZkChildListener(监听子节点的变动);
  • IZkConnection (监听链接).

再赘述一下zookeeper的节点类型:

  • 持久节点(PERSISTENT);
  • 临时节点(EPHEMERAL);
  • 持久顺序节点(PERSISTENT_SEQUENTIAL);
  • 临时顺序节点(EPHEMERAL_SEQUENTIAL)。

分别说说这四种listener:

  1. IZkStateListener

主要作用是会话超时的监控,需要在处理函数里重新注册临时节点。主要方法两个:

  • handleStateChanged,zookeeper的链接状态改变的时候调用
  • handleNewSession,与zookeeper的会话超时,导致断开并新连接建立的时候会调用。需要在此方法中实现临时节点的注册。
    在kafka中主要有以下四个实现:
  1. ZKSessionExpireListener
    是Kafka.consumer.ZookeeperConsumerConnector的内部类。所属对象为每个消费者的对象:ZookeeperConsumerConnector会话超时需要重新注册的临时节点为consumer的zknode临时节点:consumerGroupDir + "/ids"+consumerIdString。会导致消费者进行再平衡:loadBalancerListener.syncedRebalance()。
  2. SessionExpirationListener
    是kafka.controller.KafkaController的内部类。所属对象是每个Broker的Controller对象。维护的临时节点为"/controller"。会话超时会导致Crontroller再选举。
  3. ZkSessionExpireListener
    是kafka.consumer.ZookeeperTopicEventWatcher内部类。所属对象也是给ZookeeperTopicEventWatcher对象。作用是,每次会话超时事件触发后都会重新将ZkTopicEventListener和"/brokers/topics"的目录进行绑定。ZkTopicEventListener负责会监控该目录下的子节点,也即topic的增删,最终调用WildcardStreamsHandler.handleTopicEvent。会在创建带topic过滤器的流的时候用到。createMessageStreamsByFilter具体请参考源码.
  4. SessionExpireListener
    Kafka.server.KafkaHealthcheck的内部类。监控的临时节点为"/brokers/ids"+brokerID。同时注册临时节点的时候会将advertisedHost:advertisedPort等信息写入该临时节点。是保障Broker存活在集群的关键。
  1. IZkDataListener

该类及其实现,主要作用是监控zk节点数据的变更,来实现配置在集群中的更新。
其子类要实现的方法有两个:

  • handleDataChange数据变动会调用该方法。
  • handleDataDeleted数据被删除会调用该方法。
  1. ZKTopicPartitionChangeListener
    是Kafka.consumer.ZookeeperConsumerConnector的内部类。 "/brokers/topics"+topic监控的是消费者消费topic的节点。实现的方法handleDataChange,当topic节点存储的数据也即是partition信息(包括新增分区,分区迁移,leader变动)更新的时候,会调用该方法,并触发消费者的再平衡。
  2. PartitionsReassignedListener
    类的路径为Kafka.consumer.PartitionsReassignedListener。KafkaController的成员变量,监听的目录为"/admin/reassign_partitions"。通过管理命令往该节点写分区重分配策略,会触发分区的重分配,完成分区的迁移等动作。
  3. ReassignedPartitionsIsrChangeListener
    类的路径为Kafka.consumer.ReassignedPartitionsIsrChangeListener。监控的目录是每个partition的子节点state,如/brokers/topics/YourTopicName/partitions/21/state。假如我们需要将某些优先副本选为leader的时候会触发。
  4. PreferredReplicaElectionListener
    类的路径为Kafka.consumer.PreferredReplicaElectionListener。也是kafkaController内部对象。监控的目录是"/admin/preferred_replica_election"。根据给节点指定的优先副本列表,进行leader的重新选举。
  5. AddPartitionsListener
    该类是kafka.controller.PartitionStateMachine内部类及成员变量。监控的目录是每个topic的具体目录:"/brokers/topics/topic",当新增分区的时候会触发该listener,然后做相关处理比如,让分区的leader上线等。
  6. LeaderChangeListener
    kafka.server.ZookeeperLeaderElector内部类及成员变量。Controller的选举的过程中用到了。监控的节点为/controller。
  1. IZkChildListener

这种listener,主要是负责监听一个节点子节点的变动。
只有一个要实现的方法:

  • handleChildChange(String parentPath, List currentChilds):

子节点变动的时候会调用该方法。我们可以在此实现自己的处理。

在kafka中主要有以下几个实现:

  1. BrokerChangeListener
    是ReplicaStateMachine内部类及成员变量,监控的目录是"/brokers/ids",当子节点有变动的时候会触发该listener,进而进行Broker故障处理和新Broker加入的处理。
  2. TopicChangeListener
    是PartitionStateMachine内部类及成员变量,监控的目录是"/brokers/topics",当子节点变动的时候,会触发该函数,进而进入topic增加之后的相关处理。
  3. DeleteTopicsListener
    是PartitionStateMachine内部类及内部对象,监控的目录是" /admin/delete_topics",当子节点变动的时候,会触发该函数,进而进入topic删除后相关处理操作。
  4. ZkTopicEventListener
    是kafka.consumer.ZookeeperTopicEventWatcher内部类及成员变量。也是在创建带topicfilter功能的消费流的时候用到,无论是删除topic还是增加topic都会重新初始化消费者。监控的目录是"/brokers/topics"。每个消费者维护一个。
  5. ConfigChangeListener
    TopicConfigManager的一个内部object和内部成员变量,监控的目录是"/config/changes"
    方便用户进行topic的配置动态管理。
  6. ZKRebalancerListener
    是Kafka.consumer.ZookeeperConsumerConnector的内部类。每个消费者维护一个该对象,监控的目录是 /consumers/groupid/ids,当有新的conusmer加入或者有消费者死掉,都会触发该listener,进而会触发消费者再平衡。
  1. IZkConnection

Kafka里面没有用到。

kafka用zookeeper实现的服务类型:

  1. 配置管理
    Topic的配置之所以能动态更新就是基于zookeeper做了一个动态全局配置管理。
  2. 负载均衡
    基于zookeeper的消费者,实现了该特性,动态的感知分区变动,将负载使用既定策略分不到消费者身上。
  3. 命名服务
    Broker将advertised.port和advertised.host.name,这两个配置发布到zookeeper上的zookeeper的节点上/brokers/ids/BrokerId(broker.id),这个是供生产者,消费者,其它Broker跟其建立连接用的。
  4. 分布式通知
    比如分区增加,topic变动,Broker上线下线等均是基于zookeeper来实现的分布式通知。
  5. 集群管理和master选举
  • 我们可以在通过命令行,对kafka集群上的topic partition分布,进行迁移管理,也可以对partition leader选举进行干预。
  • Master选举,要说有也是违反常规,常规的master选举,是基于临时顺序节点来实现的,序列号最小的作为master。而kafka的Controller的选举是基于临时节点来实现的,临时节点创建成功的成为Controller,更像一个独占锁服务。
  1. 分布式锁
    独占锁,用于Controller的选举。

最后来简化梳理下kafka在zookeeper实现:

Kafka通过Zookeeper管理集群配置,选举leader,以及在Consumer Group发生变化时进行rebalance。Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息.

  • broker在zookeeper中的注册

为了记录broker的注册信息,在zookeeper上,专门创建了属于kafka的一个节点,其路径为/brokers.

Kafka的每个broker启动时,都会到zookeeper中进行注册,告诉zookeeper其broker.id, 在整个集群中,broker.id应该全局唯一,并在zookeeper上创建其属于自己的节点,其节点路径为/brokers/ids/{broker.id}.

创建完节点后,kafka会将该broker的broker.name及端口号记录到改节点.

另外,改broker节点属性为临时节点,当broker会话失效时,zookeeper会删除该节点,这样,我们就可以很方便的监控到broker节点的变化,及时调整负载均衡等。

  • Topic在zookeeper中的注册

在kafka中,用户可以自定义多个topic,每个topic又可以划分为多个分区,一般情况下,每个分区存储在一个独立的broker上。所有这些topic与broker的对应关系都有zookeeper进行维护。

在zookeeper中,建立专门的节点来记录这些信息,其节点路径为/brokers/topics/{topic_name}

针对topic 的每一个分区与broker的对应关系,zookeeper通过节点 /brokers/topics/topic.name来记录

当broker启动时,会到对应topic节点下注册自己的broker.id到对应分区的isr列表中

同样的,当broker退出数,也会触发zookeeper更新其对应topic分区的isr列表,并决定是否需要做消费者的负载均衡。

  • consumer在zookeeper中的注册
  1. 注册新的消费者分组

当新的消费者组注册到zookeeper中时,zookeeper会创建专用的节点来保存相关信息,其节点路径为ls /consumers/{group_id},其节点下有三个子节点,分别为[ids, owners, offsets]。

  • ids节点:记录该消费组中当前正在消费的消费者;
  • owners节点:记录该消费组消费的topic信息;
  • offsets节点:记录每个topic的每个分区的offset
  1. 注册新的消费者

当新的消费者注册到kafka中时,会在/consumers/{group_id}/ids节点下创建临时子节点,并记录相关信息。

  1. 监听消费者分组中消费者的变化

每个消费者都要关注其所属消费者组中消费者数目的变化,即监听/consumers/{group_id}/ids下子节点的变化。一旦发现消费者新增或减少,就会触发消费者的负载均衡。

以上,知识来自网上文章的整理与结合。

参见:
zookeeper在kafka中的作用:https://blog.csdn.net/caiyefly/article/details/77938777
Kafka源码系列之源码分析zookeeper在kafka的作用:
https://blog.csdn.net/rlnLo2pNEfx9c/article/details/80491142

  • topic中partition与consumer group中consumer的关系?

每个Consumer 进程都会划归到一个逻辑的Consumer Group中,逻辑的订阅者是Consumer Group。

所以一条message可以被多个订阅topic的Consumer Group消费,也就好像是这条message被广播到每个Consumer Group一样。

而每个Consumer Group中,类似于一个Queue(JMS中的Queue)的概念差不多,即一条消息只会被Consumer Group中的一个Consumer消费。


冰解的破-KAFKA_第3张图片
consumer多于partition

冰解的破-KAFKA_第4张图片
consumer小于等于partition

冰解的破-KAFKA_第5张图片
多个消费组

另外,我想补充一些关于kafka中coordinator的一些知识。

为了更好实现rebalance,解决之前zookeeper作为管理中间件的羊群效应(Herd Effect)和脑裂问题(Split Brain),kafka在0.9版本中引入Coordinator,并在随后的版本不断改进。

其主要思想是:将全部的Consumer Group分成多个子集,每个Consumer Group子集在Broker端对应一个GroupCoordinator对其进行管理。通过Consumer向GroupCoordinator发起JoinGroup请求、或者Consumer的HeartBeat超时,促使GroupCoordinator感知Group成员变化,由GroupCoordinator在zk上添加Watcher,感知目标topic/partition的变化(在topic发生扩容时,会触发GroupCoordinator设置的Watcher)。基于上述变化,GroupCoordinator进行Rebalance操作。

coordinator组件分为三个部分,GroupCoordinator,WorkerCoordinator,ConsumerCoordinator

  • GroupCoordinator:broker端的,每个kafka server都有一个实例,管理部分的consumer group和它们的offset
  • WorkerCoordinator:broker端的,管理GroupCoordinator程序,主要管理workers的分配。
  • ConsumerCoordinator:consumer端的,管理ConsumerCoordinator程序。

其中对于每个consumerGroup,GroupCoordinator主要存放:

  1. 对每个存在的topic,可以有多个消费组group订阅同一个topic(对应消息系统中的广播)
  2. 对每个Consumer Group,元数据如下:
  • 订阅的topics列表
  • Consumer Group配置信息,包括session timeout等
  • 组中每个Consumer的元数据。包括主机名,consumer id
  • 每个正在消费的topic partition的当前offsets
  • Partition的ownership元数据,包括consumer消费的partitions映射关系

ConsumerCoordinator是KafkaConsumer的一个成员变量,所以每个消费者都要自己的ConsumerCoordinator,消费者的ConsumerCoordintor只是和服务端的GroupCoordinator通信的介质。

整个过程大致如下:

冰解的破-KAFKA_第6张图片
consumer时序图
  1. 当前消费者准备加入某Group或者GroupCoordinator发生故障转移时,消费者并不知道GroupCoordinator的网络位置,此时,消费者会向Kafka Broker集群中的任意一个Broker发送ConsuemrMetadataRequest请求,此请求中包含了其Consumer Group的GroupName,收到请求的Broker会返回ConsumerMetadataResponse响应,其中包含了管理此ConsumerGroup的GroupCoordinator的相关信息。
  2. 消费者根据ConsumerMetadataResponse中的GroupCoordinator信息,连接到GroupCoordinator并周期性的发送HeartBeatRequest。发送心跳包的作用主要是为了告诉GroupCoordinator“我还活着”,GroupCoordinator会认为长时间未发送心跳包的消费者已经挂掉,进而触发GroupCoordinator的Rebalance逻辑。
  3. 如果HeartbeatResponse中带有IllegalGeneration异常,说明GroupCoordinator发起了Rebalance操作,此时消费者进入Rebalance状态,发送JoinGroupRequest,等待GroupCoordinator完成Rebalance逻辑,下发新的partition信息。GroupCoordinator会根据收到的JoinGroupRequest信息,以及zk上的信息,完成rebalance逻辑,计算出各消费者应该负责哪些分区的消费。
  4. Join Group阶段

GroupCoordinator在收到了消费者发来的JoinGroupRequest请求后,会暂存消费者的信息,待在一定时间内收集到全部消费者信息后,则将根据从所有消费者中选出1位消费者称为Group Leader,还会选取使用的分区分配策略,这些信息都将打包在JoinGroupResponse中返回给消费者。

每个消费者都将收到JoinGroupResponse响应,但是只有Group Leader收到的JoinGroupResponse响应中包含了所有消费者信息。当消费者确定自己是Group Leader后,会根据消费者的信息以及选定的分区分配策略进行分区分配。

  1. Synchronize Group State阶段

每个消费者会发送SyncGroupRequest到GroupCoordinator,但是,只有Group Leader的SyncGroupRequest请求包含了分区的分配结果,GroupCoordinator根据Group Leader的分区分配结果,形成SyncGroupResponse,并返回给所有消费者。消费者在收到SyncGroupResponse后即可获知归属自己的分区信息。

  1. 消费者成功成为Group成员之一(获取到了Rebalance结果)后,会周期性的发送HeartBeatRequest。如果HeartBeatResponse包含IllegalGeneration异常,则进入第3步。如果HeartBeatRequest发送失败,收到NotCoordinatorForGroup异常,则周期性的执行第1步,直至成功。


    冰解的破-KAFKA_第7张图片
    消息结构

参见:
Kafka 0.10 Coordinator概述:https://www.cnblogs.com/byrhuangqiang/p/6384986.html
Kafka Consumer Rebalance的演进:
http://lday.me/2017/07/24/0011_kafka_consumer_rebalance_evolution
Kafka 0.9 Coordinator的负载均衡实现:http://alchimie.iteye.com/blog/2292877

  • consumer重复读的原因?若要要求exactly once的通信,怎样实现?

虽然问题是问consumer端重复的问题,但我觉得有必要更全面的从kafka各个模块说说这个问题。如我们所知,kafka作为消息传递系统,消息经由producer生产,推送到broker缓存,再由consumer拉取以获取消息。

  1. producer端(因为是直接推送到broker,所以不考虑消息会丢失的情况)

消息丢失:异步模式下,producer到broker之间的网络原因,数据未送达。

消息重复:同步模式下,producer向broker发送一条消息,这时由于网络故障(可能在发送过程中,也可能在broker返回ack过程中),导致无法知晓是否发送成功,producer会再次发送,造成消息重复。此外在分布式环境下,由于一旦producer实列崩溃后会有新的实列生成来代替崩溃的producer,当旧的实列后来恢复并与新的实列共存时,也会造成数据重复,称之为“僵尸实列”问题。

解决方案: 0.11.0之后的版本为每个单独的producer分配PID,为每条消息分配sequence num 这样producer端可数据可去重,进而实现producer端的exactly once(实质是at least once和幂等性操作的结合)

补充:producer 端数据发送分,其中同步模式(ack=1和ack=all)和异步模式(ack=0)。
设置ack=0 //不等待消息ack便认为成功;
ack=1 //只需要partition的leader确认ack即可认为成功;
ack=all 或 -1 //partition成功写入数量>= min.insync.replicas 认为成功 。

  1. broker端

消息丢失:当ack=0时,因为网络原因(producer和broker之间)或者缓冲区满造成消息丢失;当ack=1时,分区leader接受数据成功返回ack后宕机,而分区副本未同步,此时ISR为空,unclean.leader.election.enable=true,无法重选leader,引发log truncation(日志截取),造成数据丢失。

消息重复:因为不负责生产数据故不考虑数据重复。

解决方案:方法一,可以简单设置min.insync.replicas >= 2,unclean.leader.election.enable=false,producer端的acks=all;方法二,主要针对异步缓冲区满的情况,配置文件设置不限制阻塞超时时间,当缓冲区满时,让生产端一直阻塞。

  1. consumer端

消息丢失:假设consumer执行顺序为先保存offset再处理消息(At Most Once),由于offset存放在内存中,则offset更新后,消息没来的及处理便宕机,则会造成数据丢失。

消息重复:假设consumer执行顺序为先处理消息再保存offset(At Least Once语义),如果consumer在消息处理完后offset保存之前挂掉,则由于负载均衡,consumer group 中新的consumer会按照未更新的offset处继续任务,造成消息重复消费。

解决方案:第一种是可以选择两阶段提交(较为麻烦);第二种是自己维护offset,将它和消息的结果放在同一个地方,并利用事务保证一致性;第三种是at least once + 幂等处理的方式。

最后再看看kafka新特性为我们提供的kafka streaming exactly once事务机制实现。


冰解的破-KAFKA_第8张图片
完整事务过程
  1. producer向任意broker发送FindCoordinator请求找到Transaction Coordinator leader位置,如1.所示;
  2. 找到transaction coordinator之后,producer发送包含Transaction ID的InitPidRequest消息,transaction coordinator 返回增加epoch之后的PID(同时拒绝epoch小于当前值的producer实列,并恢复Producer commit或abort之前未完成的事务),若为第一次收到该请求,会持久化到Transaction Log中,如2.所示;
  3. 开启事务beginTransaction(),此时producer本地会记录开启了事务,transaction producer会在收到第一条消息后认为事务开启;
  4. Consume-Transform-Produce阶段

4.1 producer在给多个发送数据之前,先要向transaction coordinator 发送AddPartitionsToTxnRequest 消息,transaction coordinator 将持久化到Transaction Log中并设状态BEGIN,如4.1所示,若该为事务中第一个则transaction coordinator 会启动对该事务的计时器;
4.2 producer 通过ProduceRequest发送多条消息其中包括:应用数据、PID、epoch和Sequence Number,如4.2所示;
4.3 producer 先判断当前事务是否使用使用sendOffsetsToTransaction方法并传入了相同group id参数,若没有,则向transaction coordinator发送AddOffsetsToTxnRequests请求(其中封装了多组消息的发送和消费),transaction coordinator 会将其对应的所有存入transaction log 设置状态BEGIN,producer这时会阻塞直至完成,如4.3所示;
4.4 处理完AddOffsetsToTxnRequest之后,produce会发送TxnOffsetCommit消息到Consumer Coordinator,将与读相关的 Offset 持久化到其内部__consumer_offsets中(Consumer Coordinator会通过PID和对应的epoch来验证 producer请求),如4.4所示,注意__consumer_offsets在当前事务commit之前对外不可见;

  1. producer调用commitTransaction或abortTransaction结束当前事务,commitTransaction使写入的数据对下游可见,abortTransaction则会根据下游consumer的isolation.level(read_committed或read_uncommitted)来取舍数据,如5.所示;

5.1 此时transaction coordinator 发送EndTxnRequest请求到Transaction Coordinator,收到请求后,Transaction Coordinator 将PREPARE_COMMIT 或 PREPARE_ABORT消息写入Transaction Log中,如5.1所示。
5.2 transaction coordinator 发送 WriteTxnMarker请求以Transaction Marker 形式将commit或abort信息写入broker中用户数据日志和Consumer Coordinator中Off set log,如5.2所示,注意此时发送的WriteTxnMarkerRequest会由transaction coordinator传递到事务涉及的所有的leader,包括涉及到__consumer_offsets读相关的所有的leader;
5.3 transaction coordinator 写入COMPLETE_COMMIT或COMPLETE_ABORT 信息到 transaction log 中,如5.3所示。

总的来说对于producer,新的kafka通过pid和sequence number实现了写操作的幂等性,并结合at least once 实现了exactly once 语义;引入transaction coordinator 并使用 transaction marker 和 PID实现了事务的隔离;通过transaction id 解决了僵尸实列的问题。
而对于consumer,任然需要结合具体外部应用来实现exactly once。

如果我们将kafka分解成“流处理”部分和将数据发送到数据存储的连接器部分,kafka提供的streaming api 已经能很好地完成“流处理“部分的exactly once 实现。

最后补充一点:ransaction Coordinator维护 Transaction Log,该log存于一个内部的Topic内。由于Topic数据具有持久性,因此事务的状态也具有持久性。

参见:
Kafka消息投递语义-消息不丢失,不重复,不丢不重:https://3gods.com/bigdata/Kafka-Message-Delivery-Semantics.html
Exactly Once语义与事务机制原理:http://www.jasongj.com/kafka/transaction/
正好一次(Exactly-once)消息传递在Kafka中已经完全支持:http://www.jdon.com/48977
恰好一次发送和事务消息(译):
http://atbug.com/kafka-exactly-once-delivery-and-transactional-messaging/

TO BE CONTINUED ......

你可能感兴趣的:(冰解的破-KAFKA)