---
title: "Kafka Simulator v1.4 — 投递语义、事务与双活"
date: 2026-08-24T00:00:00.000Z
author: "michal"
excerpt: "exactly-once、read_committed 与 last stable offset、begin/commit/abort，以及 consume–process–produce 循环。事务模块把 Kafka 最难的主题 EOS 变成可以一步步走完的东西。自由模式还新增了一种集群类型：双活。"
---
v1.4 为 [Kafka Simulator](/kafka-simulator/) 带来 **事务** 模块 —— **10 个新场景**，讲的是工程师最常点头称是、却最少真正看见的主题：exactly-once 语义。课程体系达到 **125 个中的 55 个**。

## v1.4 里有什么

事务把一串写入变成一个原子单元，同时也改变了消费者被允许看到的内容：

- **`read_committed` 与 last stable offset（LSO）** —— 为什么事务型消费者读到 LSO 而不是 high watermark，以及一个未结束的事务如何把这个上限按在原地。
- **begin / commit / abort** —— 生产者的事务生命周期，作为一台你亲手驱动的状态机，包括 abort 标记对它所覆盖的记录做了什么。
- **consume–process–produce 循环** —— `sendOffsetsToTransaction`，以及为什么把偏移量提交放在事务*内部*，才让端到端的 exactly-once 成为现实。
- **屏蔽（fencing）** —— 僵尸生产者如何被 epoch 挡在门外，以及为什么这是整个模型底下的安全网。

exactly-once 难教，是因为其中大半都不可见：标记、LSO、epoch，还有一个悄悄跳过已中止记录的消费者。模拟器把这一切都摆上时间线 —— 你可以看着 LSO 在未结束的事务下一动不动，又在它提交的那一刻向前一跃。

## 自由模式的新集群类型：双活

从 v1.3 的主备开始的拓扑阶梯继续往上走：自由模式的集群选择器新增了第三种形态 —— **Active / active（双活）**：两个区域都接受写入，并各自向对方镜像。

它刻意不是「把主备的箭头翻一倍」。这个预设是*对称*的：

- **每个区域拥有自己的 topic。** `west` 拥有 `west.orders`，`east` 拥有 `east.orders`，各自以只读方式镜像到对面区域 —— 于是两边都持有完整的流，而不会出现同名之下谁能写的歧义。
- **每个区域本地写、全局读。** 生产者只往自己区域的 topic 追加；每个区域的消费者组同时读本地 topic 和镜像过来的副本，因此每条记录在每一侧都恰好被看到一次。
- **每个区域都是独立的集群。** 两边各跑一个独立的 KRaft 仲裁组，所以你可以杀掉某个区域的控制器 —— 或者整个区域 —— 看看幸存的一侧还能做什么、不能做什么。

最后这一点，正是与本次版本包所讲的事务保证之间那道锋利的对照：exactly-once 是*单集群*范围内的承诺，而一对双活是两个集群。事务模块给你的一切，在一个区域内部成立，到镜像那里为止。

解释这些形态的灾备*课程*仍然会在 v1.8 到来 —— 沙盒先落地是有意为之，好让你在场景开口讲述之前，先有地方把这些想法试一遍。

## 建立在复制之上

这个版本包直接站在 v1.3 之上。事务就是在持久性保证之上再加一层原子性，所以你在那里学会读的 ISR 和 high watermark，在这里依然是底层事实 —— LSO 只不过是压在它们之上、更严格的一道上限。

打开[模拟器](/kafka-simulator/)，开启一个事务，往里生产，然后中止它 —— 再看看 `read_committed` 消费者看得到什么、看不到什么。