Apache SeaTunnel 协作开发指南:基于 AGENTS.md 的提交规范、代码标准与工程实践全解析
【免费下载链接】seatunnelSeaTunnel is a multimodal, high-performance, distributed, massive data integration tool.项目地址: https://gitcode.com/GitHub_Trending/se/seatunnel
本指南以仓库根目录的 AGENTS.md(LLM Context Guide for Apache SeaTunnel)为核心骨架,系统讲解面向 AI 助手与人类开发者共同遵守的 SeaTunnel 协作契约:从提交前验证、Git 提交信息规范、Java 代码标准、向后兼容性硬约束,到架构准则、测试与调试流程。结合仓库中的 pom.xml、SeaTunnelSource.java、Option.java 等源码证据,帮助读者在提交 PR 时一次通过评审,并为希望用 AI Agent 辅助贡献代码的团队提供可直接落地的工程规范模板。
一、为什么需要 AGENTS.md:LLM 时代的工程协作契约
Apache SeaTunnel 是一个多模态、高性能、分布式的海量数据集成工具,代码库横跨 seatunnel-api、seatunnel-connectors-v2、seatunnel-engine(Zeta 引擎)、seatunnel-transforms-v2 等十余个核心模块。当越来越多的 AI 助手(LLM / Agent)参与代码生成与修改时,如何保证它们产出的代码安全、一致、可验证,就成了社区治理的核心问题。
AGENTS.md 正是为此而生:它借鉴了成熟 Apache 项目的实践,并将其适配到 SeaTunnel 的构建、测试、架构与文档约定上。它不只约束人类开发者,更是一份可被 Agent 直接读取、执行的"机器可读工程规范"。对贡献者而言,理解这份文件等于理解了 SeaTunnel 社区对"一个合格 PR"的全部隐性要求。
二、提交前必须通过的三道验证关卡
AGENTS.md 开篇即给出CRITICAL 级要求:Agent 在提议或定稿任何改动之前,必须在本地运行验证命令。未满足这些要求的 PR 极有可能被直接拒绝。
# 格式化代码(强制) ./mvnw spotless:apply # 快速验证(强制) ./mvnw -q -DskipTests verify # 单元测试(强烈建议) ./mvnw test三道关卡的定位各不相同:
| 命令 | 阶段 | 作用 |
|---|---|---|
./mvnw spotless:apply | 格式化 | 统一全仓库代码风格,消除格式差异造成的评审噪音 |
./mvnw -q -DskipTests verify | 构建验证 | 跳过测试、快速确认编译与打包链路畅通 |
./mvnw test | 单元测试 | 验证行为正确性,防止回归 |
源码佐证:Spotless 到底在强制什么
在根 pom.xml 中,spotless-maven-plugin(版本 2.29.0)被绑定到validate阶段的spotless-check执行目标,也就是说每次构建都会自动检查代码格式,不通过即构建失败。其 Java 配置严格定义了 SeaTunnel 的编码风格:
- 格式化引擎:Google Java Format 1.7,风格为AOSP;
- 自动清理:
removeUnusedImports删除未使用的导入; - 导入排序:强制顺序为
org.apache.seatunnel.shade→org.apache.seatunnel→org.apache→ 其他 →javax→java→ 静态导入; - 正则化替换:删除通配符导入(wildcard imports)、封禁
org.powermock.*、封禁非 JUnit 5 的 JUnit 4 导入(org.junit.[^jupiter]),并将 Guava、Jetty、Hikari、Janino、Apache Commons Lang3 的导入自动改写为 shade 版本(如com.google.common.*→org.apache.seatunnel.shade.com.google.common.*)。
这意味着即使你手写了import java.util.*,spotless:apply也会自动修正;而spotless:check则保证 CI 中永远只存在符合规范的代码。
三、Git 提交信息规范:可搜索历史的基石
SeaTunnel 采用严格的提交信息格式来维护干净、可检索的提交历史,格式为:
[类型][模块] 描述类型(Type)
| 类型 | 含义 |
|---|---|
Feature | 新功能 |
Fix | Bug 修复 |
Improve | 对现有行为的改进 |
Docs | 仅文档变更 |
Test | 测试用例或测试框架变更 |
Chore | 构建、依赖或维护任务 |
模块(Module)
| 模块标识 | 对应代码库目录 |
|---|---|
Connector-V2 | seatunnel-connectors-v2 |
Zeta | seatunnel-engine(Zeta 引擎) |
Core | seatunnel-core |
API | seatunnel-api |
Transform-V2 | seatunnel-transforms-v2 |
Format | seatunnel-formats |
Translation | seatunnel-translation |
E2E | seatunnel-e2e |
官方示例
[Fix][Connector-V2] Fix MySQL source split enumeration bug [Fix][Zeta] Fix checkpoint timeout under heavy backpressure [Feature][Transform-V2] Add LLM transform plugin [Improve][Core] Optimize jar package loading speed [Docs] Update quick start guide从示例可以看出两个要点:一条提交只做一件事;描述采用"动词 + 宾语"的祈使句风格,精准点明改动位置与意图。这与后文的 PR Scope Rule 一脉相承。
四、仓库结构速览:模块地图
AGENTS.md 给出了顶层模块地图,与当前仓库实际目录一一对应:
seatunnel/ ├── seatunnel-api/ # 核心 API 定义 ├── seatunnel-connectors-v2/ # Source & Sink 连接器(主要贡献区域) ├── seatunnel-transforms-v2/ # Transform 插件(含 LLM) ├── seatunnel-engine/ # Zeta 引擎 & Web UI ├── seatunnel-core/ # 作业提交与 CLI 入口 ├── seatunnel-translation/ # Flink & Spark 适配层 ├── seatunnel-formats/ # 数据格式(JSON、Avro 等) ├── seatunnel-e2e/ # 端到端集成测试 ├── docs/ # 文档(en 与 zh 双语) └── config/ # 默认配置理解这张地图的意义在于:任何改动都应落在正确的模块内。例如新增一个连接器,你的代码主体属于seatunnel-connectors-v2;而如果改动涉及作业提交入口,则应定位到seatunnel-core。架构准则一节会进一步解释为什么这条边界如此重要。
五、Java 代码标准:从格式到设计
5.1 核心规则清单
- 格式化:Google Java Format(AOSP 风格),由 Spotless 强制执行(见上文 pom.xml 证据);
- 导入:
- 禁止通配符导入;
- 使用 shade 依赖:
org.apache.seatunnel.shade.*;
- 可空性:避免隐式空值假设,null 语义要显式表达;
- 可见性:保持 API 最小化,能包级私有(package-private)就优先包级私有;
- 注释:重要方法必须添加注释——包括 public API、生命周期钩子(初始化、start/stop、checkpoint)、以及复杂或性能关键的逻辑。
5.2 注释规范示例
AGENTS.md 给出了 Source Split 枚举方法的注释模板,该示例在仓库中有完全对应的真实接口。见 SeaTunnelSource.java 中createEnumerator的 Javadoc:
/** * Create source split enumerator, used to generate splits. This method will be called only once * when start a source. * * @param enumeratorContext enumerator context. * @return source split enumerator. * @throws Exception when create enumerator failed. */ SourceSplitEnumerator<SplitT, StateT> createEnumerator( SourceSplitEnumerator.Context<SplitT> enumeratorContext) throws Exception;这种"先写清契约(何时调用、参数含义、返回值、异常),再写实现"的注释风格,正是评审者与后续维护者最需要的信息。
六、ASF License 头:所有新文件的硬性要求
所有新增文件必须包含 Apache Software Foundation 许可证头(NOTICE 与 LICENSE 共同构成合规基础),完整模板如下:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */该头在仓库每个.java文件的顶部均可看到,例如上文引用的 SeaTunnelSource.java 与 Option.java 都完整携带此头。缺少 License 头的文件在提交时会被社区工具链标记,属于一票否决项。
七、向后兼容性:硬约束(VERY IMPORTANT)
AGENTS.md 将向后兼容性定义为硬约束(hard constraint),Agent 与开发者都必须遵守:
- 禁止删除或重命名现有配置项(Option);
- 禁止随意修改默认值;
- 禁止破坏公共 API 或 SPI 契约。
任何不兼容变更必须同时满足四点:
- 被显式记录在文档中;
- 记录在 docs/en/introduction/concepts/incompatible-changes.md;
- 包含迁移指南(migration guidance);
- 在 PR 描述中清晰说明。
仓库实践:incompatible-changes.md 的用法
该文档按版本记录所有不兼容更新,升级前必须先检查。以当前仓库中记录的一条 JDBC 变更为例(见 incompatible-changes.md),其标准结构是:
- Affected component:
seatunnel-connectors-v2/connector-jdbcsink exactly-once XA 路径; - Description:恢复逻辑改为消费
max_commit_attempts、fail-closed 处理 XID 缺口; - Impact:依赖旧恢复语义的任务可能在新版本恢复时失败;
- Migration Guide:升级前检查资源管理器中悬挂的 prepared XA 事务(如 MySQL 的
XA RECOVER、PostgreSQL 的pg_prepared_xacts),并协调外部清理动作与作业恢复的时序。
另一个例子是表级监控指标键格式从{tableName}变更为{VertexIdentifier}.{tableName}(如Sink[0].fake.user_table),影响 Grafana 面板与 Prometheus 告警规则——这类对运行期可观测性的破坏同样被显式登记(见同文件### API Changes一节)。
这套机制保证了:破坏性变更永远有迹可循、有据可迁,不会在升级时"静默爆炸"。
八、依赖规则:能不引入就不引入
- 禁止无充分理由引入新依赖;
- 优先复用
org.apache.seatunnel.shade.*下已有的 shade 依赖; - 任何新依赖必须:
- 在 PR 描述中说明理由;
- 评估 shading、体积与冲突风险。
这一规则与 Spotless 的导入改写规则(第五节)互为表里:仓库刻意将 Guava、Jetty、Hikari、Commons Lang3 等常用库统一收编到org.apache.seatunnel.shade命名空间,目的就是收敛依赖版本、消除类冲突——这是大数据组件生态里最常见的"依赖地狱"来源。新增依赖时若与已有 shade 库功能重叠,评审几乎必然要求改用现成 shade 版本。
九、架构准则:连接器与 Zeta 引擎的边界
9.1 连接器(Connector V2)开发准则
- 实现
SeaTunnelSource或SeaTunnelSink接口; - 使用
Option定义配置; - 通过
SourceSplitEnumerator支持并行度; - 禁止将连接器特定逻辑泄漏到引擎或 core 层。
以 SeaTunnelSource.java 为例,该接口同时继承Serializable、PluginIdentifierInterface、SeaTunnelPluginLifeCycle、SeaTunnelJobAware,其核心方法分工清晰:
| 方法 | 职责 |
|---|---|
getBoundedness() | 返回数据源的有界性(BATCH / STREAMING) |
getProducedCatalogTables() | 输出 CatalogTable 元数据,替代已废弃的getProducedType() |
createReader(...) | 创建用于产出数据的 SourceReader |
getSplitSerializer() | 序列化/反序列化 Split,默认DefaultSerializer |
createEnumerator(...) | 创建 Split 枚举器,仅在 Source 启动时调用一次 |
对应地,SeaTunnelSink.java 定义了 Sink 侧契约。**"连接器逻辑不进引擎"**这条边界保证了:新增一个连接器不需要改动 Zeta 引擎一行代码,引擎只依赖seatunnel-api中的 SPI 契约。
9.2 Zeta 引擎三角色模型
AGENTS.md 明确了 Zeta 引擎(seatunnel-engine)的角色分工:
- Client:提交作业配置;
- Master:调度与协调;
- Worker:执行任务(Source → Transform → Sink)。
这三个角色对应 seatunnel-engine 下的 client / server 等子模块,任务边界与生命周期语义必须被严格遵守——例如 checkpoint 的触发与恢复逻辑归属引擎层,连接器只通过 SPI 暴露状态快照接口。
十、配置(Option)规则:稳定契约的基石
所有面向用户的配置必须使用Option定义,且每个 Option 必须包含:
name(名称)type(类型)default value(默认值,如适用)description(清晰描述)
Option 名称是稳定契约,不得随意重命名。
源码佐证:Option 类的字段设计
Option.java 的字段恰好对应上述四项要求:
public class Option<T> { /** The current key for that config option. */ private final String key; /** Type of the value that this Option describes. */ private final TypeReference<T> typeReference; /** The default value for this option. */ private final T defaultValue; /** The description for this option. */ String description = ""; @Getter private final List<String> fallbackKeys; ... }值得关注的是fallbackKeys字段与withFallbackKeys(...)方法:它允许一个 Option 携带回退键,这是实现"旧配置名 → 新配置名"平滑迁移的机制——旧键仍可被识别,从而在重命名配置时不必破坏向后兼容(呼应第七节)。当配置语义变化时,正确做法是保留旧 Option 键并通过 fallback 过渡,而非直接删除。
十一、错误处理与日志规范
- 异常必须携带足够上下文(表名、任务、配置键);
- 禁止吞掉异常(swallow exceptions);
- 日志级别使用规范:
INFO—— 生命周期事件;WARN—— 可恢复问题;ERROR—— 会导致任务失败的错误;
- 绝不记录敏感信息(密码、令牌、凭据)。
这条规则在连接器与引擎的错误路径中随处可见:例如 CDC 连接器的 DDL 解析错误处理变更(见 incompatible-changes.md)——DDL 解析器内部错误不再被吞掉,而是作为解析失败向上传播,避免 CDC 作业静默跳过 schema 变更。这正是"异常必须暴露而非吞掉"原则的活案例。
十二、文档规则:文档是功能的一部分
任何用户可见的变更必须同步更新:
- docs/en(英文文档)
- docs/zh(中文文档)
并且:配置名、默认值、示例必须与代码严格一致;文档是功能的一部分,而不是事后的补充说明("Documentation is part of the feature, not an afterthought")。
这一规则与第七节的兼容性登记(incompatible-changes.md位于 docs/en/introduction/concepts/incompatible-changes.md)形成闭环:代码变更 → 双语文档更新 → 破坏性变更登记。对 Agent 而言,生成代码却不同步更新docs/en与docs/zh,属于必然被打回的缺陷。
十三、测试指南:单元测试与 E2E 测试
13.1 单元测试
- 位于各模块
src/test/java下; - 验证行为而非实现细节;
- 优先确定性、最小化的测试。
运行命令:
./mvnw test13.2 E2E 测试
- 位于 seatunnel-e2e 目录;
- 使用Testcontainers拉起真实依赖(数据库、消息队列等);
- 测试类继承
TestSuiteBase。
运行命令(跳过单测、只跑集成测试):
./mvnw -DskipUT -DskipIT=false verifyTestSuiteBase位于 seatunnel-e2e/seatunnel-e2e-common/src/test/java/org/apache/seatunnel/e2e/common/TestSuiteBase.java,它是所有连接器 E2E 测试的公共基类,负责容器生命周期管理与测试环境搭建。以连接器为例,seatunnel-e2e 下每个connector-xxx-e2e模块即为对应连接器的集成测试工程,例如 connector-jdbc-e2e 下有 60+ 个 Java 测试文件,覆盖各数据库方言。
十四、性能意识与 PR 范围
性能意识
Agent 与开发者在任何改动中都必须考虑性能影响:
- 避免在热路径(hot paths)中创建不必要的对象;
- 谨慎使用大内存缓冲区;
- 考虑并行度与资源使用。
SeaTunnel 作为数据集成引擎,Source/Sink 的每行数据处理都在热路径上,微小的分配开销会被数据量放大成可观测的吞吐损失。仓库还提供了 seatunnel-benchmarks(JMH 基准测试)与 tools/benchmarks 脚本,用于量化这类影响。
PR Scope Rule
- 保持改动最小且聚焦;
- 避免无关的重构或纯格式化改动;
- 一个 PR 只解决一个问题。
这与第三节"一条提交只做一件事"相呼应,是 SeaTunnel 社区评审效率高的根本原因之一。
十五、运行与调试:从源码构建到跑通作业
15.1 从源码构建
./mvnw clean install -DskipTests -Dskip.spotless=true注意:跳过测试(-DskipTests)与跳过格式检查(-Dskip.spotless=true)通常用于本地快速构建;正式提交前仍应按第二节要求补跑完整验证。
15.2 安装连接器
sh bin/install-plugin.sh $current_version该脚本按当前版本将连接器插件安装到运行环境(插件清单可参考根目录 plugin-mapping.properties)。
15.3 以 Zeta 模式运行作业
sh bin/seatunnel.sh --config config/v2.batch.config.template -e local其中-e local表示以本地(单机)模式执行,--config指向作业配置。仓库自带的示例配置 config/v2.batch.config.template 展示了最小可运行结构:
env { # You can set SeaTunnel environment configuration here parallelism = 2 job.mode = "BATCH" checkpoint.interval = 10000 } source { FakeSource { parallelism = 2 plugin_output = "fake" row.num = 16 schema = { fields { name = "string" age = "int" } } } }FakeSource是仅用于测试与演示的假数据源,配合 Console Sink 即可在不依赖任何外部系统的情况下验证引擎全链路(Source → Transform → Sink)。流式版本可参考 config/v2.streaming.conf.template。
十六、给 Agent 与贡献者的行动清单
把 AGENTS.md 的全部约束浓缩为一次贡献的完整流程:
- 动工前:明确改动所属模块(第四节的模块地图),确认接口契约(第九节的 SPI 边界);
- 编码中:遵守 Java 规范(第五节)、携带 License 头(第六节)、使用
Option定义配置(第十节)、按规范记录日志(第十一节); - 验证:依次执行
./mvnw spotless:apply→./mvnw -q -DskipTests verify→./mvnw test(第二节),连接器改动还应补充 E2E 测试(第十三节); - 兼容性:自检是否触碰了"删除/重命名配置、改默认值、破 SPI"三条红线(第七节),必要时登记到 incompatible-changes 文档并附迁移指南;
- 提交:按
[Type][Module] Description格式书写提交信息(第三节); - PR:保持范围最小(第十四节),在描述中说明依赖与性能考量,并同步更新 docs/en 与 docs/zh(第十二节)。
遵循这套流程产出的 PR,无论在人类评审还是 CI 检查面前,都能以最低的沟通成本快速通过——这正是 AGENTS.md 作为"LLM 时代的工程协作契约"的核心价值。
【免费下载链接】seatunnelSeaTunnel is a multimodal, high-performance, distributed, massive data integration tool.项目地址: https://gitcode.com/GitHub_Trending/se/seatunnel
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考