Java 如何选择Kafka transaction.id

Java 如何选择Kafka transaction.id,java,apache-kafka,Java,Apache Kafka,我想知道我是否可以在理解卡夫卡中的事务,特别是如何使用transaction.id方面得到一些帮助。以下是上下文: 我的Kafka应用程序遵循以下模式:从输入主题、处理、发布到输出主题使用消息 我使用的不是Kafka Streams API 我在一个消费者组中有多个消费者,每个消费者都在自己的轮询线程中 有一个线程池,其中包含执行消息处理和发布到输出主题的工作线程。目前,每个线程都有自己的生产者实例 我使用PublishedTransactionsAPI来确保消费偏移量的更新和输出主题的发布以原

我想知道我是否可以在理解卡夫卡中的事务,特别是如何使用transaction.id方面得到一些帮助。以下是上下文:

  • 我的Kafka应用程序遵循以下模式:从输入主题、处理、发布到输出主题使用消息
  • 我使用的不是Kafka Streams API
  • 我在一个消费者组中有多个消费者,每个消费者都在自己的轮询线程中
  • 有一个线程池,其中包含执行消息处理和发布到输出主题的工作线程。目前,每个线程都有自己的生产者实例
  • 我使用PublishedTransactionsAPI来确保消费偏移量的更新和输出主题的发布以原子方式进行
  • 迄今为止,我的假设包括:

  • 若我的进程在事务中间崩溃,那个么该事务中的任何内容都不会发布,也不会移动任何消耗补偿。因此,在重新启动时,我只需从原始消费偏移量再次启动事务
  • 对于producer transaction.id,重要的是它是唯一的。因此,我可以在启动时生成一个基于时间戳的id
  • 然后我读了下面的博客:。特别是在“如何选择事务id”一节中,这似乎意味着我需要保证每个输入分区都有一个生产者实例。它说“正确隔离僵尸的关键是确保读进程-写周期中的输入主题和分区对于给定的transactional.id总是相同的”。它进一步引用问题示例如下:例如,在分布式流处理应用程序中,假设主题分区tp0最初由transactional.id T0处理。如果在稍后的某个时刻,它可以映射到另一个具有transactional.id T1的生产者,那么T0和T1之间就不会有隔离。因此,来自tp0的消息有可能被重新处理,违反了一次处理保证。”


    我不太明白为什么会出现这种情况。在我看来,只要事务是原子的,我就不应该关心producer如何处理来自任何分区的消息。我已经为此苦苦挣扎了一天,我想知道是否有人能告诉我我错过了什么。那么,为什么我不能将工作分配给任何具有任何事务的producer实例。id setting,只要它是唯一的。为什么他们说如果你这样做,消息可能会通过交易提供的围栏泄漏。

    你提到的博客文章包含了你要查找的所有信息,尽管它相当密集

    从中的“为什么交易?”部分

    使用为至少一次交付语义配置的vanilla Kafka生产者和消费者,流处理应用程序可能会以以下方式完全丢失一次处理语义:

  • 由于内部重试,
    producer.send()
    可能会导致重复写入消息B。这是由幂等生产者解决的,而不是本文其余部分的重点

  • 我们可能会重新处理输入消息A,导致将重复的B消息写入输出,从而违反一次处理语义。如果流处理应用程序在写入B后但在将A标记为已使用之前崩溃,则可能会发生重新处理。因此,当它恢复时,它将再次使用A并再次写入B,从而导致复制品

  • 最后,在分布式环境中,应用程序将崩溃或更糟!-暂时失去与系统其余部分的连接。通常,新实例会自动启动以替换被视为丢失的实例。通过此过程,我们可能会有多个实例处理同一输入主题并写入ame输出主题,导致重复的输出并违反一次处理语义。我们称之为“僵尸实例”问题。[强调添加]

  • 从中的事务语义部分

    僵尸围栏

    我们通过要求为每个事务性生产者分配一个称为
    transactional.id
    的唯一标识符来解决僵尸实例的问题。该标识符用于在流程重新启动时标识同一生产者实例。[强调添加]

    API要求事务生产者的第一个操作应该是向Kafka集群显式注册其
    transactional.id
    。当它这样做时,Kafka代理使用给定的
    transactional.id
    检查打开的事务并完成它们。它还增加与
    tra关联的历元nsAction.id
    。epoch是为每个
    事务性.id
    存储的内部元数据

    一旦纪元被破坏,任何具有相同
    事务性.id
    和较旧纪元的制作者都将被视为僵尸,并被隔离,即拒绝来自这些制作者的未来事务性写入。
    [重点添加]

    和中的数据流部分

    A:生产者和交易协调人的互动

    执行事务时,生产者在以下几点向事务协调器发出请求:

  • initTransactionsAPI向协调器注册了一个
    transactional.id
    。此时,协调器使用该
    transactional.id
    关闭所有挂起的事务,并启动新纪元以隔离僵尸。这在每个生产者会话中仅发生一次。
    [强调添加]

  • 当生产者准备在事务中第一次向分区发送数据时,该分区首先向协调器注册

  • 当应用程序调用
    commitTransaction
    abortTransaction
    时,会向c