第3章 实验环境:让证据可以重新产生
四个真实服务与一个消费者
随书环境使用 Docker Compose 创建 MySQL、Kafka、Debezium Connect 和 PostgreSQL。MySQL 是源,Kafka 是事件日志,Connect 运行实际 MySQL 连接器,PostgreSQL 是可查询投影。Python 程序通过真实网络连接访问它们,没有用手造 JSON 代替连接器集成。单个 broker 和单个数据分区降低环境复杂度,使我们能专注观察故障窗口。
四个服务镜像均使用真实基线取得的固定摘要,并在每次运行时保存解析后的 RepoDigests。镜像版本名称只是一项可读说明,实际摘要才说明那次运行用了什么镜像内容。程序同时执行数据库版本查询并列出连接器插件版本。若上游重新发布同名标签,读者至少可以从证据发现环境发生变化,而不是仅依靠文件名推断版本。
实验依赖固定在 lab/requirements.txt。Python 的 Kafka 客户端使用 librdkafka,数据库客户端分别连接 MySQL 和 PostgreSQL。代码没有为依赖不可用增加跳过分支;服务启动超时、没有消息或连接器失败都会使运行失败。报告中的 skipped 固定为零只是记录约定,真正的保障是每个检查条件都必须通过,失败无法进入出版步骤。
运行和清理的所有权
运行前必须设置以 cdc-book- 开头的专属 Compose 项目名。脚本只对这个项目执行 down 和卷清理,不扫描或删除机器上所有容器。固定主机端口使命令容易阅读,也意味着同一主机不能同时运行两份实验;端口冲突应当失败,不能通过杀死未知进程解决。项目名前缀不是权限隔离机制,只是防止误操作的明确约束。
export CDC_COMPOSE_PROJECT=cdc-book-learning-001
sh lab/run.sh脚本先安装固定 Python 依赖,再启动服务,记录镜像摘要,运行实验,最后保存容器日志并清理该项目。清理放在退出处理器中,成功和失败都执行。证据目录留在宿主机,容器删除不会同时抹掉失败原因。独立下载 ZIP 后也执行同一入口,确保代码、配置和依赖完整。
为什么使用低权限连接器账户
源库初始化创建专用 cdc 账户,并赋予读取表、读取复制日志及快照所需权限。实验 root 账户只用于初始化数据、变更表结构和核对过程。这样至少把业务操作与连接器身份分开,便于理解权限用途。不过这些仍然是本地教学密码,Compose 网络也没有配置生产 TLS 或机密管理,不能直接复制到公网数据库。
连接器的 server id 与源库 server id 不同,重建连接器再使用另一个 id。复制客户端身份冲突会导致难以解释的连接行为,不能把它当作任意装饰字段。topic 前缀和 schema 历史 topic 也为在线链路与重建链路分别配置,防止测试时把两个世代的记录混进同一个数据流。
保留失败现场比美化报告更有用
证据包括运行检查列表、版本、镜像摘要、连接器配置、快照交叠记录、消费者原始输出、逐次对账和容器日志。消费者返回非零时,控制程序保留标准输出和错误输出,再让整个集成失败。失败报告不能只写一个红色状态;需要知道是连接器未运行、Kafka 没有产生消息、投影事务失败,还是结果已经不一致。
初始基线和出版流水线都在 GitHub Actions 的真实 Linux runner 上运行。本地没有 Docker 的读者仍可检查源代码、下载证据和阅读书稿,但这不等于本地已经复现实验。出版检查会把下载 ZIP 解压到另一目录,再从干净容器环境完整执行一次;这比只检查 ZIP 里有没有文件更接近读者实际使用路径。
不要把测试环境当成性能结论
两千行合成商品足以触发快照、修改、删除和重建,但不能代表千万级表的吞吐。人为放慢快照查询只用于建立可观察的交叠窗口,不是生产调优建议。单机 Docker 的网络、磁盘与资源争用也不同于独立数据库集群。我们记录完成时间方便比较运行是否异常,不用它承诺生产端到端延迟。
环境失败不能被当成业务成功
初始化SQL默认要求一个干净的独占环境,重复使用已经存在的数据库会明确失败,而不是悄悄删除未知表。读者若手工绕过入口脚本,应自行保证资源所有权与清洁状态。证据目录也不应复用旧运行内容,尤其是崩溃屏障标记;正式入口在独立环境中运行,出版包排除全部运行产物。测试失败后应先保存日志,再清理项目,不能为了让下一次变绿而把失败证据一并抹掉。
本章练习
读出一次运行的 MySQL 版本、Connect 插件版本和四个镜像摘要,说明它们各自证明了什么。然后故意占用一个实验端口,确认实验明确失败且没有清理其他项目的资源。