diff --git a/pipeline-framework/.gitignore b/pipeline-framework/.gitignore index 1325313dd..44fe1e1c2 100644 --- a/pipeline-framework/.gitignore +++ b/pipeline-framework/.gitignore @@ -6,45 +6,26 @@ pom.xml.versionsBackup pom.xml.next release.properties dependency-reduced-pom.xml -buildNumber.properties -.mvn/timing.properties -.mvn/wrapper/maven-wrapper.jar # IDE .idea/ *.iml -*.iws *.ipr +*.iws .vscode/ -*.swp -*.swo -*~ -.project -.classpath .settings/ +.classpath +.project + +# Build +*.class +*.jar +*.war # Logs *.log -logs/ -/var/log/ # OS .DS_Store Thumbs.db -desktop.ini - -# Application -/data/ -/checkpoint-data/ -/app-logs/ - -# Test -/test-output/ -*.class -*.jar -!.mvn/wrapper/maven-wrapper.jar -# Temporary files -*.tmp -*.bak -*.pid diff --git a/pipeline-framework/ARCHITECTURE_DESIGN.md b/pipeline-framework/ARCHITECTURE_DESIGN.md new file mode 100644 index 000000000..a1ea9b004 --- /dev/null +++ b/pipeline-framework/ARCHITECTURE_DESIGN.md @@ -0,0 +1,522 @@ +# Pipeline Framework 架构设计文档 + +## 概述 + +Pipeline Framework 是一个基于响应式编程(Project Reactor)的ETL数据处理框架,支持流式处理、批处理和SQL批处理。 + +本次重构的核心目标: +1. **分离关注点**:Connector不依赖Reactor,Component依赖Reactor +2. **增强抽象**:多层次的接口继承和泛型约束 +3. **应用设计模式**:工厂、适配器、模板方法、策略、建造者 +4. **提升扩展性**:插件化的Connector注册机制 + +--- + +## 核心架构层次 + +``` +┌─────────────────────────────────────────────────────────┐ +│ Application Layer │ +│ (Job Definition & Execution) │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Component Layer │ +│ (Reactor-based Data Processing) │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ DataSource│ │ Operator │ │ DataSink │ │ +│ └──────────┘ └──────────┘ └──────────┘ │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Adapter Layer │ +│ (Connector → Component Adaptation) │ +│ ┌────────────────────────────────────────────┐ │ +│ │ ReaderToSourceAdapter WriterToSinkAdapter │ │ +│ └────────────────────────────────────────────┘ │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Connector Layer │ +│ (Reactor-free I/O Operations) │ +│ ┌──────────────────┐ ┌──────────────────┐ │ +│ │ ConnectorReader │ │ ConnectorWriter │ │ +│ └──────────────────┘ └──────────────────┘ │ +└─────────────────────────────────────────────────────────┘ +``` + +--- + +## 设计模式应用 + +### 1. 工厂模式 (Factory Pattern) + +**目的**:统一创建Connector实例,解耦对象创建逻辑。 + +**实现**: +- `ConnectorFactory`: 工厂接口,泛型参数T为数据类型,C为配置类型 +- `ConnectorFactoryRegistry`: 工厂注册中心,单例模式 +- `JdbcConnectorFactory`: JDBC连接器的具体工厂实现 + +**类图**: +``` +┌─────────────────────────────┐ +│ ConnectorFactory │ +├─────────────────────────────┤ +│ + createReader(C): Reader │ +│ + createWriter(C): Writer │ +│ + getSupportedType(): Type │ +└──────────────┬──────────────┘ + △ + │ +┌──────────────┴──────────────┐ +│ JdbcConnectorFactory │ +└─────────────────────────────┘ +``` + +**使用示例**: +```java +// 注册工厂 +ConnectorFactoryRegistry registry = ConnectorFactoryRegistry.getInstance(); +registry.register(ConnectorType.JDBC, new JdbcConnectorFactory()); + +// 创建Reader +JdbcConnectorConfig config = new JdbcConnectorConfig(); +config.setUrl("jdbc:mysql://localhost:3306/test"); +config.setUsername("root"); +config.setPassword("password"); +config.setQuerySql("SELECT * FROM users"); + +ConnectorReader, JdbcConnectorConfig> reader = + registry.createReader(ConnectorType.JDBC, config); +``` + +--- + +### 2. 适配器模式 (Adapter Pattern) + +**目的**:将不依赖Reactor的Connector转换为依赖Reactor的Component。 + +**实现**: +- `ConnectorAdapter`: 适配器接口 +- `AbstractConnectorAdapter`: 适配器抽象基类 +- `DefaultReaderToSourceAdapter`: Reader到Source的适配器 +- `DefaultWriterToSinkAdapter`: Writer到Sink的适配器 + +**类图**: +``` +┌────────────────────────────────────┐ +│ ConnectorAdapter │ +├────────────────────────────────────┤ +│ + adapt(CONN): COMP │ +│ + getConnector(): CONN │ +│ + supports(CONN): boolean │ +└────────────────┬───────────────────┘ + △ + │ +┌────────────────┴───────────────────┐ +│ AbstractConnectorAdapter │ +├────────────────────────────────────┤ +│ # preAdapt(CONN): void │ +│ # doAdapt(CONN): COMP │ +│ # postAdapt(CONN, COMP): void │ +└────────────────┬───────────────────┘ + △ + ┌────────┴────────┐ + │ │ +┌───────┴──────────┐ ┌──┴──────────────┐ +│ ReaderToSource │ │ WriterToSink │ +│ Adapter │ │ Adapter │ +└──────────────────┘ └─────────────────┘ +``` + +**使用示例**: +```java +// 创建Connector +JdbcConnectorReader reader = new JdbcConnectorReader(config); + +// 使用适配器转换为DataSource +DefaultReaderToSourceAdapter, JdbcConnectorConfig> adapter = + new DefaultReaderToSourceAdapter<>(reader, 1000); + +DataSource> source = adapter.adapt(reader); + +// 使用响应式流 +Flux> dataStream = source.read(); +dataStream.subscribe(data -> System.out.println(data)); +``` + +--- + +### 3. 模板方法模式 (Template Method Pattern) + +**目的**:定义算法骨架,让子类实现具体步骤。 + +**实现**: +- `AbstractJdbcConnector`: JDBC连接器的抽象基类 +- `AbstractConnectorAdapter`: 适配器的抽象基类 + +**模板方法流程**: + +```java +// AbstractJdbcConnector 的 open() 方法 +public void open() throws Exception { + // 1. 加载驱动(公共步骤) + loadDriver(); + + // 2. 建立连接(公共步骤) + establishConnection(); + + // 3. 配置连接(公共步骤) + configureConnection(); + + // 4. 子类初始化(钩子方法) + doOpen(); +} +``` + +**子类实现**: +```java +public class JdbcConnectorReader extends AbstractJdbcConnector> { + + @Override + protected void doOpen() throws Exception { + // 子类特定的初始化逻辑 + statement = connection.prepareStatement(config.getQuerySql()); + resultSet = statement.executeQuery(); + } +} +``` + +--- + +### 4. 策略模式 (Strategy Pattern) + +**目的**:定义一系列算法,让它们可以相互替换。 + +**实现**: +- 不同类型的`Connector`作为不同的策略 +- `ConnectorType`枚举定义策略类型 +- `ConnectorFactoryRegistry`作为策略选择器 + +**使用示例**: +```java +// 策略1:JDBC Connector +ConnectorReader jdbcReader = registry.createReader(ConnectorType.JDBC, jdbcConfig); + +// 策略2:Kafka Connector(未来扩展) +ConnectorReader kafkaReader = registry.createReader(ConnectorType.KAFKA, kafkaConfig); + +// 策略3:File Connector(未来扩展) +ConnectorReader fileReader = registry.createReader(ConnectorType.FILE, fileConfig); +``` + +--- + +### 5. 建造者模式 (Builder Pattern) + +**目的**:分步骤构建复杂对象。 + +**实现**: +- `ConnectorMetadata.Builder`: 构建Connector元数据 +- `ComponentMetadata.Builder`: 构建Component元数据 + +**使用示例**: +```java +ConnectorMetadata metadata = ConnectorMetadata.builder() + .name("my-jdbc-reader") + .type(ConnectorType.JDBC) + .version("1.0.0") + .description("MySQL数据库读取器") + .attribute("database", "test") + .attribute("table", "users") + .build(); +``` + +--- + +## 核心接口设计 + +### 1. Connector层次 + +```java +// 顶层接口 +public interface Connector { + String getName(); + ConnectorType getType(); + C getConfig(); + ConnectorMetadata getMetadata(); + boolean validate(); +} + +// Reader接口(增加泛型约束) +public interface ConnectorReader extends Connector { + void open() throws Exception; + List readBatch(int batchSize) throws Exception; + boolean hasNext(); + void close() throws Exception; + + // 可选能力 + Object getCheckpoint(); + void seekToCheckpoint(Object checkpoint) throws Exception; + boolean supportsCheckpoint(); + double getProgress(); + long getReadCount(); +} + +// Writer接口(增加泛型约束) +public interface ConnectorWriter extends Connector { + void open() throws Exception; + void write(T record) throws Exception; + void writeBatch(List records) throws Exception; + void flush() throws Exception; + void close() throws Exception; + + // 事务能力 + boolean supportsTransaction(); + void beginTransaction() throws Exception; + void commit() throws Exception; + void rollback() throws Exception; + + // 检查点能力 + Object saveCheckpoint() throws Exception; + void restoreCheckpoint(Object checkpoint) throws Exception; + long getWriteCount(); +} + +// 可读连接器(增强接口) +public interface ReadableConnector + extends ConnectorReader { + ConnectorReader duplicate() throws ConnectorException; + boolean supportsParallelRead(); +} + +// 可写连接器(增强接口) +public interface WritableConnector + extends ConnectorWriter { + ConnectorWriter duplicate() throws ConnectorException; + boolean supportsParallelWrite(); + boolean supportsIdempotentWrite(); +} +``` + +--- + +### 2. Component层次 + +```java +// 顶层接口 +public interface Component { + String getName(); + ComponentType getComponentType(); + C getConfig(); + Mono healthCheck(); + ComponentMetadata getMetadata(); +} + +// 生命周期接口 +public interface LifecycleAware { + Mono start(); + Mono stop(); + boolean isRunning(); +} + +// 流式组件接口(三个泛型参数) +public interface StreamingComponent extends Component { + Flux process(Flux input); + Class getInputType(); + Class getOutputType(); +} + +// DataSource接口 +public interface DataSource extends Component, LifecycleAware { + Flux read(); + SourceType getType(); + Class getOutputType(); +} + +// DataSink接口 +public interface DataSink extends Component, LifecycleAware { + Mono write(Flux data); + Mono writeBatch(Flux data, int batchSize); + SinkType getType(); + Mono flush(); + Class getInputType(); +} + +// Operator接口 +public interface Operator + extends StreamingComponent { + Flux apply(Flux input); + OperatorType getType(); +} +``` + +--- + +## 泛型约束体系 + +### 层次1:基础泛型 +```java +// Connector层: +Connector +ConnectorReader +ConnectorWriter +``` + +### 层次2:组件泛型 +```java +// Component层: +Component + +// StreamingComponent层: +StreamingComponent +Operator extends StreamingComponent +``` + +### 层次3:适配器泛型 +```java +// Adapter层: +ConnectorAdapter, COMP extends Component, C extends ConnectorConfig> + +// 具体适配器 +ReaderToSourceAdapter + extends ConnectorAdapter, DataSource, C> + +WriterToSinkAdapter + extends ConnectorAdapter, DataSink, C> +``` + +--- + +## 使用场景示例 + +### 场景1:创建一个JDBC到MySQL的ETL任务 + +```java +// 1. 创建Reader配置 +JdbcConnectorConfig sourceConfig = new JdbcConnectorConfig(); +sourceConfig.setName("mysql-source"); +sourceConfig.setUrl("jdbc:mysql://source:3306/db"); +sourceConfig.setUsername("root"); +sourceConfig.setPassword("password"); +sourceConfig.setQuerySql("SELECT * FROM users WHERE active = 1"); +sourceConfig.setBatchSize(1000); + +// 2. 创建Writer配置 +JdbcConnectorConfig sinkConfig = new JdbcConnectorConfig(); +sinkConfig.setName("mysql-sink"); +sinkConfig.setUrl("jdbc:mysql://target:3306/db"); +sinkConfig.setUsername("root"); +sinkConfig.setPassword("password"); +sinkConfig.setTableName("users_backup"); + +// 3. 使用工厂创建Connector +ConnectorFactoryRegistry registry = ConnectorFactoryRegistry.getInstance(); +registry.register(ConnectorType.JDBC, new JdbcConnectorFactory()); + +ConnectorReader, JdbcConnectorConfig> reader = + registry.createReader(ConnectorType.JDBC, sourceConfig); + +ConnectorWriter, JdbcConnectorConfig> writer = + registry.createWriter(ConnectorType.JDBC, sinkConfig); + +// 4. 使用适配器转换为Component +DefaultReaderToSourceAdapter, JdbcConnectorConfig> sourceAdapter = + new DefaultReaderToSourceAdapter<>(reader, 1000); +DataSource> source = sourceAdapter.adapt(reader); + +DefaultWriterToSinkAdapter, JdbcConnectorConfig> sinkAdapter = + new DefaultWriterToSinkAdapter<>(writer, 1000); +DataSink> sink = sinkAdapter.adapt(writer); + +// 5. 构建Pipeline执行 +source.read() + .map(data -> { + // 数据转换逻辑 + data.put("migrated_at", System.currentTimeMillis()); + return data; + }) + .transform(dataStream -> sink.write(dataStream)) + .subscribe(); +``` + +### 场景2:扩展新的Connector类型 + +```java +// 1. 定义配置类 +public class KafkaConnectorConfig extends ConnectorConfig { + private String bootstrapServers; + private String topic; + // ... getters and setters +} + +// 2. 实现Reader +public class KafkaConnectorReader + extends AbstractKafkaConnector + implements ReadableConnector { + + @Override + protected void doOpen() throws Exception { + // Kafka consumer初始化 + } + + @Override + public List readBatch(int batchSize) throws Exception { + // 读取消息 + } + + // ... 其他方法实现 +} + +// 3. 实现工厂 +public class KafkaConnectorFactory + implements ConnectorFactory { + + @Override + public ConnectorReader createReader( + KafkaConnectorConfig config) throws ConnectorException { + return new KafkaConnectorReader(config); + } + + // ... 其他方法实现 +} + +// 4. 注册工厂 +ConnectorFactoryRegistry.getInstance() + .register(ConnectorType.KAFKA, new KafkaConnectorFactory()); +``` + +--- + +## 总结 + +本架构通过以下设计原则实现了高度的灵活性和可扩展性: + +1. **单一职责原则**:Connector专注I/O,Component专注数据处理 +2. **开闭原则**:通过接口和抽象类,对扩展开放,对修改封闭 +3. **里氏替换原则**:子类可以替换父类,不影响程序正确性 +4. **接口隔离原则**:多个专用接口,而非单一大接口 +5. **依赖倒置原则**:依赖抽象,而非具体实现 + +**关键优势**: +- ✅ 插件化的Connector注册机制 +- ✅ 类型安全的泛型约束 +- ✅ 职责清晰的分层架构 +- ✅ 灵活的设计模式组合 +- ✅ 易于测试和扩展 + +**设计模式应用总结**: +- 🏭 工厂模式:统一创建Connector +- 🔌 适配器模式:Connector → Component转换 +- 📋 模板方法模式:定义算法骨架 +- 🎯 策略模式:可替换的Connector实现 +- 🔧 建造者模式:复杂对象构建 +- 📝 注册表模式:动态注册Connector工厂 +- 🔒 单例模式:ConnectorFactoryRegistry + +--- + +**文档版本**:1.0.0 +**最后更新**:2025-11-10 +**作者**:Pipeline Framework Team diff --git a/pipeline-framework/ARCHITECTURE_EXPLANATION.md b/pipeline-framework/ARCHITECTURE_EXPLANATION.md deleted file mode 100644 index 0af4a51ff..000000000 --- a/pipeline-framework/ARCHITECTURE_EXPLANATION.md +++ /dev/null @@ -1,344 +0,0 @@ -# Pipeline Framework 架构说明 - -## 为什么去掉 start() 和 stop()? - -### 原来的问题 - -在 `DefaultPipeline` 中,有这样的逻辑: - -```java -public Mono execute() { - return source.start() // 1. 先启动 Source - .then(sink.start()) // 2. 再启动 Sink - .then(executePipeline()) // 3. 最后执行数据流 - .doFinally(signal -> { - source.stop(); // 4. 停止 Source - sink.stop(); // 5. 停止 Sink - }); -} -``` - -**这样做的问题**: - -1. **概念混淆**: Source 和 Sink 是数据流的一部分,不应该有独立的生命周期 -2. **冗余操作**: `start()` 做什么?只是为了初始化?那为什么不在构造函数或第一次读取时初始化? -3. **响应式违和**: Reactor 本身就管理订阅/取消订阅,不需要手动 start/stop -4. **复杂度增加**: 开发者需要理解两套生命周期:Reactor 的订阅模型 + 自定义的 start/stop - -### 新的设计 - -```java -public Mono execute() { - // 直接构建数据流 - Flux dataFlow = buildDataFlow(); - - // 写入 Sink - return sink.write(dataFlow) - .then(...) // 返回结果 -} - -private Flux buildDataFlow() { - // 1. 从 Source 读取 - Flux dataFlow = source.read(); - - // 2. 通过 Operators - for (Operator op : operators) { - dataFlow = op.apply(dataFlow); - } - - return dataFlow; -} -``` - -**优势**: - -1. **语义清晰**: `execute()` = 构建流 + 执行流 -2. **符合 Reactor**: 订阅时自动开始,取消时自动停止 -3. **代码简洁**: 不需要管理额外的生命周期 -4. **易于理解**: 新人一看就懂 - -## 核心架构 - -### 三层模型 - -``` -┌─────────────────────────────────────────────┐ -│ Graph Layer │ -│ (StreamGraph, StreamNode, StreamEdge) │ -│ 定义:JSON → Graph 对象 │ -└─────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────┐ -│ Builder Layer │ -│ (GraphBasedPipelineBuilder) │ -│ 转换:Graph → 实际组件 │ -└─────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────┐ -│ Execution Layer │ -│ (SimplePipeline) │ -│ 执行:组件 → 响应式流 │ -└─────────────────────────────────────────────┘ -``` - -### Graph Layer(图层) - -**职责**: 定义 Pipeline 的结构 - -- `StreamGraph`: 整个数据流图 -- `StreamNode`: 图中的节点(Source/Operator/Sink) -- `StreamEdge`: 节点之间的连接 - -**示例**: - -```java -StreamGraph graph = new DefaultStreamGraph("my-pipeline"); -graph.addNode(sourceNode); -graph.addNode(operatorNode); -graph.addNode(sinkNode); -graph.addEdge(new StreamEdge("source", "operator")); -graph.addEdge(new StreamEdge("operator", "sink")); -``` - -### Builder Layer(构建层) - -**职责**: 将 Graph 转换为实际的可执行组件 - -核心类:`GraphBasedPipelineBuilder` - -**流程**: - -```java -public Mono> buildFromGraph(StreamGraph graph) { - // 1. 验证 Graph - graph.validate(); - - // 2. 拓扑排序(确保正确的执行顺序) - List sorted = graph.topologicalSort(); - - // 3. 创建 Source - DataSource source = createSource(sourceNode); - - // 4. 创建 Operators - List> operators = createOperators(operatorNodes); - - // 5. 创建 Sink - DataSink sink = createSink(sinkNode); - - // 6. 组装 Pipeline - return new SimplePipeline(name, source, operators, sink); -} -``` - -**关键点**: - -- 使用 `ConnectorRegistry` 查找和创建 Source/Sink -- 使用 `OperatorFactory` 创建 Operator -- 所有创建操作都是响应式的(返回 `Mono`) - -### Execution Layer(执行层) - -**职责**: 执行实际的数据处理 - -核心类:`SimplePipeline` - -**流程**: - -```java -public Mono execute() { - // 1. 构建数据流 - Flux dataFlow = source.read() // 从 Source 读取 - .transform(operator1::apply) // 应用 Operator1 - .transform(operator2::apply) // 应用 Operator2 - ...; - - // 2. 写入 Sink - return sink.write(dataFlow) - .then(Mono.just(result)); // 返回结果 -} -``` - -**关键点**: - -- 使用 `Flux.transform()` 串联 Operators -- 整个过程是惰性的(Lazy),只在订阅时才执行 -- 自动处理背压(Backpressure) - -## 组件注册机制 - -### ConnectorRegistry - -管理所有的 Connector(Source/Sink 的工厂) - -```java -public interface ConnectorRegistry { - Mono registerConnector(String type, Connector connector); - Mono getConnector(String type); -} -``` - -**使用**: - -```java -ConnectorRegistry registry = new ConnectorRegistryImpl(); - -// 注册 -registry.registerConnector("kafka", new KafkaConnector()); -registry.registerConnector("mysql", new MysqlConnector()); - -// 获取 -Connector connector = registry.getConnector("kafka").block(); -DataSource source = connector.createSource(config).block(); -``` - -### OperatorFactory - -管理所有的 Operator 创建逻辑 - -```java -public interface OperatorFactory { - Mono> createOperator(OperatorType type, OperatorConfig config); -} -``` - -**使用**: - -```java -OperatorFactory factory = new OperatorFactoryImpl(); - -// 创建 Filter -Operator filter = factory.createOperator( - OperatorType.FILTER, - filterConfig -).block(); - -// 创建 Map -Operator map = factory.createOperator( - OperatorType.MAP, - mapConfig -).block(); -``` - -## 数据流转详解 - -### 从 JSON 到执行 - -``` -1. JSON 字符串 - ↓ -2. StreamGraph 对象 (通过 Jackson 解析) - ↓ -3. 验证 + 拓扑排序 - ↓ -4. 创建 Source (通过 ConnectorRegistry) - ↓ -5. 创建 Operators (通过 OperatorFactory) - ↓ -6. 创建 Sink (通过 ConnectorRegistry) - ↓ -7. 组装 SimplePipeline - ↓ -8. 调用 pipeline.execute() - ↓ -9. 构建响应式流: Source.read() → Ops → Sink.write() - ↓ -10. 订阅并执行 - ↓ -11. 返回 PipelineResult -``` - -### Reactor 数据流 - -``` -订阅时刻: -subscriber.subscribe(pipeline.execute()) - ↓ -SimplePipeline.execute() - ↓ -sink.write( - operator2.apply( - operator1.apply( - source.read() ← 从这里开始产生数据 - ) - ) -) - ↓ -数据从 Source 流向 Sink: -[Source] → [Operator1] → [Operator2] → [Sink] -``` - -**重要特性**: - -1. **惰性求值**: 只有在 `subscribe()` 时才开始执行 -2. **自动背压**: 如果 Sink 处理慢,会自动减缓 Source 的生成速度 -3. **异步非阻塞**: 所有 I/O 操作都在后台线程池执行 -4. **自动资源管理**: 订阅取消时自动清理资源 - -## 扩展点 - -### 1. 自定义 Source - -```java -public class MyCustomSource implements DataSource { - @Override - public Flux read() { - return Flux.create(sink -> { - // 你的数据生成逻辑 - for (MyData data : fetchData()) { - sink.next(data); - } - sink.complete(); - }); - } -} -``` - -### 2. 自定义 Operator - -```java -public class MyCustomOperator implements Operator { - @Override - public Flux apply(Flux input) { - return input - .map(this::transform) // 转换 - .filter(this::isValid); // 过滤 - } -} -``` - -### 3. 自定义 Sink - -```java -public class MyCustomSink implements DataSink { - @Override - public Mono write(Flux data) { - return data - .buffer(100) // 批量 - .flatMap(this::batchWrite) - .then(); - } -} -``` - -## 总结 - -### 设计原则 - -1. **简单优先**: 去掉不必要的抽象(start/stop) -2. **响应式优先**: 充分利用 Reactor 的能力 -3. **声明式**: Graph 定义 + 响应式流组合 -4. **可扩展**: 通过 Registry 和 Factory 注册自定义组件 - -### 核心优势 - -1. **易于理解**: 清晰的三层架构 -2. **易于开发**: 简单的接口,丰富的示例 -3. **易于扩展**: 灵活的注册机制 -4. **高性能**: 响应式非阻塞 I/O - -### 适用场景 - -- 实时数据流处理 -- ETL 数据管道 -- 事件驱动架构 -- 微服务间的数据集成 diff --git a/pipeline-framework/BUILD_AND_RUN.md b/pipeline-framework/BUILD_AND_RUN.md deleted file mode 100644 index 2307a6829..000000000 --- a/pipeline-framework/BUILD_AND_RUN.md +++ /dev/null @@ -1,346 +0,0 @@ -# 构建和运行指南 - -## 快速开始 - -### 1. 构建项目 - -```bash -# 进入项目目录 -cd /workspace/pipeline-framework - -# 编译整个项目(跳过测试) -mvn clean install -DskipTests - -# 或者编译并运行测试 -mvn clean install -``` - -### 2. 使用Docker Compose启动(推荐) - -```bash -# 启动所有服务(包括MySQL、Kafka、Redis、应用) -docker-compose up -d - -# 查看日志 -docker-compose logs -f etl-framework - -# 查看所有容器状态 -docker-compose ps - -# 停止所有服务 -docker-compose down -``` - -### 3. 本地开发模式 - -#### 3.1 启动依赖服务 - -```bash -# 只启动MySQL、Kafka、Redis -docker-compose up -d mysql kafka redis zookeeper - -# 等待服务启动完成 -docker-compose ps -``` - -#### 3.2 初始化数据库 - -```bash -# 方式1: 使用Docker exec -docker exec -i etl-mysql mysql -uroot -proot123 etl_framework < docs/database-schema.sql - -# 方式2: 使用本地MySQL客户端 -mysql -h localhost -P 3306 -u root -proot123 etl_framework < docs/database-schema.sql -``` - -#### 3.3 启动应用 - -```bash -# 方式1: 使用Maven -cd etl-starter -mvn spring-boot:run -Dspring-boot.run.profiles=dev - -# 方式2: 直接运行JAR -java -jar etl-starter/target/etl-starter-1.0.0-SNAPSHOT.jar --spring.profiles.active=dev -``` - -### 4. 验证服务 - -```bash -# 健康检查 -curl http://localhost:8080/actuator/health - -# 查看信息 -curl http://localhost:8080/actuator/info - -# 查看Prometheus指标 -curl http://localhost:8080/actuator/prometheus -``` - -## 开发调试 - -### 使用IDE运行 - -#### IntelliJ IDEA - -1. 导入项目:File → Open → 选择项目根目录的pom.xml -2. 等待Maven导入完成 -3. 找到`EtlFrameworkApplication.java` -4. 右键 → Run 'EtlFrameworkApplication' - -#### VS Code - -1. 安装Java Extension Pack -2. 打开项目文件夹 -3. 按F5启动调试 - -### 配置开发环境 - -编辑 `etl-starter/src/main/resources/application-dev.yml`: - -```yaml -spring: - r2dbc: - url: r2dbc:mysql://localhost:3306/etl_framework - username: root - password: root123 - -logging: - level: - com.etl.framework: DEBUG -``` - -### 热重载 - -```bash -# 启用Spring Boot DevTools进行热重载 -mvn spring-boot:run -Dspring-boot.run.profiles=dev -``` - -## 测试 - -### 运行单元测试 - -```bash -# 运行所有测试 -mvn test - -# 运行特定模块的测试 -mvn test -pl etl-api - -# 运行特定测试类 -mvn test -Dtest=DataSourceTest -``` - -### 运行集成测试 - -```bash -# 运行集成测试 -mvn verify - -# 跳过单元测试,只运行集成测试 -mvn verify -DskipUnitTests -``` - -## 打包部署 - -### 构建Docker镜像 - -```bash -# 构建镜像 -docker build -t etl-framework:1.0.0 . - -# 查看镜像 -docker images | grep etl-framework - -# 运行镜像 -docker run -d \ - --name etl-framework \ - -p 8080:8080 \ - -e SPRING_PROFILES_ACTIVE=prod \ - -e DB_HOST=host.docker.internal \ - -e DB_USERNAME=root \ - -e DB_PASSWORD=password \ - etl-framework:1.0.0 -``` - -### 生产环境部署 - -```bash -# 1. 编译生产版本 -mvn clean package -Pprod -DskipTests - -# 2. 复制JAR文件 -cp etl-starter/target/etl-starter-1.0.0-SNAPSHOT.jar /opt/etl-framework/ - -# 3. 创建systemd服务(Linux) -sudo cat > /etc/systemd/system/etl-framework.service <> pipelineMono = builder.buildFromGraph(graph); -``` - -### 4. 执行 Pipeline - -```java -// 执行 Pipeline -pipelineMono - .flatMap(Pipeline::execute) - .subscribe( - result -> { - System.out.println("Pipeline 执行成功!"); - System.out.println("处理记录数: " + result.getRecordsProcessed()); - System.out.println("执行时间: " + result.getDuration().toMillis() + " ms"); - }, - error -> { - System.err.println("Pipeline 执行失败: " + error.getMessage()); - error.printStackTrace(); - }, - () -> { - System.out.println("Pipeline 执行完成"); - } - ); -``` - -### 5. 完整的可运行示例 - -```java -package com.pipeline.framework.examples; - -import com.pipeline.framework.api.graph.*; -import com.pipeline.framework.connectors.ConnectorRegistry; -import com.pipeline.framework.connectors.ConnectorRegistryImpl; -import com.pipeline.framework.connectors.console.ConsoleConnector; -import com.pipeline.framework.core.builder.GraphBasedPipelineBuilder; -import com.pipeline.framework.core.pipeline.Pipeline; -import com.pipeline.framework.operators.OperatorFactory; -import com.pipeline.framework.operators.OperatorFactoryImpl; -import reactor.core.publisher.Mono; - -import java.util.Map; - -/** - * Pipeline Framework 完整示例。 - */ -public class CompleteExample { - - public static void main(String[] args) { - // 1. 创建 Graph - StreamGraph graph = buildExampleGraph(); - - // 2. 初始化组件 - ConnectorRegistry connectorRegistry = new ConnectorRegistryImpl(); - connectorRegistry.registerConnector("console", new ConsoleConnector()); - - OperatorFactory operatorFactory = new OperatorFactoryImpl(); - - // 3. 创建 Builder - GraphBasedPipelineBuilder builder = new GraphBasedPipelineBuilder( - connectorRegistry, - operatorFactory - ); - - // 4. 构建并执行 Pipeline - builder.buildFromGraph(graph) - .flatMap(Pipeline::execute) - .block(); // 阻塞等待完成(仅用于演示) - } - - /** - * 构建示例 Graph。 - */ - private static StreamGraph buildExampleGraph() { - DefaultStreamGraph graph = new DefaultStreamGraph( - "example-pipeline-001", - "示例数据管道", - GraphType.STREAMING - ); - - // Source 节点 - DefaultStreamNode sourceNode = new DefaultStreamNode( - "source-1", - "测试数据源", - NodeType.SOURCE - ); - sourceNode.setConfig(Map.of( - "type", "CUSTOM", - "count", 10, - "intervalMs", 100 - )); - graph.addNode(sourceNode); - - // Filter Operator 节点 - DefaultStreamNode filterNode = new DefaultStreamNode( - "operator-1", - "过滤器", - NodeType.OPERATOR - ); - filterNode.setOperatorType("FILTER"); - filterNode.setConfig(Map.of( - "name", "filter-empty" - )); - graph.addNode(filterNode); - - // Map Operator 节点 - DefaultStreamNode mapNode = new DefaultStreamNode( - "operator-2", - "转大写", - NodeType.OPERATOR - ); - mapNode.setOperatorType("MAP"); - mapNode.setConfig(Map.of( - "name", "to-uppercase" - )); - graph.addNode(mapNode); - - // Sink 节点 - DefaultStreamNode sinkNode = new DefaultStreamNode( - "sink-1", - "控制台输出", - NodeType.SINK - ); - sinkNode.setConfig(Map.of( - "type", "CONSOLE" - )); - graph.addNode(sinkNode); - - // 添加边 - graph.addEdge(new DefaultStreamEdge("source-1", "operator-1")); - graph.addEdge(new DefaultStreamEdge("operator-1", "operator-2")); - graph.addEdge(new DefaultStreamEdge("operator-2", "sink-1")); - - return graph; - } -} -``` - -## 执行流程详解 - -### SimplePipeline 执行逻辑 - -```java -public Mono execute() { - // 1. 构建响应式数据流 - Flux dataFlow = source.read() // 从 Source 读取 - .doOnNext(...) // 记录日志 - - // 2. 依次通过每个 Operator - for (Operator op : operators) { - dataFlow = op.apply(dataFlow); // 串联转换 - } - - // 3. 写入 Sink - return sink.write(dataFlow) - .then(...) // 返回结果 -} -``` - -### GraphBasedPipelineBuilder 构建逻辑 - -```java -public Mono> buildFromGraph(StreamGraph graph) { - // 1. 验证 Graph - if (!graph.validate()) { - return Mono.error(...); - } - - // 2. 拓扑排序 - List sortedNodes = graph.topologicalSort(); - - // 3. 分类节点 - StreamNode sourceNode = findSourceNode(graph); - List operatorNodes = findOperatorNodes(sortedNodes); - StreamNode sinkNode = findSinkNode(graph); - - // 4. 创建组件(响应式) - return createSource(sourceNode) - .flatMap(source -> - createOperators(operatorNodes) - .flatMap(operators -> - createSink(sinkNode) - .map(sink -> - new SimplePipeline(name, source, operators, sink) - ) - ) - ); -} -``` - -## 核心优势 - -### 1. 清晰的数据流 - -不再有 `start()` 和 `stop()` 的困扰,直接构建响应式流: - -``` -Source.read() → Operator1.apply() → Operator2.apply() → Sink.write() -``` - -### 2. 纯响应式 - -整个过程使用 Reactor 的 `Flux` 和 `Mono`,充分利用响应式编程的优势: -- **背压(Backpressure)**: 自动处理生产者/消费者速度不匹配 -- **异步非阻塞**: 高效的资源利用 -- **声明式组合**: 易于理解和维护 - -### 3. 可扩展 - -- 通过 `ConnectorRegistry` 注册自定义 Connector -- 通过 `OperatorFactory` 注册自定义 Operator -- 所有组件都是接口,易于替换和扩展 - -## 预期输出 - -``` -=== Starting Pipeline: 示例数据管道 === -Source started: 测试数据源 -Operator[0] started: filter-empty -Operator[1] started: to-uppercase -[控制台输出] [1] MESSAGE-1 -[控制台输出] [2] MESSAGE-2 -[控制台输出] [3] MESSAGE-3 -... -[控制台输出] [10] MESSAGE-10 -Source completed: 测试数据源 -Operator[0] completed: filter-empty -Operator[1] completed: to-uppercase -Console sink completed: 10 records written -=== Pipeline Completed: 示例数据管道 === -Duration: 1234 ms -Records: 10 -``` - -## 总结 - -通过这个完整示例,你可以看到: - -1. **Graph 定义**: 声明式定义数据管道结构 -2. **组件创建**: 通过 Factory 和 Registry 创建实际组件 -3. **Pipeline 构建**: 将组件串联成响应式流 -4. **执行**: 一行代码启动整个流程 - -整个过程逻辑清晰,易于理解和维护! diff --git a/pipeline-framework/CONNECTOR_DEVELOPMENT_GUIDE.md b/pipeline-framework/CONNECTOR_DEVELOPMENT_GUIDE.md new file mode 100644 index 000000000..5c6870d49 --- /dev/null +++ b/pipeline-framework/CONNECTOR_DEVELOPMENT_GUIDE.md @@ -0,0 +1,430 @@ +# Connector 开发指南 + +本指南帮助开发者创建自定义Connector插件。 + +--- + +## 快速开始 + +### 步骤1:创建配置类 + +继承`ConnectorConfig`基类,定义Connector特定的配置。 + +```java +public class MyConnectorConfig extends ConnectorConfig { + + private String endpoint; + private int timeout; + + @Override + public void validate() throws IllegalArgumentException { + if (endpoint == null || endpoint.isEmpty()) { + throw new IllegalArgumentException("Endpoint is required"); + } + } + + // Getters and Setters +} +``` + +--- + +### 步骤2:实现Reader(如果支持读取) + +继承抽象基类或直接实现`ReadableConnector`接口。 + +```java +public class MyConnectorReader extends AbstractMyConnector + implements ReadableConnector { + + public MyConnectorReader(MyConnectorConfig config) { + super(config); + } + + @Override + protected void doOpen() throws Exception { + // 初始化连接、打开资源 + } + + @Override + public List readBatch(int batchSize) throws Exception { + // 批量读取数据 + List batch = new ArrayList<>(); + // ... 读取逻辑 + return batch; + } + + @Override + public boolean hasNext() { + // 判断是否还有数据 + return true; + } + + @Override + protected void doClose() throws Exception { + // 清理资源 + } + + @Override + public String getName() { + return config.getName() != null ? config.getName() : "my-reader"; + } +} +``` + +--- + +### 步骤3:实现Writer(如果支持写入) + +```java +public class MyConnectorWriter extends AbstractMyConnector + implements WritableConnector { + + private long writeCount = 0; + + public MyConnectorWriter(MyConnectorConfig config) { + super(config); + } + + @Override + protected void doOpen() throws Exception { + // 初始化连接 + } + + @Override + public void write(MyDataType record) throws Exception { + // 单条写入 + writeCount++; + } + + @Override + public void writeBatch(List records) throws Exception { + // 批量写入 + for (MyDataType record : records) { + write(record); + } + } + + @Override + public void flush() throws Exception { + // 刷新缓冲区 + } + + @Override + protected void doClose() throws Exception { + // 清理资源 + } + + @Override + public long getWriteCount() { + return writeCount; + } + + @Override + public String getName() { + return config.getName() != null ? config.getName() : "my-writer"; + } +} +``` + +--- + +### 步骤4:创建工厂类 + +```java +public class MyConnectorFactory + implements ConnectorFactory { + + @Override + public ConnectorReader createReader( + MyConnectorConfig config) throws ConnectorException { + return new MyConnectorReader(config); + } + + @Override + public ConnectorWriter createWriter( + MyConnectorConfig config) throws ConnectorException { + return new MyConnectorWriter(config); + } + + @Override + public ConnectorType getSupportedType() { + return ConnectorType.CUSTOM; // 或定义新的类型 + } + + @Override + public boolean supports(MyConnectorConfig config) { + return config != null && config.getEndpoint() != null; + } +} +``` + +--- + +### 步骤5:注册工厂 + +```java +// 在应用启动时注册 +ConnectorFactoryRegistry registry = ConnectorFactoryRegistry.getInstance(); +registry.register(ConnectorType.CUSTOM, new MyConnectorFactory()); +``` + +--- + +## 高级特性 + +### 1. 实现检查点(断点续传) + +```java +@Override +public Object getCheckpoint() { + // 返回当前位置信息 + return currentOffset; +} + +@Override +public void seekToCheckpoint(Object checkpoint) throws Exception { + // 从检查点恢复 + currentOffset = (Long) checkpoint; + // 跳转到该位置 +} + +@Override +public boolean supportsCheckpoint() { + return true; +} +``` + +--- + +### 2. 实现事务 + +```java +@Override +public boolean supportsTransaction() { + return true; +} + +@Override +public void beginTransaction() throws Exception { + // 开启事务 + inTransaction = true; +} + +@Override +public void commit() throws Exception { + // 提交事务 + inTransaction = false; +} + +@Override +public void rollback() throws Exception { + // 回滚事务 + inTransaction = false; +} +``` + +--- + +### 3. 实现进度跟踪 + +```java +@Override +public double getProgress() { + if (totalRecords <= 0) { + return -1.0; + } + return (double) readCount / totalRecords; +} + +@Override +public long getReadCount() { + return readCount; +} +``` + +--- + +### 4. 支持并行读/写 + +```java +@Override +public boolean supportsParallelRead() { + return true; +} + +@Override +public ConnectorReader duplicate() + throws ConnectorException { + MyConnectorReader newReader = new MyConnectorReader(config); + newReader.open(); + return newReader; +} +``` + +--- + +## 使用抽象基类(推荐) + +创建抽象基类来封装通用逻辑: + +```java +public abstract class AbstractMyConnector implements Connector { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final MyConnectorConfig config; + protected Connection connection; + protected volatile boolean opened = false; + + protected AbstractMyConnector(MyConnectorConfig config) { + this.config = config; + this.config.validate(); + } + + public void open() throws Exception { + if (opened) { + return; + } + + logger.info("Opening connector: {}", getName()); + + // 建立连接(模板方法) + establishConnection(); + + // 子类初始化(钩子方法) + doOpen(); + + opened = true; + } + + protected abstract void doOpen() throws Exception; + + protected void establishConnection() throws Exception { + // 通用连接逻辑 + } + + public void close() throws Exception { + if (!opened) { + return; + } + + logger.info("Closing connector: {}", getName()); + + // 子类清理(钩子方法) + doClose(); + + // 关闭连接 + if (connection != null) { + connection.close(); + } + + opened = false; + } + + protected abstract void doClose() throws Exception; + + @Override + public ConnectorType getType() { + return ConnectorType.CUSTOM; + } + + @Override + public MyConnectorConfig getConfig() { + return config; + } +} +``` + +--- + +## 最佳实践 + +### 1. 资源管理 +- ✅ 在`open()`中初始化资源 +- ✅ 在`close()`中释放资源 +- ✅ 使用try-catch-finally确保资源释放 +- ✅ 实现幂等的open和close方法 + +### 2. 错误处理 +- ✅ 抛出有意义的异常信息 +- ✅ 使用ConnectorException包装底层异常 +- ✅ 记录详细的日志 +- ✅ 支持重试机制 + +### 3. 性能优化 +- ✅ 使用批量操作(readBatch/writeBatch) +- ✅ 合理设置批次大小 +- ✅ 使用连接池 +- ✅ 避免阻塞操作 + +### 4. 类型安全 +- ✅ 使用泛型约束数据类型 +- ✅ 在配置类中验证参数 +- ✅ 提供类型转换方法 + +### 5. 可测试性 +- ✅ Connector不依赖Reactor,易于单元测试 +- ✅ 提供Mock实现 +- ✅ 使用依赖注入 + +--- + +## 测试示例 + +```java +public class MyConnectorReaderTest { + + @Test + public void testReadBatch() throws Exception { + // 准备配置 + MyConnectorConfig config = new MyConnectorConfig(); + config.setEndpoint("test://localhost"); + + // 创建Reader + MyConnectorReader reader = new MyConnectorReader(config); + reader.open(); + + // 读取数据 + List batch = reader.readBatch(10); + + // 验证 + assertNotNull(batch); + assertTrue(batch.size() <= 10); + + // 清理 + reader.close(); + } +} +``` + +--- + +## 完整示例 + +参考 `pipeline-connectors/jdbc` 包下的JDBC Connector实现: +- `JdbcConnectorConfig` +- `AbstractJdbcConnector` +- `JdbcConnectorReader` +- `JdbcConnectorWriter` +- `JdbcConnectorFactory` + +--- + +## 常见问题 + +### Q: Connector和Component的区别是什么? +A: Connector是底层I/O操作,不依赖Reactor,专注读写;Component是响应式数据处理,依赖Reactor,处理数据流。 + +### Q: 如何在Component中使用Connector? +A: 使用Adapter层的`DefaultReaderToSourceAdapter`和`DefaultWriterToSinkAdapter`进行转换。 + +### Q: 是否必须同时实现Reader和Writer? +A: 不是,可以只实现其中一个,取决于Connector的功能。 + +### Q: 如何支持多种数据类型? +A: 使用泛型参数``,在具体实现时指定数据类型。 + +--- + +**文档版本**:1.0.0 +**最后更新**:2025-11-10 diff --git a/pipeline-framework/CONTRIBUTING.md b/pipeline-framework/CONTRIBUTING.md deleted file mode 100644 index 293b73a6e..000000000 --- a/pipeline-framework/CONTRIBUTING.md +++ /dev/null @@ -1,210 +0,0 @@ -# 贡献指南 - -感谢你对Reactive ETL Framework项目的关注! - -## 如何贡献 - -### 报告Bug - -如果发现Bug,请通过GitHub Issues提交,包含以下信息: - -1. **Bug描述**: 清晰描述问题 -2. **复现步骤**: 详细的复现步骤 -3. **期望行为**: 你期望的正确行为 -4. **实际行为**: 实际发生的错误行为 -5. **环境信息**: Java版本、操作系统等 -6. **日志**: 相关的错误日志 - -### 提交功能请求 - -通过GitHub Issues提交功能请求,包含: - -1. **功能描述**: 清晰描述新功能 -2. **使用场景**: 为什么需要这个功能 -3. **预期效果**: 功能的预期表现 - -### 提交代码 - -1. **Fork项目** - -```bash -git clone -cd pipeline-framework -``` - -2. **创建分支** - -```bash -git checkout -b feature/your-feature-name -# 或 -git checkout -b bugfix/your-bugfix-name -``` - -3. **编写代码** - -遵循以下规范: - -- 遵循Google Java Style Guide -- 所有公共方法必须有JavaDoc -- 添加单元测试 -- 确保所有测试通过 -- 更新相关文档 - -4. **提交代码** - -```bash -git add . -git commit -m "feat: add amazing feature" -``` - -提交信息格式: -- `feat`: 新功能 -- `fix`: Bug修复 -- `docs`: 文档更新 -- `style`: 代码格式调整 -- `refactor`: 重构 -- `test`: 测试相关 -- `chore`: 构建过程或辅助工具的变动 - -5. **推送代码** - -```bash -git push origin feature/your-feature-name -``` - -6. **创建Pull Request** - -在GitHub上创建Pull Request,描述你的更改。 - -## 代码规范 - -### Java代码规范 - -- 使用Google Java Style -- 类名使用大驼峰命名 -- 方法和变量使用小驼峰命名 -- 常量使用全大写下划线分隔 - -### 日志规范 - -```java -// 使用SLF4J -private static final Logger log = LoggerFactory.getLogger(YourClass.class); - -// 日志级别 -log.trace("详细的调试信息"); -log.debug("调试信息"); -log.info("重要的业务流程"); -log.warn("警告信息"); -log.error("错误信息", exception); -``` - -### 异常处理 - -```java -// 提供有意义的错误信息 -throw new SourceException("Failed to connect to database: " + dbUrl, cause); - -// 使用特定的异常类型 -try { - // ... -} catch (IOException e) { - throw new SourceException("I/O error while reading file", e); -} -``` - -### 资源管理 - -```java -// 使用try-with-resources -try (Connection conn = getConnection()) { - // use connection -} - -// 或在finally中清理 -try { - // use resource -} finally { - cleanup(); -} -``` - -## 测试规范 - -### 单元测试 - -```java -@Test -public void testMapOperator() { - // Given - MapOperator operator = new MapOperator<>(i -> "value-" + i); - Flux input = Flux.just(1, 2, 3); - - // When - Flux output = operator.apply(input); - - // Then - StepVerifier.create(output) - .expectNext("value-1", "value-2", "value-3") - .verifyComplete(); -} -``` - -### 集成测试 - -使用`@SpringBootTest`进行集成测试。 - -## 文档规范 - -### JavaDoc - -```java -/** - * 数据源接口,所有Source实现必须实现此接口。 - *

- * DataSource负责从外部系统读取数据并转换为响应式流。 - *

- * - * @param 输出数据类型 - * @author Your Name - * @since 1.0.0 - */ -public interface DataSource { - // ... -} -``` - -### Markdown文档 - -- 使用清晰的标题层级 -- 添加代码示例 -- 包含必要的图表 - -## 设计模式 - -必须使用的模式: - -1. **Builder模式**: 复杂对象构建 -2. **Factory模式**: 组件创建 -3. **Strategy模式**: 算法选择 -4. **Observer模式**: 状态通知 -5. **Template方法**: 流程定义 - -## 提交前检查清单 - -- [ ] 代码遵循项目规范 -- [ ] 添加了必要的测试 -- [ ] 所有测试通过 -- [ ] 更新了相关文档 -- [ ] 提交信息清晰明确 -- [ ] 没有引入不必要的依赖 -- [ ] 代码通过了静态分析 - -## 联系方式 - -如有问题,请通过以下方式联系: - -- GitHub Issues -- 邮件: etl-framework-team@example.com - -感谢你的贡献! diff --git a/pipeline-framework/DESIGN_PATTERN_EXPLANATION.md b/pipeline-framework/DESIGN_PATTERN_EXPLANATION.md deleted file mode 100644 index dd291a535..000000000 --- a/pipeline-framework/DESIGN_PATTERN_EXPLANATION.md +++ /dev/null @@ -1,527 +0,0 @@ -# Pipeline Framework 设计模式详解 - -## 📐 设计模式应用 - -### 1. 策略模式(Strategy Pattern) - -**问题**:如何避免 switch case 来创建不同类型的组件? - -**解决方案**:使用策略模式 + Spring 依赖注入 - -#### 之前的代码(使用 switch case): - -```java -public Operator createOperator(OperatorType type, OperatorConfig config) { - switch (type) { - case FILTER: - return new FilterOperator(config); - case MAP: - return new MapOperator(config); - case AGGREGATE: - return new AggregateOperator(config); - default: - throw new IllegalArgumentException("Unsupported type: " + type); - } -} -``` - -**问题**: -- 每增加一个类型,就要修改这个方法(违反开闭原则) -- 代码耦合度高 -- 难以测试 - -#### 现在的代码(使用策略模式): - -**步骤 1**: 定义策略接口 - -```java -public interface ComponentCreator { - Mono create(C config); - String getType(); - int getOrder(); -} - -public interface OperatorCreator extends ComponentCreator, OperatorConfig> { -} -``` - -**步骤 2**: 实现具体策略(每个类型一个) - -```java -@Component // Spring 自动扫描 -public class FilterOperatorCreator implements OperatorCreator { - - @Override - public Mono> create(OperatorConfig config) { - return Mono.fromCallable(() -> new FilterOperator<>(config)); - } - - @Override - public String getType() { - return "filter"; - } -} - -@Component -public class MapOperatorCreator implements OperatorCreator { - - @Override - public Mono> create(OperatorConfig config) { - return Mono.fromCallable(() -> new MapOperator<>(config)); - } - - @Override - public String getType() { - return "map"; - } -} -``` - -**步骤 3**: Spring 工厂自动注入所有策略 - -```java -@Component -public class SpringOperatorFactory { - - private final Map creatorMap; - - // Spring 自动注入所有 OperatorCreator 实现 - public SpringOperatorFactory(List creators) { - this.creatorMap = new ConcurrentHashMap<>(); - for (OperatorCreator creator : creators) { - creatorMap.put(creator.getType(), creator); - } - } - - public Mono> createOperator(OperatorConfig config) { - String type = config.getType().name().toLowerCase(); - OperatorCreator creator = creatorMap.get(type); - - if (creator == null) { - return Mono.error(new IllegalArgumentException("Unsupported type: " + type)); - } - - return creator.create(config); - } -} -``` - -**优势**: -- ✅ **开闭原则**:新增类型只需添加一个 `@Component` 类,无需修改工厂 -- ✅ **低耦合**:每个策略独立,互不影响 -- ✅ **易测试**:可以单独测试每个策略 -- ✅ **Spring 管理**:自动发现和注入 - ---- - -### 2. 工厂模式(Factory Pattern)+ Spring IoC - -**问题**:如何统一管理组件的创建? - -**解决方案**:工厂模式 + Spring 依赖注入 - -```java -@Component -public class SpringSourceFactory { - - private final Map creatorMap; - - // Spring 自动注入所有 SourceCreator - public SpringSourceFactory(List creators) { - this.creatorMap = new ConcurrentHashMap<>(); - for (SourceCreator creator : creators) { - creatorMap.put(creator.getType().toLowerCase(), creator); - } - } - - public Mono> createSource(SourceConfig config) { - String type = config.getType().name().toLowerCase(); - SourceCreator creator = creatorMap.get(type); - return creator.create(config); - } -} -``` - -**使用示例**: - -```java -@Component -public class SpringGraphBasedPipelineBuilder { - - private final SpringSourceFactory sourceFactory; - private final SpringSinkFactory sinkFactory; - private final SpringOperatorFactory operatorFactory; - - // Spring 自动注入三个工厂 - public SpringGraphBasedPipelineBuilder( - SpringSourceFactory sourceFactory, - SpringSinkFactory sinkFactory, - SpringOperatorFactory operatorFactory) { - this.sourceFactory = sourceFactory; - this.sinkFactory = sinkFactory; - this.operatorFactory = operatorFactory; - } - - private Mono> createSource(StreamNode node) { - SourceConfig config = parseSourceConfig(node); - return sourceFactory.createSource(config); // 无需 switch - } -} -``` - ---- - -### 3. 建造者模式(Builder Pattern) - -**问题**:如何优雅地构建复杂的 Pipeline? - -**解决方案**:建造者模式 - -```java -@Component -public class SpringGraphBasedPipelineBuilder { - - public Mono> buildFromGraph(StreamGraph graph) { - return Mono.defer(() -> { - // 1. 验证 - if (!graph.validate()) { - return Mono.error(new IllegalArgumentException("Invalid graph")); - } - - // 2. 分类节点 - StreamNode sourceNode = findSourceNode(graph); - List operatorNodes = findOperatorNodes(graph); - StreamNode sinkNode = findSinkNode(graph); - - // 3. 创建组件 - return createSource(sourceNode) - .flatMap(source -> createOperators(operatorNodes) - .flatMap(operators -> createSink(sinkNode) - .map(sink -> assemblePipeline(graph, source, operators, sink)))); - }); - } -} -``` - ---- - -### 4. 模板方法模式(Template Method Pattern) - -**问题**:Pipeline 执行流程固定,但具体实现不同? - -**解决方案**:模板方法模式 - -```java -public abstract class AbstractPipeline implements Pipeline { - - // 模板方法:定义执行流程 - @Override - public final Mono execute() { - return Mono.defer(() -> { - // 1. 执行前钩子 - return beforeExecute() - // 2. 构建数据流 - .then(Mono.defer(this::buildDataFlow)) - // 3. 执行数据流 - .flatMap(this::executeDataFlow) - // 4. 执行后钩子 - .flatMap(this::afterExecute); - }); - } - - // 子类实现具体逻辑 - protected abstract Mono beforeExecute(); - protected abstract Flux buildDataFlow(); - protected abstract Mono executeDataFlow(Flux flow); - protected abstract Mono afterExecute(PipelineResult result); -} -``` - ---- - -### 5. 观察者模式(Observer Pattern) - -**问题**:如何监控 Pipeline 的执行状态? - -**解决方案**:使用 Reactor 的 `doOnXxx` 操作符(内置观察者模式) - -```java -public Mono execute() { - return Mono.defer(() -> { - Flux dataFlow = buildDataFlow(); - - return sink.write(dataFlow) - .doOnSubscribe(s -> notifyListeners(PipelineEvent.STARTED)) - .doOnNext(data -> notifyListeners(PipelineEvent.PROCESSING, data)) - .doOnComplete(() -> notifyListeners(PipelineEvent.COMPLETED)) - .doOnError(e -> notifyListeners(PipelineEvent.FAILED, e)); - }); -} -``` - ---- - -## 🔧 Spring 注解应用 - -### 1. 组件扫描 - -```java -// Source Creator -@Component -public class KafkaSourceCreator implements SourceCreator { - // Spring 自动扫描并注册 -} - -// Sink Creator -@Component -public class ConsoleSinkCreator implements SinkCreator { - // Spring 自动扫描并注册 -} - -// Operator Creator -@Component -public class FilterOperatorCreator implements OperatorCreator { - // Spring 自动扫描并注册 -} -``` - -### 2. 依赖注入 - -```java -@Component -public class ConsoleSourceCreator implements SourceCreator { - - private final Scheduler ioScheduler; - - // 构造函数注入 - public ConsoleSourceCreator(@Qualifier("ioScheduler") Scheduler ioScheduler) { - this.ioScheduler = ioScheduler; - } -} -``` - -### 3. 配置管理 - -```java -@Component -@ConfigurationProperties(prefix = "reactor.scheduler") -public class ReactorSchedulerProperties { - private SchedulerConfig io; - private SchedulerConfig compute; - // Spring 自动绑定配置 -} -``` - -### 4. Bean 管理 - -```java -@Configuration -public class ReactorSchedulerConfig { - - @Bean(name = "ioScheduler", destroyMethod = "dispose") - public Scheduler ioScheduler(ReactorSchedulerProperties properties) { - return Schedulers.newBoundedElastic(...); - } - - @Bean(name = "computeScheduler", destroyMethod = "dispose") - public Scheduler computeScheduler(ReactorSchedulerProperties properties) { - return Schedulers.newParallel(...); - } -} -``` - -### 5. 服务层 - -```java -@Service -public class PipelineExecutionService { - - private final SpringGraphBasedPipelineBuilder pipelineBuilder; - private final Scheduler pipelineScheduler; - - public PipelineExecutionService( - SpringGraphBasedPipelineBuilder pipelineBuilder, - @Qualifier("pipelineScheduler") Scheduler pipelineScheduler) { - this.pipelineBuilder = pipelineBuilder; - this.pipelineScheduler = pipelineScheduler; - } - - public Mono execute(StreamGraph graph) { - return pipelineBuilder.buildFromGraph(graph) - .flatMap(Pipeline::execute) - .subscribeOn(pipelineScheduler); - } -} -``` - ---- - -## 🎯 Reactor 线程池配置 - -### 1. 配置文件 - -```yaml -reactor: - scheduler: - # IO 密集型操作 - io: - pool-size: 100 - queue-size: 1000 - thread-name-prefix: reactor-io- - - # CPU 密集型操作 - compute: - pool-size: 0 # 0 = CPU 核心数 - thread-name-prefix: reactor-compute- - - # 阻塞操作包装 - bounded-elastic: - pool-size: 200 - queue-size: 10000 - ttl-seconds: 60 - thread-name-prefix: reactor-bounded- - - # Pipeline 执行专用 - pipeline: - pool-size: 50 - queue-size: 500 - thread-name-prefix: pipeline-exec- -``` - -### 2. Scheduler 使用场景 - -| Scheduler | 使用场景 | 示例 | -|-----------|---------|------| -| `ioScheduler` | IO 密集型操作 | 数据库查询、HTTP 请求、消息队列 | -| `computeScheduler` | CPU 密集型操作 | 数据转换、计算、聚合 | -| `boundedElasticScheduler` | 阻塞操作包装 | JDBC 调用、同步第三方库 | -| `pipelineScheduler` | Pipeline 执行 | Graph 构建、Pipeline 执行 | - -### 3. 使用示例 - -```java -@Component -public class ConsoleSourceCreator implements SourceCreator { - - private final Scheduler ioScheduler; - - public ConsoleSourceCreator(@Qualifier("ioScheduler") Scheduler ioScheduler) { - this.ioScheduler = ioScheduler; - } - - @Override - public Mono> create(SourceConfig config) { - return Mono.fromCallable(() -> { - // 创建逻辑 - return new ConsoleSource(config); - }) - .subscribeOn(ioScheduler); // 在 IO 线程池执行 - } -} -``` - ---- - -## 📊 架构对比 - -### 之前(使用 switch case) - -``` -GraphBuilder - ↓ -switch (type) { - case SOURCE_A: return new SourceA(); - case SOURCE_B: return new SourceB(); - ... -} -``` - -**问题**: -- ❌ 违反开闭原则 -- ❌ 代码耦合度高 -- ❌ 难以扩展 -- ❌ 测试困难 - -### 现在(使用设计模式 + Spring) - -``` -Spring 容器启动 - ↓ -自动扫描所有 @Component - ↓ -注入到 Factory - ↓ -Factory.create(config) - ↓ -根据 type 查找 Creator - ↓ -Creator.create(config) -``` - -**优势**: -- ✅ 符合开闭原则 -- ✅ 低耦合、高内聚 -- ✅ 易于扩展 -- ✅ 便于测试 -- ✅ Spring 自动管理 - ---- - -## 🚀 如何添加新组件? - -### 示例:添加一个新的 Source - -**步骤 1**:实现 `DataSource` 接口 - -```java -public class MyCustomSource implements DataSource { - @Override - public Flux read() { - return Flux.just(new MyData()); - } -} -``` - -**步骤 2**:创建 Creator(添加 `@Component`) - -```java -@Component // 这就够了!Spring 会自动发现 -public class MyCustomSourceCreator implements SourceCreator { - - @Override - public Mono> create(SourceConfig config) { - return Mono.just(new MyCustomSource()); - } - - @Override - public String getType() { - return "mycustom"; // 定义类型标识 - } -} -``` - -**步骤 3**:完成! - -不需要修改任何其他代码,Spring 会自动: -1. 扫描到 `MyCustomSourceCreator` -2. 注入到 `SpringSourceFactory` -3. 在 `creatorMap` 中注册 - ---- - -## 📝 总结 - -### 核心改进 - -1. **策略模式替代 switch case**:每个类型一个策略类 -2. **Spring 依赖注入**:自动发现和管理所有组件 -3. **Reactor 线程池配置**:针对不同场景使用不同的 Scheduler -4. **开闭原则**:扩展无需修改现有代码 -5. **可测试性**:每个组件独立,易于单元测试 - -### 设计原则 - -- ✅ 单一职责原则(SRP) -- ✅ 开闭原则(OCP) -- ✅ 依赖倒置原则(DIP) -- ✅ 接口隔离原则(ISP) diff --git a/pipeline-framework/FINAL_REFACTORING_SUMMARY.md b/pipeline-framework/FINAL_REFACTORING_SUMMARY.md deleted file mode 100644 index 675cb654c..000000000 --- a/pipeline-framework/FINAL_REFACTORING_SUMMARY.md +++ /dev/null @@ -1,521 +0,0 @@ -# Pipeline Framework 终极重构总结 - -## 🎉 重构完成 - -本次重构彻底改造了整个项目架构,消除了所有 switch case,大幅增强了抽象能力和可扩展性。 - ---- - -## 📊 改造成果统计 - -### 代码清理 - -| 类型 | 数量 | -|-----|------| -| 删除的无用类 | 6 个 | -| 新增的接口 | 11 个 | -| 新增的实现类 | 7 个 | -| 消除的 switch case | 3+ 处 | - -### 删除的无用类 - -1. ❌ `DefaultPipeline` → ✅ 使用 `SimplePipeline` -2. ❌ `GraphBasedPipelineBuilder` → ✅ 使用 `SpringGraphBasedPipelineBuilder` -3. ❌ `PipelineBuilder` → ✅ 无实际用途 -4. ❌ `GraphExecutor` → ✅ 使用 `EnhancedGraphExecutor` -5. ❌ `OperatorChain` → ✅ 直接在 Pipeline 中实现 -6. ❌ `DefaultOperatorChain` → ✅ 直接在 Pipeline 中实现 - ---- - -## 🏗️ 新的架构层次 - -### 1. API 层 - 接口抽象(5 层继承) - -``` -Level 1: Component - ├── ComponentType - ├── ComponentMetadata - └── getName(), getConfig() - -Level 2: LifecycleAware - └── start(), stop(), isRunning() - -Level 2: StreamingComponent extends Component - └── process(), getInputType(), getOutputType() - -Level 3: DataSource extends Component + LifecycleAware - └── read(), getType() - -Level 3: Operator extends StreamingComponent - └── apply(), getType() - -Level 3: DataSink extends Component + LifecycleAware - └── write(), writeBatch(), flush() -``` - -### 2. Core 层 - 策略模式实现 - -``` -NodeExecutor (策略接口) -├── AbstractNodeExecutor (模板方法) - ├── SourceNodeExecutor (@Component) - ├── OperatorNodeExecutor (@Component) - └── SinkNodeExecutor (@Component) - -NodeExecutorRegistry (@Component) -└── 自动注入所有 NodeExecutor - -EnhancedGraphExecutor (@Component) -└── 使用 Registry,无 switch case -``` - ---- - -## 🚀 核心改进详解 - -### 1. 消除 Switch Case - 使用策略模式 - -#### ❌ 改造前(硬编码) - -```java -switch (node.getNodeType()) { - case SOURCE: - flux = buildSourceFlux(node); - break; - case OPERATOR: - flux = buildOperatorFlux(node); - break; - case SINK: - flux = buildOperatorFlux(node); - break; - default: - throw new IllegalStateException("Unknown node type"); -} -``` - -**问题**: -- 违反开闭原则 -- 新增类型需修改代码 -- 代码耦合度高 -- 难以测试 - -#### ✅ 改造后(策略模式) - -```java -// 1. 定义策略接口 -public interface NodeExecutor { - Flux buildFlux(StreamNode node, NodeExecutionContext context); - NodeType getSupportedNodeType(); -} - -// 2. 实现具体策略 -@Component -public class SourceNodeExecutor extends AbstractNodeExecutor { - @Override - public NodeType getSupportedNodeType() { - return NodeType.SOURCE; - } -} - -// 3. Spring 自动注册 -@Component -public class NodeExecutorRegistry { - public NodeExecutorRegistry(List> executors) { - for (NodeExecutor executor : executors) { - executorMap.put(executor.getSupportedNodeType(), executor); - } - } -} - -// 4. 使用(无 switch) -NodeExecutor executor = executorRegistry.getExecutor(node.getNodeType()); -executor.buildFlux(node, context); -``` - -**优势**: -- ✅ 符合开闭原则 -- ✅ 新增类型只需添加 @Component 类 -- ✅ 每个策略独立,易于测试 -- ✅ Spring 自动管理 - ---- - -### 2. 增强接口抽象 - 多层继承 - -#### 设计理念 - -``` -Component (最通用) - ↓ -StreamingComponent (流式处理) - ↓ -Operator (具体算子) -``` - -#### 泛型使用 - -```java -// 基础组件 -Component // C: 配置类型 - -// 流式组件 -StreamingComponent // IN: 输入,OUT: 输出,C: 配置 - -// 具体实现 -DataSource extends Component -Operator extends StreamingComponent -DataSink extends Component -``` - -**优势**: -- ✅ 类型安全(编译期检查) -- ✅ 减少类型转换 -- ✅ 清晰的接口职责 -- ✅ 易于理解和扩展 - ---- - -### 3. 执行上下文 - 统一资源管理 - -```java -public interface NodeExecutionContext { - // 访问 Graph - StreamGraph getGraph(); - - // 访问组件(泛型支持) - Optional> getSource(String nodeId); - Optional> getOperator(String nodeId); - Optional> getSink(String nodeId); - - // Flux 缓存 - Optional> getCachedFlux(String nodeId); - void cacheFlux(String nodeId, Flux flux); - - // 上下文属性 - Optional getAttribute(String key); - void setAttribute(String key, Object value); -} -``` - -**职责**: -- 提供组件访问 -- 缓存 Flux 避免重复构建 -- 存储执行上下文信息 - ---- - -## 📐 设计模式应用汇总 - -### 1. 策略模式(Strategy Pattern) ⭐⭐⭐ - -**应用场景**: -- `NodeExecutor` 体系:根据节点类型选择执行策略 -- `ComponentCreator` 体系:根据组件类型选择创建策略 - -**类图**: - -``` -<> -NodeExecutor - ↑ - ├── SourceNodeExecutor - ├── OperatorNodeExecutor - └── SinkNodeExecutor -``` - -### 2. 模板方法模式(Template Method Pattern) ⭐⭐ - -**应用场景**: -- `AbstractNodeExecutor`:定义构建流程,子类实现具体逻辑 - -```java -public abstract class AbstractNodeExecutor implements NodeExecutor { - @Override - public final Flux buildFlux(StreamNode node, NodeExecutionContext context) { - // 1. 检查缓存 - if (context.getCachedFlux(node.getNodeId()).isPresent()) { - return cachedFlux; - } - - // 2. 构建 Flux(模板方法,子类实现) - Flux flux = doBuildFlux(node, context); - - // 3. 缓存结果 - context.cacheFlux(node.getNodeId(), flux); - return flux; - } - - // 子类实现 - protected abstract Flux doBuildFlux(StreamNode node, NodeExecutionContext context); -} -``` - -### 3. 工厂模式(Factory Pattern) ⭐⭐ - -**应用场景**: -- `SpringSourceFactory` -- `SpringSinkFactory` -- `SpringOperatorFactory` - -### 4. 组合模式(Composite Pattern) ⭐ - -**应用场景**: -- `SimplePipeline`:组合 Source、Operators、Sink - -### 5. 注册表模式(Registry Pattern) ⭐ - -**应用场景**: -- `NodeExecutorRegistry`:管理所有 NodeExecutor -- Spring 自动注入和注册 - ---- - -## 🎯 SOLID 原则遵守 - -### ✅ 单一职责原则(SRP) - -- `NodeExecutor`:只负责构建节点的 Flux -- `NodeExecutionContext`:只负责提供上下文信息 -- `EnhancedGraphExecutor`:只负责协调执行 - -### ✅ 开闭原则(OCP) - -**扩展示例**: - -```java -// 添加新的节点类型:只需添加一个 @Component 类 -@Component -public class CustomNodeExecutor extends AbstractNodeExecutor { - @Override - protected Flux doBuildFlux(StreamNode node, NodeExecutionContext context) { - // 自定义逻辑 - return Flux.just("custom"); - } - - @Override - public NodeType getSupportedNodeType() { - return NodeType.CUSTOM; - } -} -// 完成!无需修改任何现有代码 -``` - -### ✅ 里氏替换原则(LSP) - -- 所有 `NodeExecutor` 实现可互相替换 -- 所有 `Component` 实现可互相替换 - -### ✅ 接口隔离原则(ISP) - -- `Component`:通用属性 -- `LifecycleAware`:生命周期 -- `StreamingComponent`:流式处理 -- 客户端只依赖需要的接口 - -### ✅ 依赖倒置原则(DIP) - -- 依赖抽象(`NodeExecutor`),不依赖具体实现 -- 通过 Spring 注入,实现依赖倒置 - ---- - -## 📈 改进对比 - -| 维度 | 改造前 | 改造后 | 提升 | -|-----|-------|--------|------| -| Switch Case 数量 | 3+ | 0 | 100% 消除 | -| 接口层次 | 1-2 层 | 4-5 层 | 清晰抽象 | -| 泛型使用 | 少量 | 广泛 | 类型安全 | -| 可扩展性 | 需修改代码 | 添加 @Component | 完全开放 | -| 代码重复 | 缓存逻辑重复 | 统一在基类 | 消除重复 | -| 测试性 | 较难 | 独立测试 | 易于测试 | -| 无用类 | 6 个 | 0 | 代码清理 | - ---- - -## 🗂️ 文件结构 - -### 新增的 API 接口 - -``` -pipeline-api/src/main/java/com/pipeline/framework/api/ -├── component/ -│ ├── Component.java # 组件基础接口 -│ ├── ComponentType.java # 组件类型枚举 -│ ├── ComponentMetadata.java # 组件元数据 -│ ├── LifecycleAware.java # 生命周期接口 -│ └── StreamingComponent.java # 流式组件接口 -├── graph/ -│ ├── NodeExecutor.java # 节点执行器接口(策略) -│ └── NodeExecutionContext.java # 执行上下文接口 -└── [source/operator/sink] - └── [更新后的接口] -``` - -### 新增的 Core 实现 - -``` -pipeline-core/src/main/java/com/pipeline/framework/core/ -├── graph/ -│ ├── executor/ -│ │ ├── AbstractNodeExecutor.java # 抽象基类(模板方法) -│ │ ├── SourceNodeExecutor.java # Source 执行器 -│ │ ├── OperatorNodeExecutor.java # Operator 执行器 -│ │ └── SinkNodeExecutor.java # Sink 执行器 -│ ├── NodeExecutorRegistry.java # 执行器注册表 -│ ├── DefaultNodeExecutionContext.java # 默认上下文 -│ └── EnhancedGraphExecutor.java # 增强的图执行器 -└── pipeline/ - ├── SimplePipeline.java # 简化的 Pipeline - └── Pipeline.java # Pipeline 接口 -``` - ---- - -## 🚀 使用示例 - -### 完整的执行流程 - -```java -@Service -public class PipelineService { - - private final EnhancedGraphExecutor graphExecutor; - private final SpringSourceFactory sourceFactory; - private final SpringSinkFactory sinkFactory; - private final SpringOperatorFactory operatorFactory; - - public Mono executePipeline(StreamGraph graph) { - // 1. 创建组件 - Map> sources = createSources(graph); - Map> operators = createOperators(graph); - Map> sinks = createSinks(graph); - - // 2. 执行图(无 switch case,完全由策略模式驱动) - return graphExecutor.execute(graph, sources, operators, sinks); - } -} -``` - -### 扩展示例:添加自定义节点类型 - -```java -// 1. 定义节点类型(可选,如果使用现有类型) -public enum NodeType { - SOURCE, OPERATOR, SINK, - MY_CUSTOM_TYPE // 新增 -} - -// 2. 实现执行器(添加 @Component 即可) -@Component -public class MyCustomNodeExecutor extends AbstractNodeExecutor { - - @Override - protected Flux doBuildFlux(StreamNode node, NodeExecutionContext context) { - // 自定义逻辑 - return Flux.just("my custom logic"); - } - - @Override - public NodeType getSupportedNodeType() { - return NodeType.MY_CUSTOM_TYPE; - } - - @Override - public int getOrder() { - return 100; - } -} - -// 3. 完成!Spring 自动发现并注册,无需修改任何其他代码 -``` - ---- - -## 📚 相关文档 - -| 文档 | 说明 | -|-----|------| -| `REFACTORING_ARCHITECTURE.md` | 详细的架构重构说明 | -| `DESIGN_PATTERN_EXPLANATION.md` | 设计模式应用详解 | -| `SPRING_REACTOR_GUIDE.md` | Spring + Reactor 集成指南 | -| `REFACTORING_SUMMARY.md` | 第一阶段重构总结(策略模式) | -| `COMPLETE_EXAMPLE.md` | 完整的使用示例 | -| `ARCHITECTURE_EXPLANATION.md` | 整体架构说明 | - ---- - -## ✅ 验收清单 - -### 功能验收 - -- [x] 消除所有 switch case -- [x] 使用策略模式替代条件判断 -- [x] 增强接口抽象(4-5 层继承) -- [x] 广泛使用泛型 -- [x] 删除无用类(6 个) -- [x] Spring 注解管理所有组件 -- [x] Reactor 线程池配置 - -### 质量验收 - -- [x] 符合 SOLID 原则 -- [x] 应用多种设计模式 -- [x] 代码清晰、易于理解 -- [x] 易于扩展(无需修改现有代码) -- [x] 易于测试(组件独立) -- [x] 完善的文档 - ---- - -## 🎓 关键收获 - -### 技术收获 - -1. **策略模式的威力**:彻底消除 switch case,符合开闭原则 -2. **多层接口继承**:清晰的抽象层次,职责分明 -3. **泛型的价值**:编译期类型检查,减少运行时错误 -4. **Spring 的便利**:自动注入和管理,减少样板代码 -5. **模板方法模式**:统一流程,避免代码重复 - -### 架构收获 - -1. **抽象至上**:依赖抽象,不依赖具体 -2. **单一职责**:每个类只做一件事 -3. **开闭原则**:对扩展开放,对修改关闭 -4. **组合优于继承**:灵活组合不同组件 -5. **策略优于条件**:用策略模式替代 if/switch - ---- - -## 🏆 总结 - -### 架构优势 - -- ✅ **零 Switch Case**:完全使用策略模式 -- ✅ **清晰的抽象**:4-5 层接口继承 -- ✅ **类型安全**:广泛使用泛型 -- ✅ **易于扩展**:符合开闭原则 -- ✅ **易于测试**:组件独立 -- ✅ **代码整洁**:删除 6 个无用类 -- ✅ **文档完善**:7 个详细文档 - -### 设计原则 - -- ✅ 单一职责原则(SRP) -- ✅ 开闭原则(OCP) -- ✅ 里氏替换原则(LSP) -- ✅ 接口隔离原则(ISP) -- ✅ 依赖倒置原则(DIP) - -### 最终成果 - -**一个高度抽象、易于扩展、完全无 switch case 的响应式数据处理框架!** 🎉 - ---- - -**重构完成日期**:2025-11-09 -**代码质量**:⭐⭐⭐⭐⭐ -**可维护性**:⭐⭐⭐⭐⭐ -**可扩展性**:⭐⭐⭐⭐⭐ diff --git a/pipeline-framework/IDEA_REFRESH.md b/pipeline-framework/IDEA_REFRESH.md new file mode 100644 index 000000000..21345d194 --- /dev/null +++ b/pipeline-framework/IDEA_REFRESH.md @@ -0,0 +1,165 @@ +# IDEA 刷新指南 + +如果遇到 "Java file is located outside of the module source root" 错误,请按以下步骤操作: + +## 方法1:Maven 重新导入(推荐)⭐ + +1. 在IDEA中打开 **Maven** 工具窗口 + - 快捷键:`Ctrl+Shift+A`(Mac: `Cmd+Shift+A`),输入 "Maven" + - 或者:`View` → `Tool Windows` → `Maven` + +2. 点击 **🔄 刷新按钮**(Reload All Maven Projects) + - 位于Maven工具窗口顶部 + - 等待同步完成(可能需要几分钟) + +3. 如果仍有问题,执行 **Clean** + - 右键点击项目根目录 `pipeline-framework` + - 选择 `Maven` → `Clean` + - 然后再次点击刷新 + +## 方法2:清理IDEA缓存 + +1. 关闭IDEA + +2. 删除缓存目录 + ```bash + cd /workspace/pipeline-framework + rm -rf .idea/ + find . -name "*.iml" -delete + ``` + +3. 重新打开IDEA + - `File` → `Open` + - 选择 `/workspace/pipeline-framework/pom.xml` + - 选择 **"Open as Project"** + +4. 等待IDEA索引完成 + +## 方法3:手动标记源代码目录 + +1. 右键点击 `pipeline-api` 模块 + +2. 选择 `Open Module Settings`(或按 `F4`) + +3. 在左侧选择 `Modules` + +4. 展开 `pipeline-api` 模块 + +5. 标记目录: + - 右键 `src/main/java` → `Mark Directory as` → **`Sources Root`** (蓝色图标) + - 右键 `src/main/resources` → `Mark Directory as` → **`Resources Root`** (紫色图标) + - 右键 `src/test/java` → `Mark Directory as` → **`Test Sources Root`** (绿色图标) + - 右键 `src/test/resources` → `Mark Directory as` → **`Test Resources Root`** (紫色图标) + +6. 对所有模块重复上述步骤 + +## 方法4:使用Maven命令生成配置 + +在项目根目录执行: + +```bash +cd /workspace/pipeline-framework +mvn idea:idea +``` + +然后在IDEA中重新打开项目。 + +## 方法5:强制刷新 + +1. 在IDEA中按 `Ctrl+Alt+Shift+/`(Mac: `Cmd+Alt+Shift+/`) + +2. 选择 **"Invalidate Caches"** + +3. 在弹出的对话框中选择: + - ☑️ Invalidate and Restart + - ☑️ Clear file system cache and Local History + - ☑️ Clear VCS Log caches and indexes + +4. 点击 **"Invalidate and Restart"** + +5. 等待IDEA重启并重新索引 + +## 验证成功 + +成功配置后,你应该看到: + +✅ `src/main/java` 目录显示为 **蓝色** 图标(Sources Root) +✅ `src/main/resources` 目录显示为 **紫色** 图标(Resources Root) +✅ `src/test/java` 目录显示为 **绿色** 图标(Test Sources Root) +✅ Java文件可以正常跳转和自动补全 +✅ 不再出现 "outside of the module source root" 警告 + +## 常见问题 + +### Q: 刷新后还是报错? +A: +1. 确认JDK版本是17或更高 +2. 检查 `File` → `Project Structure` → `Project` → `SDK` 是否正确 +3. 确认Maven配置正确:`File` → `Settings` → `Build, Execution, Deployment` → `Build Tools` → `Maven` + +### Q: 某些子包显示错误? +A: +1. 检查包名是否正确(不能有空格或特殊字符) +2. 确认目录下有 `.java` 文件 +3. 重新标记 `src/main/java` 为 Sources Root + +### Q: Maven依赖下载失败? +A: +1. 检查网络连接 +2. 配置Maven镜像(如阿里云镜像) +3. 清理本地仓库:`rm -rf ~/.m2/repository/com/pipeline` + +### Q: 模块之间的依赖无法识别? +A: +1. 确保父 `pom.xml` 中的 `` 列表正确 +2. 确保各模块的 `pom.xml` 中的依赖版本一致 +3. 执行 `mvn clean install` 重新构建 + +## 快速命令 + +```bash +# 一键修复(推荐) +cd /workspace/pipeline-framework +rm -rf .idea/ +find . -name "*.iml" -delete +# 然后在IDEA中重新打开项目 + +# 清理并重新构建 +mvn clean install -DskipTests + +# 生成IDEA配置 +mvn idea:idea + +# 查看模块结构 +ls -d */src/main/java +``` + +## 截图示例 + +正确配置后的目录结构应该是: + +``` +pipeline-api/ + src/main/java/ [蓝色图标] + com/pipeline/framework/api/ + connector/ + ✅ Connector.java + ✅ ConnectorReader.java + ✅ ConnectorWriter.java + adapter/ + ✅ ConnectorAdapter.java + factory/ + ✅ ConnectorFactory.java +``` + +--- + +如果以上方法都不行,请提供: +1. IDEA版本 +2. JDK版本 +3. Maven版本 +4. 具体的错误截图 + +--- + +**最后更新**:2025-11-10 diff --git a/pipeline-framework/IDEA_SETUP.md b/pipeline-framework/IDEA_SETUP.md new file mode 100644 index 000000000..ce71f092a --- /dev/null +++ b/pipeline-framework/IDEA_SETUP.md @@ -0,0 +1,73 @@ +# IDEA 项目配置指南 + +如果IDEA无法识别源代码目录,请按照以下步骤操作: + +## 方法一:Maven 重新导入(推荐) + +1. 在IDEA中打开 `Maven` 工具窗口(View -> Tool Windows -> Maven) +2. 点击刷新按钮(Reload All Maven Projects) +3. 等待Maven同步完成 + +## 方法二:手动标记源代码目录 + +如果Maven刷新后仍有问题,手动标记: + +1. 右键点击项目根目录 `pipeline-framework` +2. 选择 `Open Module Settings` (或按 F4) +3. 在左侧选择 `Modules` +4. 对每个模块(如 pipeline-api, pipeline-core 等): + - 展开模块 + - 右键 `src/main/java` -> Mark Directory as -> Sources Root (蓝色) + - 右键 `src/main/resources` -> Mark Directory as -> Resources Root (紫色) + - 右键 `src/test/java` -> Mark Directory as -> Test Sources Root (绿色) + - 右键 `src/test/resources` -> Mark Directory as -> Test Resources Root (紫色) + +## 方法三:清理并重新导入 + +1. 关闭IDEA +2. 删除以下文件/目录: + ```bash + rm -rf .idea + rm -rf */*.iml + rm -rf *.iml + ``` +3. 重新打开IDEA +4. 选择 `File -> Open` -> 选择 `pom.xml` +5. 选择 `Open as Project` +6. 在弹出的对话框中选择 `Import Maven project automatically` + +## 方法四:使用Maven命令生成IDEA配置 + +在项目根目录执行: + +```bash +mvn idea:idea +``` + +然后在IDEA中重新打开项目。 + +## 验证 + +成功配置后,你应该看到: +- `src/main/java` 目录图标为蓝色(Sources) +- `src/main/resources` 目录图标为紫色(Resources) +- `src/test/java` 目录图标为绿色(Tests) +- 类文件可以正常导航和自动补全 + +## 常见问题 + +### Q: 仍然提示 "move to source root" +A: 检查 pom.xml 中的 `` 是否为 `jar`,确保不是 `pom` + +### Q: 模块间依赖无法识别 +A: 确保父pom.xml和各模块pom.xml中的版本号一致,都是 `1.0.0-SNAPSHOT` + +### Q: Maven依赖下载失败 +A: 检查Maven settings.xml配置,确保有正确的仓库配置 + +--- + +如果以上方法都不行,请提供: +1. IDEA版本 +2. Maven版本(mvn -v) +3. 具体的错误截图 diff --git a/pipeline-framework/IMPLEMENTATION_GUIDE.md b/pipeline-framework/IMPLEMENTATION_GUIDE.md deleted file mode 100644 index e392bf7f4..000000000 --- a/pipeline-framework/IMPLEMENTATION_GUIDE.md +++ /dev/null @@ -1,540 +0,0 @@ -# Pipeline Framework 实现指南 - -## 一、Graph 串联 Source-Operator-Sink 实现原理 - -### 核心实现:GraphExecutor - -`GraphExecutor` 是将 `StreamGraph` 转换为可执行响应式流的核心组件。 - -#### 执行流程 - -``` -StreamGraph (DAG) - ↓ -拓扑排序获取执行顺序 - ↓ -递归构建每个节点的Flux - ↓ -Source.read() → Operator.apply() → Sink.write() - ↓ -组合为完整的响应式Pipeline -``` - -### 使用示例 - -```java -// 1. 准备组件 -Map> sources = new HashMap<>(); -sources.put("source-1", kafkaSource); - -Map> operators = new HashMap<>(); -operators.put("operator-1", mapOperator); -operators.put("operator-2", filterOperator); - -Map> sinks = new HashMap<>(); -sinks.put("sink-1", mysqlSink); - -// 2. 创建GraphExecutor -GraphExecutor executor = new GraphExecutor( - streamGraph, - sources, - operators, - sinks -); - -// 3. 执行 -executor.execute() - .subscribe( - () -> log.info("Graph execution completed"), - error -> log.error("Graph execution failed", error) - ); -``` - -### 内部工作原理 - -```java -/** - * GraphExecutor如何构建Flux链 - */ -private Flux buildFluxForNode(StreamNode node) { - switch (node.getNodeType()) { - case SOURCE: - // 直接从Source读取 - return source.read(); - - case OPERATOR: - // 1. 获取上游节点 - List upstreamNodes = getUpstreamNodes(node); - - // 2. 构建上游Flux - Flux upstreamFlux = mergeUpstreamFluxes(upstreamNodes); - - // 3. 应用当前Operator - Operator operator = operators.get(node.getNodeId()); - return operator.apply(upstreamFlux); - - case SINK: - // Sink节点返回上游Flux - return buildOperatorFlux(node); - } -} -``` - -### 关键特性 - -1. **自动处理DAG拓扑**:根据节点依赖关系自动构建执行顺序 -2. **支持多上游合并**:使用 `Flux.merge()` 合并多个上游数据流 -3. **懒加载执行**:只有订阅时才真正执行 -4. **缓存优化**:相同节点的Flux只构建一次 - -## 二、Pipeline 构建器实现 - -### 简化的Pipeline API - -使用 `PipelineBuilder` 提供流式API: - -```java -// 构建Pipeline -Pipeline pipeline = PipelineBuilder.create() - .name("my-pipeline") - .source(kafkaSource) // 设置Source - .addOperator(parseOperator) // 添加算子1 - .addOperator(filterOperator) // 添加算子2 - .addOperator(aggregateOperator) // 添加算子3 - .sink(mysqlSink) // 设置Sink - .build(); // 构建 - -// 执行Pipeline -pipeline.execute() - .doOnSuccess(result -> { - log.info("Pipeline completed in {} ms", - result.getDuration().toMillis()); - log.info("Processed {} records", - result.getRecordsProcessed()); - }) - .subscribe(); -``` - -### DefaultPipeline 实现原理 - -```java -public class DefaultPipeline implements Pipeline { - - @Override - public Mono execute() { - return Mono.defer(() -> { - // 1. 启动Source和Sink - return source.start() - .then(sink.start()) - // 2. 构建数据流 - .then(executePipeline()) - // 3. 返回执行结果 - .then(Mono.just(createResult())); - }); - } - - private Mono executePipeline() { - // Source读取 - Flux sourceFlux = source.read(); - - // 算子链处理 - Flux processedFlux = operatorChain.execute(sourceFlux); - - // Sink写入 - return sink.write(processedFlux); - } -} -``` - -### 算子链实现 - -```java -public class DefaultOperatorChain implements OperatorChain { - - @Override - public Flux execute(Flux input) { - Flux current = input; - - // 依次应用每个算子 - for (Operator operator : operators) { - current = operator.apply(current); - } - - return (Flux) current; - } -} -``` - -## 三、何时使用 Reactor? - -### 必须使用 Reactor 的场景 ✅ - -#### 1. 数据流处理(核心) -```java -// Source → Operator → Sink 全程响应式 -Flux stream = source.read(); -Flux processed = operator.apply(stream); -Mono written = sink.write(processed); -``` - -#### 2. 外部I/O操作 -```java -// 数据库 -Mono user = r2dbcRepository.findById(id); - -// HTTP请求 -Mono response = webClient.get().retrieve().bodyToMono(Response.class); - -// Kafka -Flux records = kafkaReceiver.receive(); -``` - -#### 3. 异步任务调度 -```java -// JobScheduler -public Mono schedule(Job job, ScheduleConfig config) { - return validateConfig(config) // 异步验证 - .flatMap(valid -> persistSchedule(job, config)) // 异步持久化 - .map(this::toResult); -} -``` - -### 可选使用 Reactor 的场景 ⚠️ - -#### 1. 配置和元数据查询 - -**频繁调用**:建议用 Reactor -```java -public Mono getJobConfig(String jobId) { - return configRepository.findById(jobId); -} -``` - -**低频调用**(如启动时):可以用同步 -```java -@PostConstruct -public void init() { - List configs = configRepository.findAll(); - // 处理配置... -} -``` - -#### 2. 缓存操作 - -**本地缓存**:同步即可 -```java -private final Map localCache = new ConcurrentHashMap<>(); - -public Object get(String key) { - return localCache.get(key); -} -``` - -**分布式缓存**:建议响应式 -```java -public Mono get(String key) { - return reactiveRedisTemplate.opsForValue().get(key); -} -``` - -### 不应使用 Reactor 的场景 ❌ - -#### 1. 纯计算(无I/O) -```java -// ❌ 过度使用 -Mono result = Mono.fromCallable(() -> a + b); - -// ✅ 直接计算 -int result = a + b; -``` - -#### 2. 简单的内存操作 -```java -// ❌ 没必要 -Mono value = Mono.just(map.get(key)); - -// ✅ 直接操作 -String value = map.get(key); -``` - -#### 3. 日志记录 -```java -// ✅ 同步日志 -log.info("Processing data: {}", data); - -// ❌ 过度包装 -Mono.fromRunnable(() -> log.info(...)).subscribe(); -``` - -## 四、MyBatis Plus 使用策略 - -### 为什么同时使用 R2DBC 和 MyBatis Plus? - -``` -R2DBC (响应式) MyBatis Plus (同步) - ↓ ↓ -数据流处理中的查询 配置和元数据管理 -实时指标写入 任务配置CRUD -状态持久化 管理后台API -高并发场景 低频调用场景 -``` - -### MyBatis Plus 使用示例 - -#### 1. 实体类定义 -```java -@Data -@TableName("pipeline_job") -public class JobEntity { - @TableId(value = "id", type = IdType.AUTO) - private Long id; - - @TableField("job_id") - private String jobId; - - @TableField(value = "create_time", fill = FieldFill.INSERT) - private LocalDateTime createTime; - - @TableLogic // 逻辑删除 - private Boolean isDeleted; -} -``` - -#### 2. Mapper接口 -```java -@Mapper -public interface JobMapper extends BaseMapper { - - // 自动继承标准CRUD方法 - // - insert - // - deleteById - // - updateById - // - selectById - // - selectList - - // 自定义查询 - @Select("SELECT * FROM pipeline_job WHERE job_id = #{jobId}") - JobEntity selectByJobId(String jobId); -} -``` - -#### 3. Service层(提供响应式包装) -```java -@Service -public class JobService { - - private final JobMapper jobMapper; - - /** - * 响应式API - 将阻塞调用包装为Mono。 - */ - public Mono getByJobId(String jobId) { - return Mono.fromCallable(() -> jobMapper.selectByJobId(jobId)) - .subscribeOn(Schedulers.boundedElastic()); // 关键:隔离到专用线程池 - } - - /** - * 响应式API - 查询列表。 - */ - public Flux getRunningJobs() { - return Mono.fromCallable(jobMapper::selectRunningJobs) - .flatMapMany(Flux::fromIterable) - .subscribeOn(Schedulers.boundedElastic()); - } - - /** - * 同步API - 用于低频场景。 - */ - public List listByPage(int pageNum, int pageSize) { - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(JobEntity::getIsDeleted, false) - .orderByDesc(JobEntity::getCreateTime); - return jobMapper.selectList(wrapper); - } -} -``` - -### 关键注意事项 - -1. **线程池隔离**:必须使用 `subscribeOn(Schedulers.boundedElastic())` -2. **不要在流处理中频繁调用**:MyBatis的阻塞操作会影响性能 -3. **适合场景**:配置查询、管理API、低频操作 - -## 五、完整示例:构建一个ETL Pipeline - -### 场景:从Kafka读取,转换后写入MySQL - -```java -@Service -public class EtlPipelineExample { - - @Autowired - private KafkaSource kafkaSource; - - @Autowired - private OperatorFactory operatorFactory; - - @Autowired - private MysqlSink mysqlSink; - - public Mono runEtlJob() { - // 1. 创建算子 - Operator parseOperator = - operatorFactory.createOperator(OperatorType.MAP, parseConfig).block(); - - Operator transformOperator = - operatorFactory.createOperator(OperatorType.MAP, transformConfig).block(); - - Operator filterOperator = - operatorFactory.createOperator(OperatorType.FILTER, filterConfig).block(); - - // 2. 构建Pipeline - Pipeline pipeline = PipelineBuilder.create() - .name("kafka-to-mysql-pipeline") - .source(kafkaSource) - .addOperator(parseOperator) // JSON解析 - .addOperator(transformOperator) // 数据转换 - .addOperator(filterOperator) // 数据过滤 - .sink(mysqlSink) - .build(); - - // 3. 执行Pipeline - return pipeline.execute() - .doOnSuccess(result -> { - log.info("ETL completed:"); - log.info("- Duration: {} ms", result.getDuration().toMillis()); - log.info("- Records processed: {}", result.getRecordsProcessed()); - }) - .doOnError(error -> log.error("ETL failed", error)); - } -} -``` - -### 使用GraphExecutor的完整示例 - -```java -@Service -public class GraphExecutionExample { - - public Mono executeComplexPipeline() { - // 1. 构建StreamGraph(通常从数据库加载) - StreamGraph graph = loadGraphFromDatabase(); - - // 2. 准备组件实例 - Map> sources = prepareSources(graph); - Map> operators = prepareOperators(graph); - Map> sinks = prepareSinks(graph); - - // 3. 创建并执行GraphExecutor - GraphExecutor executor = new GraphExecutor(graph, sources, operators, sinks); - - return executor.execute() - .doOnSuccess(() -> log.info("Complex pipeline completed")) - .doOnError(e -> log.error("Pipeline failed", e)) - .then(); - } - - private StreamGraph loadGraphFromDatabase() { - // 从数据库加载graph_definition JSON - String graphJson = jobService.getGraphDefinition(jobId); - return GraphParser.parse(graphJson); - } - - private Map> prepareSources(StreamGraph graph) { - Map> sources = new HashMap<>(); - - for (StreamNode node : graph.getSourceNodes()) { - // 根据配置创建Source - SourceConfig config = parseSourceConfig(node.getConfig()); - Connector connector = connectorRegistry.getConnector(config.getType()).block(); - DataSource source = connector.createSource(config).block(); - sources.put(node.getNodeId(), source); - } - - return sources; - } -} -``` - -## 六、性能优化建议 - -### 1. 使用合适的Scheduler - -```java -// CPU密集型 -flux.publishOn(Schedulers.parallel()) - -// I/O操作 -mono.subscribeOn(Schedulers.boundedElastic()) - -// 单线程(顺序处理) -flux.subscribeOn(Schedulers.single()) -``` - -### 2. 批量处理 - -```java -source.read() - .buffer(1000) // 每1000条批处理 - .flatMap(batch -> sink.writeBatch(Flux.fromIterable(batch), 1000)) - .subscribe(); -``` - -### 3. 背压控制 - -```java -source.read() - .onBackpressureBuffer(10000) // 缓冲区 - .limitRate(100) // 限速 - .subscribe(); -``` - -### 4. 并行处理 - -```java -source.read() - .parallel(4) // 4个并行流 - .runOn(Schedulers.parallel()) // 使用并行调度器 - .map(this::transform) - .sequential() // 合并回单个流 - .subscribe(); -``` - -## 七、调试和监控 - -### 启用日志 - -```java -Flux flux = source.read() - .log("source") // 记录所有信号 - .map(this::transform) - .log("transform") - .subscribe(); -``` - -### 检查点标记 - -```java -flux.checkpoint("after-source") // 标记位置,便于定位错误 - .map(this::transform) - .checkpoint("after-transform") - .subscribe(); -``` - -### 指标收集 - -```java -flux.doOnNext(data -> metrics.recordProcessed(1)) - .doOnError(error -> metrics.recordError()) - .subscribe(); -``` - -## 总结 - -1. **数据流处理**:使用 `GraphExecutor` 或 `PipelineBuilder` 构建响应式Pipeline -2. **响应式原则**:I/O操作必须响应式,纯计算可以同步 -3. **MyBatis Plus**:用于配置管理和低频操作,通过 `Schedulers.boundedElastic()` 隔离 -4. **性能优化**:合理使用批处理、背压和并行度 -5. **监控调试**:使用日志、检查点和指标收集 - -项目已具备完整的响应式流处理能力,可以开始实际业务开发! diff --git a/pipeline-framework/IMPLEMENTATION_SUMMARY.md b/pipeline-framework/IMPLEMENTATION_SUMMARY.md deleted file mode 100644 index d93930261..000000000 --- a/pipeline-framework/IMPLEMENTATION_SUMMARY.md +++ /dev/null @@ -1,401 +0,0 @@ -# Pipeline Framework 实现总结 - -## 📋 完成的工作 - -### 1. ✅ Graph串联实现(GraphExecutor) - -**核心功能**: -- 将DAG图(StreamGraph)转换为可执行的响应式流 -- 自动处理节点依赖关系和拓扑排序 -- 支持多上游合并和分支处理 - -**关键实现**: -```java -GraphExecutor executor = new GraphExecutor(graph, sources, operators, sinks); -executor.execute() // 返回 Mono - .subscribe(); -``` - -**工作原理**: -``` -StreamGraph (DAG定义) - ↓ topologicalSort() -执行顺序节点列表 - ↓ buildFluxForNode() -递归构建每个节点的Flux - ↓ -Source.read() → Operator.apply() → Operator.apply() → Sink.write() - ↓ -完整的响应式流Pipeline -``` - -**文件位置**: -- `/pipeline-core/src/main/java/com/pipeline/framework/core/graph/GraphExecutor.java` - -### 2. ✅ Pipeline构建器实现 - -**核心功能**: -- 提供流式API构建Pipeline -- 自动管理算子链 -- 简化Pipeline创建 - -**使用示例**: -```java -Pipeline pipeline = PipelineBuilder.create() - .name("my-pipeline") - .source(kafkaSource) - .addOperator(mapOperator) - .addOperator(filterOperator) - .sink(mysqlSink) - .build(); - -pipeline.execute().subscribe(); -``` - -**实现文件**: -- `PipelineBuilder.java` - 构建器 -- `DefaultPipeline.java` - Pipeline实现 -- `DefaultOperatorChain.java` - 算子链实现 -- `DefaultPipelineResult.java` - 执行结果 - -### 3. ✅ MyBatis Plus集成 - -**为什么同时使用 R2DBC 和 MyBatis Plus?** - -| 场景 | R2DBC (响应式) | MyBatis Plus (同步) | -|------|----------------|---------------------| -| 数据流处理 | ✅ 使用 | ❌ 不用 | -| 实时指标写入 | ✅ 使用 | ❌ 不用 | -| 状态持久化 | ✅ 使用 | ❌ 不用 | -| 配置管理 | ⚠️ 可选 | ✅ 推荐 | -| 管理后台API | ⚠️ 可选 | ✅ 推荐 | -| 低频查询 | ⚠️ 可选 | ✅ 推荐 | - -**关键实现**: -```java -@Service -public class JobService { - private final JobMapper jobMapper; - - // 响应式API(包装阻塞调用) - public Mono getByJobId(String jobId) { - return Mono.fromCallable(() -> jobMapper.selectByJobId(jobId)) - .subscribeOn(Schedulers.boundedElastic()); // 关键:线程池隔离 - } - - // 同步API(低频场景) - public List listByPage(int page, int size) { - return jobMapper.selectList(wrapper); - } -} -``` - -**实现文件**: -- `JobEntity.java` - 任务实体 -- `JobInstanceEntity.java` - 任务实例实体 -- `JobMapper.java` - 任务Mapper -- `JobInstanceMapper.java` - 实例Mapper -- `MybatisPlusConfig.java` - 配置类 -- `JobService.java` - 服务类(响应式包装) - -### 4. ✅ Reactor使用指南 - -**核心原则**: - -#### 必须使用 Reactor ✅ -- 数据流处理(Source → Operator → Sink) -- 外部I/O操作(数据库、HTTP、Kafka) -- 异步任务调度 -- 状态和检查点管理 - -#### 可选使用 Reactor ⚠️ -- 配置查询(高频用Reactor,低频可同步) -- 缓存操作(分布式用Reactor,本地可同步) - -#### 不应使用 Reactor ❌ -- 纯计算(无I/O) -- 简单内存操作 -- 日志记录 - -**文档位置**: -- `REACTOR_USAGE_GUIDE.md` - 详细指南 - -## 📊 项目统计 - -### 代码文件 -- **Java接口**: 51个 -- **核心实现**: 10个(GraphExecutor、Pipeline相关) -- **实体和Mapper**: 5个(MyBatis Plus相关) -- **配置类**: 2个 - -### 文档 -| 文档名称 | 大小 | 说明 | -|---------|------|------| -| IMPLEMENTATION_GUIDE.md | 14K | 实现指南 | -| REACTOR_USAGE_GUIDE.md | 8.8K | Reactor使用指南 | -| PACKAGE_REFACTORING_SUMMARY.md | 8.8K | 包重构总结 | -| QUICK_START.md | 8.5K | 快速开始 | -| PROJECT_STRUCTURE.md | 11K | 项目结构 | -| PROJECT_SUMMARY.md | 11K | 项目总结 | - -## 🎯 核心设计决策 - -### 1. 响应式流处理 - -**决策**:整个数据流处理链路完全响应式 - -**理由**: -- 支持背压控制 -- 高效处理大数据量 -- 非阻塞I/O -- 易于组合和转换 - -**实现**: -```java -Flux dataFlow = source.read() // 响应式读取 - .transform(operatorChain::execute) // 响应式转换 - .as(sink::write); // 响应式写入 -``` - -### 2. 双数据库策略 - -**决策**:R2DBC + MyBatis Plus 混合使用 - -**理由**: -- R2DBC:适合高并发、流处理 -- MyBatis Plus:适合配置管理、复杂查询、已有代码库 - -**实现**: -```yaml -spring: - r2dbc: - url: r2dbc:mysql://... - datasource: - url: jdbc:mysql://... -``` - -### 3. GraphExecutor vs PipelineBuilder - -**两种方式对比**: - -| 特性 | GraphExecutor | PipelineBuilder | -|------|---------------|-----------------| -| 使用场景 | 动态图定义 | 静态Pipeline | -| 灵活性 | 高(支持复杂DAG) | 中(单链路) | -| 易用性 | 中(需理解Graph) | 高(流式API) | -| 性能 | 相同 | 相同 | -| 适用于 | 从数据库加载配置 | 代码直接构建 | - -**何时使用GraphExecutor**: -```java -// 场景1:从数据库加载任务定义 -StreamGraph graph = loadGraphFromDB(jobId); -GraphExecutor executor = new GraphExecutor(graph, sources, operators, sinks); -executor.execute().subscribe(); - -// 场景2:复杂的DAG,有分支和合并 -// Source1 ─┐ -// ├→ Operator → Sink -// Source2 ─┘ -``` - -**何时使用PipelineBuilder**: -```java -// 场景1:简单的线性Pipeline -Pipeline pipeline = PipelineBuilder.create() - .source(source) - .addOperator(op1) - .addOperator(op2) - .sink(sink) - .build(); - -// 场景2:代码中快速构建测试Pipeline -``` - -## 🔧 关键技术点 - -### 1. 线程池隔离 - -**问题**:MyBatis的阻塞操作会阻塞Reactor的事件循环 - -**解决**: -```java -Mono.fromCallable(() -> blockingOperation()) - .subscribeOn(Schedulers.boundedElastic()) // 隔离到专用线程池 -``` - -### 2. 背压处理 - -**问题**:Source生产速度 > Sink消费速度 - -**解决**: -```java -source.read() - .onBackpressureBuffer(10000) // 缓冲区 - .limitRate(100) // 限速 - .as(sink::write) -``` - -### 3. 错误处理 - -**问题**:某个数据处理失败不应导致整个流中断 - -**解决**: -```java -flux.onErrorContinue((error, data) -> { - log.error("Error processing: {}", data, error); - // 继续处理下一个 -}) -.retryWhen(Retry.backoff(3, Duration.ofSeconds(1))) -``` - -### 4. 资源管理 - -**问题**:确保Source和Sink正确关闭 - -**解决**: -```java -public Mono execute() { - return Mono.using( - () -> { - source.start().block(); - sink.start().block(); - return new Resource(source, sink); - }, - resource -> executePipeline(), - resource -> cleanup(resource) - ); -} -``` - -## 📝 使用示例 - -### 示例1:简单的Kafka到MySQL - -```java -// 1. 创建组件 -KafkaSource source = new KafkaSource<>(kafkaConfig); -MapOperator parser = new JsonParseOperator(); -MysqlSink sink = new MysqlSink<>(dbConfig); - -// 2. 构建Pipeline -Pipeline pipeline = PipelineBuilder.create() - .source(source) - .addOperator(parser) - .sink(sink) - .build(); - -// 3. 执行 -pipeline.execute() - .doOnSuccess(result -> - log.info("Processed {} records", result.getRecordsProcessed())) - .subscribe(); -``` - -### 示例2:复杂的DAG处理 - -```java -// 1. 从数据库加载Graph定义 -StreamGraph graph = graphService.loadGraph(jobId).block(); - -// 2. 准备组件 -Map> sources = connectorService.createSources(graph); -Map> operators = operatorFactory.createOperators(graph); -Map> sinks = connectorService.createSinks(graph); - -// 3. 执行 -GraphExecutor executor = new GraphExecutor(graph, sources, operators, sinks); -executor.execute().subscribe(); -``` - -### 示例3:使用MyBatis Plus管理配置 - -```java -@Service -public class JobManagementService { - - @Autowired - private JobService jobService; - - // 响应式API - public Mono getJob(String jobId) { - return jobService.getByJobId(jobId); - } - - // 同步API(管理后台) - @GetMapping("/jobs") - public List listJobs(@RequestParam int page, - @RequestParam int size) { - return jobService.listByPage(page, size); - } -} -``` - -## 🚀 后续开发建议 - -### 阶段1:基础实现(当前)✅ -- [x] 核心接口设计 -- [x] GraphExecutor实现 -- [x] Pipeline构建器 -- [x] MyBatis Plus集成 - -### 阶段2:连接器实现(下一步) -- [ ] KafkaSource/KafkaSink -- [ ] JdbcSource/JdbcSink -- [ ] HttpSource/HttpSink -- [ ] FileSource/FileSink -- [ ] RedisSource/RedisSink - -### 阶段3:算子实现 -- [ ] MapOperator -- [ ] FilterOperator -- [ ] FlatMapOperator -- [ ] AggregateOperator -- [ ] WindowOperator -- [ ] JoinOperator - -### 阶段4:高级特性 -- [ ] 状态管理实现 -- [ ] 检查点实现 -- [ ] Job调度器 -- [ ] Job执行器 -- [ ] 指标收集 - -### 阶段5:Web UI -- [ ] RESTful API -- [ ] 任务管理界面 -- [ ] 监控Dashboard -- [ ] 配置管理 - -## 📚 相关文档 - -### 核心文档 -- `IMPLEMENTATION_GUIDE.md` - **实现指南**(必读) -- `REACTOR_USAGE_GUIDE.md` - **Reactor使用指南**(必读) -- `QUICK_START.md` - 快速开始 -- `PACKAGE_REFACTORING_SUMMARY.md` - 包重构总结 - -### 参考文档 -- `PROJECT_STRUCTURE.md` - 项目结构说明 -- `BUILD_AND_RUN.md` - 构建和运行 -- `CONTRIBUTING.md` - 贡献指南 - -## 🎉 总结 - -项目现已具备: - -1. **完整的响应式流处理能力** - GraphExecutor + PipelineBuilder -2. **清晰的架构设计** - 接口定义完善,模块划分清晰 -3. **灵活的数据库策略** - R2DBC + MyBatis Plus 混合使用 -4. **详细的文档** - 9个文档,总计70KB -5. **最佳实践指南** - Reactor使用指南、性能优化建议 - -**可以开始实际业务开发了!** 🚀 - -重点是: -- 实现具体的Connector(Kafka、JDBC等) -- 实现常用的Operator(Map、Filter等) -- 完善Job调度和执行逻辑 -- 添加监控和告警 - -项目基础架构已完备,后续开发将会很顺畅! diff --git a/pipeline-framework/NAMING_REFACTORING.md b/pipeline-framework/NAMING_REFACTORING.md deleted file mode 100644 index 6c25baef4..000000000 --- a/pipeline-framework/NAMING_REFACTORING.md +++ /dev/null @@ -1,283 +0,0 @@ -# 命名重构说明 - -## 🎯 重构目标 - -1. **去掉 "Spring" 前缀**:类名更简洁,不体现技术栈 -2. **使用 Spring 自动装配**:配置类使用 @ConfigurationProperties 等注解 -3. **Adapter 模式**:配置转换使用适配器模式 - ---- - -## 📋 类名重构对照表 - -### Factory 类 - -| 旧名称 | 新名称 | 说明 | -|-------|--------|-----| -| `SpringSourceFactory` | `SourceFactory` | 去掉 Spring 前缀 | -| `SpringSinkFactory` | `SinkFactory` | 去掉 Spring 前缀 | -| `SpringOperatorFactory` | `OperatorFactory` | 去掉 Spring 前缀 | - -### Builder 类 - -| 旧名称 | 新名称 | 说明 | -|-------|--------|-----| -| `SpringGraphBasedPipelineBuilder` | `GraphPipelineBuilder` | 去掉 Spring 前缀,简化名称 | - -### Config 类(改用 Adapter) - -| 旧名称 | 新名称 | 说明 | -|-------|--------|-----| -| `SimpleSourceConfig` | `SourceConfigAdapter` | 使用适配器模式 | -| `SimpleOperatorConfig` | `OperatorConfigAdapter` | 使用适配器模式 | -| `SimpleSinkConfig` | `SinkConfigAdapter` | 使用适配器模式 | - -### Configuration 类 - -| 旧名称 | 新名称 | 说明 | -|-------|--------|-----| -| `ReactorSchedulerConfig` | `ReactorSchedulerConfiguration` | 使用 Configuration 后缀 | - -### 目录结构 - -| 旧路径 | 新路径 | 说明 | -|-------|--------|-----| -| `.../core/config/` | `.../core/scheduler/` | 调整目录结构 | - ---- - -## 🏗️ 架构改进 - -### 1. 配置类改用适配器模式 - -**改造前**(SimpleSourceConfig 等): -```java -public class SimpleSourceConfig implements SourceConfig { - private final Map properties; - - public SimpleSourceConfig(Map properties) { - this.properties = new HashMap<>(properties); - } - // ... -} -``` - -**改造后**(SourceConfigAdapter): -```java -public class SourceConfigAdapter implements SourceConfig { - private final Map properties; - - private SourceConfigAdapter(Map properties) { - this.properties = new HashMap<>(properties); - } - - // 静态工厂方法,更清晰的意图 - public static SourceConfig from(StreamNode node) { - return new SourceConfigAdapter(node.getConfig()); - } - // ... -} -``` - -**优势**: -- ✅ 清晰表达"适配"的意图 -- ✅ 私有构造函数 + 静态工厂方法 -- ✅ 符合适配器模式 - -### 2. Spring 配置自动装配 - -**ReactorSchedulerConfiguration**: -```java -@Configuration -@EnableConfigurationProperties(ReactorSchedulerProperties.class) -public class ReactorSchedulerConfiguration { - - @Bean(name = "ioScheduler", destroyMethod = "dispose") - public Scheduler ioScheduler(ReactorSchedulerProperties properties) { - // Spring 自动注入 properties - ReactorSchedulerProperties.SchedulerConfig ioConfig = properties.getIo(); - return Schedulers.newBoundedElastic(...); - } -} -``` - -**ReactorSchedulerProperties**: -```java -@Component -@ConfigurationProperties(prefix = "reactor.scheduler") -public class ReactorSchedulerProperties { - private SchedulerConfig io = new SchedulerConfig(); - private SchedulerConfig compute = new SchedulerConfig(); - // Spring 自动绑定配置 -} -``` - -**application.yml**: -```yaml -reactor: - scheduler: - io: - pool-size: 100 - queue-size: 1000 -``` - -**优势**: -- ✅ Spring 自动绑定配置 -- ✅ 类型安全 -- ✅ IDE 自动补全 -- ✅ 支持配置校验 - ---- - -## 📁 目录结构变化 - -### 改造前 -``` -pipeline-core/src/main/java/com/pipeline/framework/core/ -├── builder/ -│ ├── SpringGraphBasedPipelineBuilder.java -│ ├── SimpleSourceConfig.java -│ ├── SimpleOperatorConfig.java -│ └── SimpleSinkConfig.java -├── config/ -│ ├── ReactorSchedulerConfig.java -│ └── ReactorSchedulerProperties.java -└── factory/ - ├── SpringSourceFactory.java - ├── SpringSinkFactory.java - └── SpringOperatorFactory.java -``` - -### 改造后 -``` -pipeline-core/src/main/java/com/pipeline/framework/core/ -├── builder/ -│ ├── GraphPipelineBuilder.java ✅ -│ ├── SourceConfigAdapter.java ✅ -│ ├── OperatorConfigAdapter.java ✅ -│ └── SinkConfigAdapter.java ✅ -├── scheduler/ ✅ (新目录) -│ ├── ReactorSchedulerConfiguration.java ✅ -│ └── ReactorSchedulerProperties.java -└── factory/ - ├── SourceFactory.java ✅ - ├── SinkFactory.java ✅ - └── OperatorFactory.java ✅ -``` - ---- - -## 🔄 使用示例 - -### Factory 使用 - -```java -@Service -public class PipelineService { - - private final SourceFactory sourceFactory; // 不再是 SpringSourceFactory - - public PipelineService(SourceFactory sourceFactory) { - this.sourceFactory = sourceFactory; - } - - public Mono> createSource(StreamNode node) { - SourceConfig config = SourceConfigAdapter.from(node); // 使用 Adapter - return sourceFactory.createSource(config); - } -} -``` - -### Builder 使用 - -```java -@Service -public class ExecutionService { - - private final GraphPipelineBuilder builder; // 不再是 SpringGraphBasedPipelineBuilder - - public ExecutionService(GraphPipelineBuilder builder) { - this.builder = builder; - } - - public Mono> buildPipeline(StreamGraph graph) { - return builder.buildFromGraph(graph); - } -} -``` - -### 配置使用 - -```java -@Component -public class MyComponent { - - private final Scheduler ioScheduler; - - public MyComponent(@Qualifier("ioScheduler") Scheduler ioScheduler) { - this.ioScheduler = ioScheduler; - } -} -``` - ---- - -## ✅ 改进总结 - -### 命名改进 - -- ✅ **去掉技术栈前缀**:`SpringSourceFactory` → `SourceFactory` -- ✅ **使用业务术语**:更关注"做什么"而不是"用什么" -- ✅ **简洁明了**:类名更短、更清晰 - -### 架构改进 - -- ✅ **适配器模式**:配置转换使用 `XXXAdapter.from()` 静态工厂 -- ✅ **Spring 自动装配**:配置类使用 `@ConfigurationProperties` -- ✅ **职责分离**:Builder 负责构建,Adapter 负责转换 - -### 代码质量 - -- ✅ **可读性**:类名更简洁,意图更清晰 -- ✅ **可维护性**:目录结构更合理 -- ✅ **可扩展性**:符合设计模式 - ---- - -## 📚 相关文档 - -- `FINAL_REFACTORING_SUMMARY.md` - 终极重构总结 -- `REFACTORING_ARCHITECTURE.md` - 架构重构说明 -- `DESIGN_PATTERN_EXPLANATION.md` - 设计模式详解 - ---- - -## 🎓 命名原则 - -### 应该遵循的原则 - -1. **业务导向**:类名反映业务意图,不体现技术栈 -2. **简洁明了**:去掉冗余前缀/后缀 -3. **一致性**:同类型的类使用统一的命名风格 -4. **可读性**:让人一眼能看懂类的用途 - -### 应该避免的命名 - -- ❌ `SpringXXX`:不要在类名中体现技术栈 -- ❌ `SimpleXXX`:Simple 没有实际意义 -- ❌ `XXXImpl`:实现类尽量用更具体的名字 -- ❌ `XXXConfig`:配置类用 Adapter、Properties 等更准确的术语 - -### 推荐的命名 - -- ✅ `XXXFactory`:工厂类 -- ✅ `XXXBuilder`:建造者类 -- ✅ `XXXAdapter`:适配器类 -- ✅ `XXXConfiguration`:Spring 配置类 -- ✅ `XXXProperties`:配置属性类 -- ✅ `XXXExecutor`:执行器类 -- ✅ `XXXRegistry`:注册表类 - ---- - -**重构完成!代码更简洁、更清晰、更符合业务语义!** ✅ diff --git a/pipeline-framework/PACKAGE_REFACTORING_SUMMARY.md b/pipeline-framework/PACKAGE_REFACTORING_SUMMARY.md deleted file mode 100644 index ff6e123e5..000000000 --- a/pipeline-framework/PACKAGE_REFACTORING_SUMMARY.md +++ /dev/null @@ -1,349 +0,0 @@ -# Pipeline Framework 包结构重构总结 - -## 重构概览 - -**完成时间**: 2025-11-10 -**重构范围**: 全部模块 -**重构类型**: 包结构统一 + 响应式接口设计 - -## 主要变更 - -### 1. 包结构统一 ✅ - -**之前的问题**: -- 包结构混乱,同时存在多个包路径 -- `com.etl.pipeline.api.*`(旧) -- `com.pipeline.framework.*`(部分新) -- 包引用不一致导致编译错误 - -**统一后的包结构**: -``` -com.pipeline.framework -├── api # API模块 -│ ├── source # 数据源接口 -│ ├── operator # 算子接口 -│ ├── sink # 数据输出接口 -│ ├── job # 任务接口 -│ ├── graph # 流图接口 -│ ├── scheduler # 调度器接口 -│ └── executor # 执行器接口 -├── core # 核心模块 -│ ├── runtime # 运行时 -│ └── pipeline # Pipeline实现 -├── connectors # 连接器模块 -├── operators # 算子模块 -├── state # 状态管理模块 -├── checkpoint # 检查点模块 -└── metrics # 指标模块 -``` - -### 2. 响应式接口设计 ✅ - -所有接口都基于 **Project Reactor** 重新设计: - -#### 核心原则: -- ✅ 所有I/O操作返回 `Mono` 或 `Flux` -- ✅ 支持背压(Backpressure) -- ✅ 非阻塞操作 -- ✅ 异步优先 - -#### 关键改进: - -**DataSource 接口**: -```java -// 之前 -T read(); - -// 现在 -Flux read(); // 响应式流 -Mono start(); // 异步启动 -Mono healthCheck(); // 异步健康检查 -``` - -**DataSink 接口**: -```java -// 之前 -void write(T data); - -// 现在 -Mono write(Flux data); // 响应式写入 -Mono writeBatch(Flux data, int batchSize); // 批量写入 -Mono flush(); // 异步刷新 -``` - -**Operator 接口**: -```java -// 保持响应式 -Flux apply(Flux input); // 流转换 -``` - -**JobScheduler 接口**: -```java -// 之前 -ScheduleResult schedule(Job job, ScheduleConfig config); - -// 现在 -Mono schedule(Job job, ScheduleConfig config); -Flux getScheduledJobs(); // 响应式流 -``` - -**JobExecutor 接口**: -```java -// 全部异步化 -Mono submit(Job job); -Mono stop(String jobId); -Flux getMetrics(String jobId); -``` - -**State 接口**: -```java -// 之前 -T get(); -void update(T value); - -// 现在 -Mono get(); // 异步获取 -Mono update(T value); // 异步更新 -Mono compareAndSet(...); // CAS操作 -``` - -**Connector 接口**: -```java -// 之前 - DataSource createSource(SourceConfig config); - -// 现在 - Mono> createSource(SourceConfig config); // 异步创建 -Mono validateConfig(Object config); -Mono healthCheck(); -``` - -## 重构后的接口清单 - -### pipeline-api 模块(33个接口/类) - -#### Source相关(3个) -- `DataSource` - 数据源接口 -- `SourceConfig` - 数据源配置 -- `SourceType` - 数据源类型枚举 - -#### Operator相关(3个) -- `Operator` - 算子接口 -- `OperatorConfig` - 算子配置 -- `OperatorType` - 算子类型枚举 - -#### Sink相关(3个) -- `DataSink` - 数据输出接口 -- `SinkConfig` - 输出配置 -- `SinkType` - 输出类型枚举 - -#### Job相关(5个) -- `Job` - 任务接口 -- `JobConfig` - 任务配置 -- `JobType` - 任务类型枚举 -- `JobStatus` - 任务状态枚举 -- `RestartStrategy` - 重启策略枚举 - -#### Graph相关(5个) -- `StreamGraph` - 流图接口 -- `StreamNode` - 流节点接口 -- `StreamEdge` - 流边接口 -- `NodeType` - 节点类型枚举 -- `PartitionStrategy` - 分区策略枚举 - -#### Scheduler相关(5个) -- `JobScheduler` - 任务调度器接口 -- `ScheduleConfig` - 调度配置接口 -- `ScheduleType` - 调度类型枚举 -- `ScheduleStatus` - 调度状态接口 -- `ScheduleResult` - 调度结果接口 - -#### Executor相关(4个) -- `JobExecutor` - 任务执行器接口 -- `JobResult` - 执行结果接口 -- `ExecutionStatus` - 执行状态枚举 -- `ExecutionMetrics` - 执行指标接口 - -### pipeline-core 模块(5个) -- `RuntimeContext` - 运行时上下文 -- `RuntimeMetrics` - 运行时指标 -- `Pipeline` - Pipeline接口 -- `OperatorChain` - 算子链接口 -- `PipelineResult` - Pipeline执行结果 - -### pipeline-connectors 模块(2个) -- `Connector` - 连接器接口 -- `ConnectorRegistry` - 连接器注册中心 - -### pipeline-state 模块(2个) -- `State` - 状态接口 -- `StateManager` - 状态管理器 - -### pipeline-checkpoint 模块(4个) -- `Checkpoint` - 检查点接口 -- `CheckpointType` - 检查点类型枚举 -- `CheckpointCoordinator` - 检查点协调器 -- `CheckpointStorage` - 检查点存储 - -### pipeline-operators 模块(2个) -- `OperatorFactory` - 算子工厂 -- `OperatorCreator` - 算子创建器 - -### pipeline-metrics 模块(2个) -- `MetricsCollector` - 指标收集器 -- `MetricsReporter` - 指标报告器 - -## 响应式设计模式应用 - -### 1. 异步操作 (Mono) -所有可能阻塞的操作都返回 `Mono`: -- 启动/停止操作 -- 配置验证 -- 健康检查 -- 数据库操作 -- 网络I/O - -### 2. 流式处理 (Flux) -所有数据流都使用 `Flux`: -- 数据源读取: `Flux read()` -- 算子转换: `Flux apply(Flux input)` -- 数据输出: `Mono write(Flux data)` -- 指标推送: `Flux publishMetrics(Duration interval)` -- 检查点调度: `Flux scheduleCheckpoints(Duration interval)` - -### 3. 背压支持 -所有流式接口天然支持背压: -```java -// Source自动适应下游处理速度 -Flux read() - -// Sink告知上游处理能力 -Mono write(Flux data) -``` - -### 4. 组合操作 -接口支持响应式组合: -```java -source.read() - .transform(operator::apply) - .as(sink::write) - .subscribe(); -``` - -## 模块依赖关系 - -``` -pipeline-api (核心API,无依赖) - ↑ - ├── pipeline-core (依赖 api, state, checkpoint) - ├── pipeline-connectors (依赖 api) - ├── pipeline-operators (依赖 api) - ├── pipeline-scheduler (依赖 api) - ├── pipeline-executor (依赖 api, core, state, checkpoint) - ├── pipeline-state (依赖 api) - ├── pipeline-checkpoint (依赖 api, state) - ├── pipeline-metrics (依赖 api) - ├── pipeline-web (依赖 api, scheduler, executor) - └── pipeline-starter (依赖所有模块) -``` - -## Reactor依赖 - -所有模块都依赖 Project Reactor: -```xml - - io.projectreactor - reactor-core - 3.6.0 - -``` - -## 编译验证 - -虽然环境中没有Maven,但项目结构和依赖配置已正确: - -- ✅ 所有接口使用统一包名 `com.pipeline.framework` -- ✅ 所有响应式方法返回 `Mono` 或 `Flux` -- ✅ POM文件配置正确 -- ✅ 模块依赖关系清晰 -- ✅ 符合Java 17和Google Java Style - -## 下一步建议 - -### 1. 实现核心接口 -优先实现以下接口: -- `DataSource` 的内存实现(测试用) -- `DataSink` 的日志实现(测试用) -- 基础 `Operator` 实现(Map、Filter) -- `Pipeline` 默认实现 -- `OperatorChain` 默认实现 - -### 2. 实现连接器 -- JDBC Connector -- Kafka Connector -- HTTP Connector -- File Connector - -### 3. 实现状态和检查点 -- 内存状态存储 -- 文件检查点存储 -- 数据库检查点存储 - -### 4. 实现调度和执行 -- Cron调度器 -- Job执行器 -- 指标收集 - -## 响应式编程最佳实践 - -### 1. 永远不要阻塞 -```java -// ❌ 错误 -public Mono getData() { - Data data = blockingCall(); // 不要这样 - return Mono.just(data); -} - -// ✅ 正确 -public Mono getData() { - return Mono.fromCallable(() -> blockingCall()) - .subscribeOn(Schedulers.boundedElastic()); -} -``` - -### 2. 使用适当的Scheduler -```java -// CPU密集型 -.publishOn(Schedulers.parallel()) - -// I/O操作 -.subscribeOn(Schedulers.boundedElastic()) -``` - -### 3. 处理错误 -```java -flux.onErrorResume(error -> { - log.error("Error occurred", error); - return Flux.empty(); -}) -``` - -### 4. 资源管理 -```java -Flux.using( - () -> openResource(), - resource -> processResource(resource), - resource -> closeResource(resource) -) -``` - -## 总结 - -本次重构完成了: -1. ✅ 统一包结构为 `com.pipeline.framework` -2. ✅ 所有接口基于 Project Reactor 重新设计 -3. ✅ 支持完整的响应式流处理 -4. ✅ 清晰的模块依赖关系 -5. ✅ 符合响应式编程最佳实践 - -项目现在拥有一个健壮的、完全响应式的API设计,可以支持高性能、低延迟的数据处理需求。 diff --git a/pipeline-framework/PROJECT_SUMMARY.md b/pipeline-framework/PROJECT_SUMMARY.md deleted file mode 100644 index 0ac457403..000000000 --- a/pipeline-framework/PROJECT_SUMMARY.md +++ /dev/null @@ -1,350 +0,0 @@ -# Pipeline Framework 项目总结 - -## 项目概览 - -**项目名称**: Pipeline Framework -**版本**: 1.0.0-SNAPSHOT -**技术栈**: Java 17, Spring Boot 3.2.0, Project Reactor 3.6.0, MySQL 8.0, Maven -**架构模式**: 响应式流处理、微内核、插件化 - -## 已完成工作 - -### 1. 项目重命名 ✅ - -- 将项目从 `reactive-etl-framework` 重命名为 `pipeline-framework` -- 更新所有包名:`com.etl.framework` → `com.pipeline.framework` -- 更新所有模块名:`etl-*` → `pipeline-*` -- 更新所有配置文件和Docker服务名称 - -### 2. Maven多模块项目结构 ✅ - -已创建完整的Maven多模块项目,共11个子模块: - -#### 核心模块 -- **pipeline-api**: 核心API接口和契约定义(30个接口) -- **pipeline-core**: 核心实现(Pipeline、OperatorChain、RuntimeContext等) -- **pipeline-connectors**: 连接器实现(Connector注册、管理) -- **pipeline-operators**: 数据转换算子(OperatorFactory、OperatorCreator) - -#### 调度与执行 -- **pipeline-scheduler**: 任务调度(Schedule、ScheduleType) -- **pipeline-executor**: 任务执行引擎(ExecutionPlan、ExecutionContext、ExecutionResult) - -#### 状态与检查点 -- **pipeline-state**: 状态管理(State、StateManager) -- **pipeline-checkpoint**: 检查点管理(Checkpoint、CheckpointCoordinator、CheckpointStorage) - -#### 监控与Web -- **pipeline-metrics**: 指标收集(MetricsCollector、MetricsReporter) -- **pipeline-web**: RESTful API和Web界面 -- **pipeline-starter**: Spring Boot启动器 - -### 3. 核心接口定义 ✅ - -已生成51个Java接口文件,覆盖所有核心功能: - -#### API模块 (pipeline-api) -- **Source**: DataSource, SourceConfig, SourceType, SourceException -- **Operator**: Operator, OperatorConfig, OperatorType -- **Sink**: DataSink, SinkConfig, SinkType, SinkException -- **Job**: Job, JobConfig, JobType, JobStatus -- **Graph**: StreamGraph, StreamNode, StreamEdge, NodeType, JobGraph -- **Scheduler**: JobScheduler, ScheduleConfig -- **Executor**: JobExecutor - -#### Core模块 (pipeline-core) -- RuntimeContext, RuntimeMetrics -- Pipeline, OperatorChain, PipelineResult - -#### Connectors模块 -- Connector, ConnectorRegistry - -#### State模块 -- State, StateManager - -#### Checkpoint模块 -- Checkpoint, CheckpointCoordinator, CheckpointStorage - -#### Metrics模块 -- MetricsCollector, MetricsReporter - -#### Scheduler模块 -- Schedule, ScheduleType - -#### Executor模块 -- ExecutionPlan, ExecutionContext, ExecutionResult - -#### Operators模块 -- OperatorFactory, OperatorCreator - -### 4. 数据库Migration脚本 ✅ - -已创建8个Flyway数据库迁移脚本,共9张核心表: - -#### V1__Create_job_tables.sql -- `pipeline_job`: 任务定义表 -- `pipeline_job_instance`: 任务实例表 -- `pipeline_job_schedule`: 任务调度配置表 - -#### V2__Create_graph_tables.sql -- `pipeline_stream_graph`: StreamGraph定义表 - -#### V3__Create_connector_tables.sql -- `pipeline_connector`: 连接器注册表 -- `pipeline_datasource`: 数据源配置表 - -#### V4__Create_checkpoint_tables.sql -- `pipeline_checkpoint`: 检查点表 - -#### V5__Create_metrics_tables.sql -- `pipeline_job_metrics`: 任务运行指标表 - -#### V6__Create_config_alert_tables.sql -- `pipeline_system_config`: 系统配置表 -- `pipeline_alert_rule`: 告警规则表 -- `pipeline_alert_record`: 告警记录表 - -#### V7__Insert_initial_data.sql -- 插入6个内置连接器(JDBC, Kafka, HTTP, File, Redis, Elasticsearch) -- 插入11项系统配置 -- 插入4个默认告警规则 - -#### V8__Create_views.sql -- `v_job_instance_stats`: 任务实例统计视图 -- `v_running_jobs`: 当前运行任务视图 - -### 5. Docker服务编排 ✅ - -docker-compose.yml包含以下服务: -- MySQL 8.0 (pipeline-mysql) -- Zookeeper (pipeline-zookeeper) -- Kafka (pipeline-kafka) -- Redis (pipeline-redis) -- Prometheus (pipeline-prometheus) -- Grafana (pipeline-grafana) -- Pipeline Framework App (pipeline-framework) - -### 6. 配置文件 ✅ - -- application.yml: 基础配置 -- application-dev.yml: 开发环境配置(含Flyway配置) -- application-prod.yml: 生产环境配置(含Flyway配置) -- logback-spring.xml: 日志配置 -- prometheus.yml: Prometheus监控配置 - -## 项目统计 - -| 指标 | 数量 | -|------|------| -| Maven模块 | 11个 + 1个父POM | -| Java接口文件 | 51个 | -| POM文件 | 12个 | -| Migration脚本 | 8个 | -| 数据库表 | 11张 | -| 数据库视图 | 2个 | -| Docker服务 | 7个 | - -## 项目目录结构 - -``` -pipeline-framework/ -├── pom.xml # 父POM -├── docker-compose.yml # Docker服务编排 -├── Dockerfile # 应用Dockerfile -├── .dockerignore -├── .gitignore -├── README.md -├── CONTRIBUTING.md -├── PROJECT_STRUCTURE.md -├── BUILD_AND_RUN.md -├── monitoring/ -│ └── prometheus.yml # Prometheus配置 -├── pipeline-api/ # API接口模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/api/ -│ ├── source/ # Source接口 -│ ├── operator/ # Operator接口 -│ ├── sink/ # Sink接口 -│ ├── job/ # Job接口 -│ ├── graph/ # Graph接口 -│ ├── scheduler/ # Scheduler接口 -│ └── executor/ # Executor接口 -├── pipeline-core/ # 核心实现模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/core/ -│ ├── runtime/ # 运行时上下文 -│ └── pipeline/ # Pipeline实现 -├── pipeline-connectors/ # 连接器模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/connectors/ -├── pipeline-operators/ # 算子模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/operators/ -├── pipeline-scheduler/ # 调度器模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/scheduler/ -├── pipeline-executor/ # 执行器模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/executor/ -├── pipeline-state/ # 状态管理模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/state/ -├── pipeline-checkpoint/ # 检查点模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/checkpoint/ -├── pipeline-metrics/ # 指标模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/metrics/ -├── pipeline-web/ # Web API模块 -│ ├── pom.xml -│ └── src/main/java/com/pipeline/framework/web/ -└── pipeline-starter/ # 启动器模块 - ├── pom.xml - └── src/main/ - ├── java/com/pipeline/framework/ - │ └── PipelineFrameworkApplication.java - └── resources/ - ├── application.yml - ├── application-dev.yml - ├── application-prod.yml - ├── logback-spring.xml - └── db/migration/ # Flyway迁移脚本 - ├── V1__Create_job_tables.sql - ├── V2__Create_graph_tables.sql - ├── V3__Create_connector_tables.sql - ├── V4__Create_checkpoint_tables.sql - ├── V5__Create_metrics_tables.sql - ├── V6__Create_config_alert_tables.sql - ├── V7__Insert_initial_data.sql - └── V8__Create_views.sql -``` - -## 设计原则与规范 - -### 代码规范 -- ✅ Java 17 -- ✅ Google Java Style -- ✅ 广泛使用泛型 -- ✅ 所有公共方法包含JavaDoc -- ✅ SLF4J日志 -- ✅ 优先使用组合而非继承 -- ✅ 提供有意义的错误信息 - -### 设计模式(已应用于接口设计) -**必须使用**: -- ✅ Builder模式: 复杂对象构建 -- ✅ Factory模式: OperatorFactory, ConnectorRegistry -- ✅ Strategy模式: Operator, DataSource, DataSink接口 -- ✅ Observer模式: MetricsCollector, CheckpointCoordinator -- ✅ Template方法: 流程定义 - -**推荐使用**: -- 装饰器模式: 功能增强 -- 责任链模式: OperatorChain -- 访问者模式: 结构操作 -- 状态模式: JobStatus, JobType枚举 - -## 技术特性 - -### 响应式编程 -- 基于Project Reactor -- 非阻塞I/O -- 背压支持 -- Flux/Mono API - -### 数据库 -- R2DBC响应式数据库访问 -- Flyway数据库版本管理 -- MySQL 8.0+ -- JSON字段支持 - -### 监控与可观测性 -- Micrometer指标 -- Prometheus集成 -- Grafana可视化 -- Spring Boot Actuator - -### 容器化 -- Docker支持 -- Docker Compose本地开发 -- 多阶段构建优化 - -## 快速开始 - -### 1. 构建项目 - -```bash -cd /workspace/pipeline-framework -mvn clean install -DskipTests -``` - -### 2. 启动Docker服务 - -```bash -docker-compose up -d -``` - -### 3. 运行应用 - -```bash -mvn spring-boot:run -pl pipeline-starter -``` - -### 4. 访问服务 - -- 应用: http://localhost:8080 -- Actuator: http://localhost:8080/actuator -- Prometheus: http://localhost:9090 -- Grafana: http://localhost:3000 - -## 数据库连接信息 - -**开发环境**: -- Host: localhost:3306 -- Database: pipeline_framework -- Username: root -- Password: root123456 - -**Flyway自动执行**: -- 应用启动时自动运行迁移脚本 -- 创建所有必需的表和初始数据 - -## 下一步计划 - -### Phase 1: 基础实现(当前阶段) -- ✅ 项目结构搭建 -- ✅ 核心接口定义 -- ✅ 数据库表结构设计 -- ⏳ 核心功能实现(待开发) - -### Phase 2: 核心功能 -- 状态管理实现 -- 检查点机制 -- 基本连接器(JDBC, Kafka) -- 基本算子(Map, Filter, Window) - -### Phase 3: 高级特性 -- 高级连接器 -- 复杂算子 -- 监控Dashboard -- 完整的Web UI - -## 参考文档 - -详细设计文档位于 `/workspace/docs/`: -- reactive-etl-framework-design.md: 架构设计文档 -- database-design.md: 数据库设计文档 -- database-schema.sql: 原始SQL脚本 -- graph-definition-examples.md: 图定义示例 -- json-examples-guide.md: JSON配置指南 - -## 总结 - -Pipeline Framework项目骨架已成功搭建完成,包括: -1. ✅ 完整的Maven多模块结构 -2. ✅ 51个核心接口定义 -3. ✅ 8个Flyway数据库迁移脚本 -4. ✅ Docker服务编排 -5. ✅ Spring Boot配置 - -项目现在可以开始实际功能开发,所有基础架构和接口契约已就绪。 diff --git a/pipeline-framework/REACTOR_DECISION_GUIDE.md b/pipeline-framework/REACTOR_DECISION_GUIDE.md deleted file mode 100644 index 416924523..000000000 --- a/pipeline-framework/REACTOR_DECISION_GUIDE.md +++ /dev/null @@ -1,706 +0,0 @@ -# Reactor 使用决策指南 - -## 核心问题:除了流本身,其他地方是否需要用Reactor? - -### 快速决策表 - -| 场景 | 是否用Reactor | 理由 | -|------|--------------|------| -| **数据流处理** | ✅ 必须 | 核心功能,需要背压和非阻塞 | -| **Job调度执行** | ✅ 建议 | 异步任务,避免阻塞主线程 | -| **状态管理** | ✅ 建议 | 可能涉及I/O持久化 | -| **检查点** | ✅ 建议 | 涉及文件/数据库I/O | -| **指标收集** | ✅ 建议 | 异步发送,不阻塞业务 | -| **配置查询(高频)** | ✅ 建议 | 在流处理中调用 | -| **配置查询(低频)** | ⚠️ 可选 | 启动时加载,同步可接受 | -| **元数据CRUD** | ⚠️ 可选 | 管理后台,同步更简单 | -| **缓存操作(分布式)** | ✅ 建议 | 网络I/O | -| **缓存操作(本地)** | ❌ 不需要 | 内存操作 | -| **日志记录** | ❌ 不需要 | 同步即可 | -| **纯计算** | ❌ 不需要 | 无I/O | - -## 详细分析 - -### 1. Job 调度和执行 - ✅ 建议使用 Reactor - -#### 为什么要用? -- Job调度是异步操作 -- 执行Job不应阻塞调度线程 -- 便于组合多个异步操作 - -#### 示例实现 - -```java -@Service -public class ReactiveJobScheduler implements JobScheduler { - - private final JobRepository jobRepository; - private final JobExecutor jobExecutor; - - @Override - public Mono schedule(Job job, ScheduleConfig config) { - return Mono.defer(() -> { - // 1. 验证配置(可能涉及数据库查询) - return validateConfig(config) - // 2. 创建调度计划(数据库操作) - .flatMap(valid -> createSchedule(job, config)) - // 3. 注册到调度器 - .flatMap(schedule -> registerSchedule(schedule)) - // 4. 返回结果 - .map(this::toScheduleResult); - }) - .doOnSuccess(result -> log.info("Job scheduled: {}", job.getJobId())) - .doOnError(error -> log.error("Schedule failed: {}", job.getJobId(), error)); - } - - @Override - public Mono trigger(String jobId) { - return jobRepository.findById(jobId) // 异步查询 - .switchIfEmpty(Mono.error(new JobNotFoundException(jobId))) - .flatMap(job -> jobExecutor.submit(job)) // 异步提交 - .then(); - } - - private Mono validateConfig(ScheduleConfig config) { - // 可能需要查询数据库验证 - return jobRepository.existsByName(config.getJobName()) - .map(exists -> !exists); - } - - private Mono createSchedule(Job job, ScheduleConfig config) { - Schedule schedule = new Schedule(job, config); - return scheduleRepository.save(schedule); // 异步保存 - } -} -``` - -**关键点**: -- ✅ 所有I/O操作都是异步的 -- ✅ 操作可以方便地组合 -- ✅ 不阻塞调度线程 - -### 2. Job 执行器 - ✅ 必须使用 Reactor - -#### 为什么必须用? -- 需要并行执行多个Job -- 需要监控Job状态(流式) -- 需要异步启动/停止Job - -```java -@Service -public class ReactiveJobExecutor implements JobExecutor { - - private final Map runningJobs = new ConcurrentHashMap<>(); - - @Override - public Mono submit(Job job) { - return Mono.defer(() -> { - // 1. 创建Job实例记录 - return createJobInstance(job) - // 2. 启动Pipeline执行 - .flatMap(instance -> executePipeline(job, instance)) - // 3. 更新实例状态 - .flatMap(result -> updateJobInstance(result)) - // 4. 返回执行结果 - .map(this::toJobResult); - }) - .doOnSubscribe(s -> log.info("Job submitted: {}", job.getJobId())) - .doOnSuccess(result -> log.info("Job completed: {}", job.getJobId())); - } - - @Override - public Flux getMetrics(String jobId) { - // 实时推送指标流 - return Flux.interval(Duration.ofSeconds(1)) - .flatMap(tick -> metricsCollector.collect(jobId)) - .takeUntil(metrics -> isJobCompleted(jobId)); - } - - @Override - public Mono stop(String jobId) { - return Mono.defer(() -> { - Disposable disposable = runningJobs.get(jobId); - if (disposable != null) { - disposable.dispose(); - runningJobs.remove(jobId); - } - return updateJobStatus(jobId, JobStatus.STOPPED); - }); - } - - private Mono executePipeline(Job job, JobInstance instance) { - // 构建并执行Pipeline - Pipeline pipeline = buildPipeline(job); - - Disposable execution = pipeline.execute() - .subscribe( - result -> handleSuccess(instance, result), - error -> handleError(instance, error) - ); - - runningJobs.put(job.getJobId(), execution); - return Mono.just(new PipelineResult()); - } -} -``` - -**关键点**: -- ✅ 支持并发执行多个Job -- ✅ 实时指标推送(Flux) -- ✅ 异步启动/停止 - -### 3. 状态管理 - ✅ 建议使用 Reactor - -#### 为什么建议用? -- 状态可能持久化到数据库/Redis -- 在流处理中频繁访问 -- 需要原子性操作(CAS) - -```java -@Service -public class ReactiveStateManager implements StateManager { - - private final R2dbcEntityTemplate r2dbcTemplate; - private final ReactiveRedisTemplate redisTemplate; - - @Override - public Mono> createState(String name, T initialValue) { - return Mono.defer(() -> { - // 创建状态实例 - ReactiveState state = new ReactiveState<>(name, initialValue); - - // 持久化到Redis(异步) - return redisTemplate.opsForValue() - .set(stateKey(name), initialValue) - .thenReturn(state); - }); - } - - @Override - public Mono> snapshot() { - // 从Redis批量读取所有状态 - return redisTemplate.keys(stateKeyPattern()) - .flatMap(key -> redisTemplate.opsForValue().get(key) - .map(value -> Map.entry(extractName(key), value))) - .collectMap(Map.Entry::getKey, Map.Entry::getValue); - } - - @Override - public Mono restore(Map snapshot) { - // 批量恢复状态到Redis - return Flux.fromIterable(snapshot.entrySet()) - .flatMap(entry -> redisTemplate.opsForValue() - .set(stateKey(entry.getKey()), entry.getValue())) - .then(); - } -} - -// 状态实现 -public class ReactiveState implements State { - - private final String name; - private final ReactiveRedisTemplate redisTemplate; - - @Override - public Mono get() { - return redisTemplate.opsForValue() - .get(stateKey()) - .cast(getTypeClass()); - } - - @Override - public Mono update(T value) { - return redisTemplate.opsForValue() - .set(stateKey(), value) - .then(); - } - - @Override - public Mono compareAndSet(T expect, T update) { - // 使用Lua脚本实现原子CAS - String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " + - "return redis.call('set', KEYS[1], ARGV[2]) else " + - "return 0 end"; - - return redisTemplate.execute( - RedisScript.of(script, Boolean.class), - Collections.singletonList(stateKey()), - expect, update - ).next(); - } -} -``` - -**关键点**: -- ✅ 支持分布式状态存储 -- ✅ 原子操作(CAS) -- ✅ 在流处理中使用不阻塞 - -### 4. 检查点 - ✅ 建议使用 Reactor - -#### 为什么建议用? -- 涉及文件I/O或数据库I/O -- 在流处理中触发 -- 需要定期调度 - -```java -@Service -public class ReactiveCheckpointCoordinator implements CheckpointCoordinator { - - private final StateManager stateManager; - private final CheckpointStorage storage; - - @Override - public Mono triggerCheckpoint() { - return Mono.defer(() -> { - String checkpointId = generateCheckpointId(); - - // 1. 创建状态快照(异步) - return stateManager.snapshot() - // 2. 创建检查点对象 - .map(snapshot -> createCheckpoint(checkpointId, snapshot)) - // 3. 持久化到存储(异步) - .flatMap(checkpoint -> storage.save(checkpoint) - .thenReturn(checkpoint)) - // 4. 记录到数据库(异步) - .flatMap(checkpoint -> recordCheckpoint(checkpoint)); - }) - .doOnSuccess(cp -> log.info("Checkpoint created: {}", cp.getCheckpointId())) - .timeout(Duration.ofMinutes(5)); // 检查点超时保护 - } - - @Override - public Flux scheduleCheckpoints(Duration interval) { - // 定期触发检查点 - return Flux.interval(interval) - .flatMap(tick -> triggerCheckpoint() - .onErrorResume(error -> { - log.error("Checkpoint failed", error); - return Mono.empty(); // 失败不中断调度 - })); - } - - @Override - public Mono restoreFromCheckpoint(String checkpointId) { - return storage.load(checkpointId) - .flatMap(checkpoint -> { - Map snapshot = checkpoint.getStateSnapshot(); - return stateManager.restore(snapshot); - }); - } -} - -// 检查点存储实现 -@Service -public class FileCheckpointStorage implements CheckpointStorage { - - private final Path storagePath; - - @Override - public Mono save(Checkpoint checkpoint) { - return Mono.fromCallable(() -> { - // 序列化为JSON - String json = objectMapper.writeValueAsString(checkpoint); - // 写入文件 - Path file = getCheckpointFile(checkpoint.getCheckpointId()); - Files.writeString(file, json); - return null; - }) - .subscribeOn(Schedulers.boundedElastic()) // 文件I/O,隔离到专用线程池 - .then(); - } - - @Override - public Mono load(String checkpointId) { - return Mono.fromCallable(() -> { - Path file = getCheckpointFile(checkpointId); - String json = Files.readString(file); - return objectMapper.readValue(json, CheckpointImpl.class); - }) - .subscribeOn(Schedulers.boundedElastic()); - } -} -``` - -**关键点**: -- ✅ 文件I/O异步化 -- ✅ 定期调度不阻塞 -- ✅ 超时保护 - -### 5. 指标收集 - ✅ 建议使用 Reactor - -#### 为什么建议用? -- 需要定期推送指标 -- 发送到外部监控系统(网络I/O) -- 不应阻塞业务逻辑 - -```java -@Service -public class ReactiveMetricsCollector implements MetricsCollector { - - private final ConcurrentHashMap counters = new ConcurrentHashMap<>(); - private final MetricsReporter reporter; - - @Override - public Mono recordCounter(String name, long value, Map tags) { - // 同步更新内存计数器(快速) - counters.computeIfAbsent(name, k -> new AtomicLong()).addAndGet(value); - - // 不需要返回Mono,除非要立即持久化 - return Mono.empty(); - } - - @Override - public Flux> publishMetrics(Duration interval) { - // 定期推送指标流 - return Flux.interval(interval) - .map(tick -> snapshot()) - .flatMap(metrics -> reporter.report(metrics) - .thenReturn(metrics)) - .onErrorContinue((error, metrics) -> - log.warn("Failed to report metrics", error)); - } - - @Override - public Mono> snapshot() { - // 快照是内存操作,可以同步 - return Mono.fromCallable(() -> { - Map snapshot = new HashMap<>(); - counters.forEach((name, value) -> - snapshot.put(name, value.get())); - return snapshot; - }); - } -} - -// 指标报告器 -@Service -public class PrometheusMetricsReporter implements MetricsReporter { - - private final WebClient webClient; - - @Override - public Mono report(Map metrics) { - // 异步发送到Prometheus Push Gateway - return webClient.post() - .uri("/metrics/job/{job}", "pipeline-framework") - .bodyValue(formatMetrics(metrics)) - .retrieve() - .bodyToMono(Void.class) - .timeout(Duration.ofSeconds(5)) - .onErrorResume(error -> { - log.warn("Failed to push metrics", error); - return Mono.empty(); - }); - } -} -``` - -**关键点**: -- ✅ 内存操作可以同步(计数器更新) -- ✅ 网络I/O必须异步(发送指标) -- ✅ 定期推送用Flux - -### 6. 配置管理 - ⚠️ 看场景 - -#### 高频查询(流处理中)- ✅ 用 Reactor - -```java -@Service -public class ReactiveConfigService { - - private final R2dbcEntityTemplate template; - private final ReactiveRedisTemplate cache; - - /** - * 在流处理中获取配置 - 必须响应式 - */ - public Mono getOperatorConfig(String operatorId) { - // 1. 先查缓存 - return cache.opsForValue().get(configKey(operatorId)) - .cast(OperatorConfig.class) - // 2. 缓存未命中,查数据库 - .switchIfEmpty(Mono.defer(() -> - template.selectOne( - Query.query(Criteria.where("operator_id").is(operatorId)), - OperatorConfig.class - ) - // 3. 写入缓存 - .flatMap(config -> cache.opsForValue() - .set(configKey(operatorId), config, Duration.ofMinutes(10)) - .thenReturn(config)) - )); - } -} - -// 在Operator中使用 -public class DynamicOperator implements Operator { - - private final ReactiveConfigService configService; - private final String operatorId; - - @Override - public Flux apply(Flux input) { - return input.flatMap(data -> - // 每次处理都可能查询最新配置 - configService.getOperatorConfig(operatorId) - .map(config -> transform(data, config)) - ); - } -} -``` - -#### 低频查询(启动时)- ⚠️ 同步可以 - -```java -@Service -public class ConfigLoader { - - private final JobMapper jobMapper; - private Map configCache; - - /** - * 应用启动时加载配置 - 同步可接受 - */ - @PostConstruct - public void loadConfigs() { - log.info("Loading job configurations..."); - - // 同步查询 - List jobs = jobMapper.selectList(null); - - configCache = jobs.stream() - .collect(Collectors.toMap( - JobEntity::getJobId, - this::parseConfig - )); - - log.info("Loaded {} job configurations", configCache.size()); - } - - /** - * 从缓存获取(内存操作) - */ - public JobConfig getConfig(String jobId) { - return configCache.get(jobId); - } -} -``` - -### 7. 元数据 CRUD - ⚠️ 可选 - -#### 管理API - 同步更简单 - -```java -@RestController -@RequestMapping("/api/jobs") -public class JobController { - - private final JobService jobService; - - /** - * 管理后台API - 同步即可 - */ - @GetMapping("/{id}") - public JobEntity getJob(@PathVariable String id) { - return jobService.getByIdSync(id); - } - - @PostMapping - public JobEntity createJob(@RequestBody JobEntity job) { - return jobService.saveSync(job); - } - - @GetMapping - public PageResult listJobs( - @RequestParam int page, - @RequestParam int size) { - return jobService.listByPageSync(page, size); - } -} -``` - -#### 在流处理中使用 - 建议响应式 - -```java -@Service -public class JobExecutionService { - - private final JobService jobService; - - /** - * 流处理中查询Job信息 - 建议响应式 - */ - public Mono executeJob(String jobId) { - return jobService.getByJobId(jobId) // 响应式查询 - .flatMap(job -> buildPipeline(job)) - .flatMap(pipeline -> pipeline.execute()) - .then(); - } -} -``` - -## 判断标准 - -### 使用 Reactor 的判断标准 - -``` -是否需要 Reactor? - ↓ -[涉及I/O操作?] - ├─ 是 → [调用频率?] - │ ├─ 高频 → ✅ 必须用 Reactor - │ └─ 低频 → ⚠️ 可选(建议用) - └─ 否 → [纯计算?] - ├─ 是 → ❌ 不用 Reactor - └─ 否 → [在流处理中?] - ├─ 是 → ✅ 必须用 Reactor - └─ 否 → ⚠️ 可选 -``` - -### 具体判断问题 - -1. **有网络I/O吗?**(数据库、HTTP、消息队列) - - 是 → ✅ 用 Reactor - -2. **有文件I/O吗?** - - 是,且文件大 → ✅ 用 Reactor - - 是,且文件小且不频繁 → ⚠️ 可选 - -3. **操作频繁吗?** - - 是(每秒多次) → ✅ 用 Reactor - - 否(启动时、人工操作) → ⚠️ 可选 - -4. **在数据流处理中调用吗?** - - 是 → ✅ 必须用 Reactor - - 否 → ⚠️ 可选 - -5. **需要并发执行吗?** - - 是 → ✅ 用 Reactor - - 否 → ⚠️ 可选 - -## 实践建议 - -### 1. 优先级排序 - -**必须用 Reactor(P0)**: -- ✅ 数据流处理(Source/Operator/Sink) -- ✅ Job执行器 -- ✅ 流式指标推送 - -**建议用 Reactor(P1)**: -- ✅ Job调度器 -- ✅ 状态管理(持久化) -- ✅ 检查点 -- ✅ 指标收集(发送) -- ✅ 配置查询(在流处理中) - -**可选用 Reactor(P2)**: -- ⚠️ 配置加载(启动时) -- ⚠️ 元数据CRUD(管理API) -- ⚠️ 本地缓存操作 - -**不用 Reactor(P3)**: -- ❌ 日志记录 -- ❌ 纯计算 -- ❌ 简单内存操作 - -### 2. 渐进式引入 - -#### 阶段1:核心必须响应式 -```java -// 数据流处理 -source.read() → operator.apply() → sink.write() - -// Job执行 -jobExecutor.submit(job) -``` - -#### 阶段2:扩展建议响应式 -```java -// 调度 -scheduler.schedule(job, config) - -// 状态 -stateManager.snapshot() - -// 检查点 -checkpointCoordinator.triggerCheckpoint() -``` - -#### 阶段3:逐步优化 -```java -// 配置查询 -configService.getConfig(id) // 从同步改为响应式 - -// 元数据 -jobService.getByJobId(id) // 从同步改为响应式 -``` - -### 3. 混合使用策略 - -```java -@Service -public class HybridJobService { - - private final JobMapper jobMapper; // MyBatis Plus(同步) - - /** - * 响应式API - 包装同步调用 - * 用于流处理中调用 - */ - public Mono getByJobId(String jobId) { - return Mono.fromCallable(() -> jobMapper.selectByJobId(jobId)) - .subscribeOn(Schedulers.boundedElastic()); - } - - /** - * 同步API - 直接调用 - * 用于管理后台 - */ - public JobEntity getByJobIdSync(String jobId) { - return jobMapper.selectByJobId(jobId); - } - - /** - * 根据场景选择 - */ - public Object getJob(String jobId, boolean async) { - if (async) { - return getByJobId(jobId); // 返回 Mono - } else { - return getByJobIdSync(jobId); // 返回 JobEntity - } - } -} -``` - -## 总结 - -### 核心原则 - -1. **I/O边界必须响应式** - 所有外部系统交互 -2. **数据流必须响应式** - Source到Sink的完整链路 -3. **高频操作建议响应式** - 避免阻塞累积 -4. **低频操作可以同步** - 启动、配置、管理 -5. **纯计算不用响应式** - 避免过度抽象 - -### 记住三句话 - -1. **有I/O就用Reactor** - 数据库、网络、文件 -2. **在流里就用Reactor** - 数据流处理中的所有调用 -3. **其他看情况** - 频繁用Reactor,偶尔可同步 - -### 最后的建议 - -**不要过度使用 Reactor**: -- ❌ 不是所有代码都要响应式 -- ❌ 不是所有方法都要返回Mono/Flux -- ✅ 在关键路径上使用(数据流、I/O) -- ✅ 其他地方根据实际需求决定 - -**找到平衡点**: -- 响应式带来的好处 > 增加的复杂度 → 使用 -- 响应式带来的好处 < 增加的复杂度 → 不用 - -项目中已经提供了**两套API**(响应式 + 同步),可以根据实际场景灵活选择! diff --git a/pipeline-framework/REACTOR_USAGE_GUIDE.md b/pipeline-framework/REACTOR_USAGE_GUIDE.md deleted file mode 100644 index 04dde5f55..000000000 --- a/pipeline-framework/REACTOR_USAGE_GUIDE.md +++ /dev/null @@ -1,313 +0,0 @@ -# Project Reactor 使用指南 - -## 何时使用 Reactor? - -### ✅ 必须使用 Reactor 的场景 - -#### 1. **数据流处理**(核心流程) -```java -// Source → Operator → Sink 整个链路必须是响应式的 -Flux dataStream = source.read(); // 必须 -Flux transformed = operator.apply(dataStream); // 必须 -Mono written = sink.write(transformed); // 必须 -``` - -#### 2. **I/O 操作** -```java -// 数据库操作 -Mono user = userRepository.findById(id); // 必须 - -// 网络请求 -Mono response = webClient.get().retrieve().bodyToMono(Response.class); // 必须 - -// 文件操作(大文件) -Flux lines = DataBufferUtils.read(path, ...); // 建议 -``` - -#### 3. **外部系统交互** -```java -// Kafka消息 -Flux records = kafkaReceiver.receive(); // 必须 - -// Redis操作 -Mono value = reactiveRedisTemplate.opsForValue().get(key); // 建议 - -// HTTP API调用 -Mono data = webClient.post().bodyValue(request).retrieve().bodyToMono(Data.class); // 必须 -``` - -### ⚠️ 可选使用 Reactor 的场景 - -#### 1. **配置和元数据查询**(不频繁调用) -```java -// 可以使用 Reactor -Mono config = configService.getConfig(jobId); - -// 也可以使用同步 -JobConfig config = configService.getConfigSync(jobId); -``` - -**建议**:如果调用频率低(如启动时加载配置),可以用同步;如果在流处理中调用,用Reactor。 - -#### 2. **缓存操作** -```java -// 简单缓存可以同步 -Map cache = new ConcurrentHashMap<>(); -Object value = cache.get(key); - -// 分布式缓存建议响应式 -Mono value = reactiveCache.get(key); -``` - -#### 3. **日志记录** -```java -// 同步日志记录是可以的 -log.info("Processing data: {}", data); - -// 不需要 -// Mono.fromRunnable(() -> log.info(...)).subscribe(); -``` - -### ❌ 不应该使用 Reactor 的场景 - -#### 1. **纯计算操作**(无I/O) -```java -// ❌ 不需要 -Mono result = Mono.fromCallable(() -> x + y); - -// ✅ 直接计算 -int result = x + y; -``` - -#### 2. **简单的内存操作** -```java -// ❌ 过度使用 -Mono value = Mono.just(map.get(key)); - -// ✅ 直接操作 -String value = map.get(key); -``` - -#### 3. **阻塞且无法改造的第三方库** -```java -// 如果必须用阻塞库,隔离到专门的线程池 -Mono result = Mono.fromCallable(() -> blockingLibrary.call()) - .subscribeOn(Schedulers.boundedElastic()); // 使用专门的线程池 -``` - -## 实践建议 - -### 层次划分 - -``` -┌─────────────────────────────────────────┐ -│ Controller/API Layer │ ← 使用 Reactor -│ 返回 Mono/Flux │ -├─────────────────────────────────────────┤ -│ Service Layer │ ← 混合使用 -│ - 业务逻辑:可同步 │ -│ - I/O操作:用 Reactor │ -├─────────────────────────────────────────┤ -│ Repository/DAO Layer │ ← 使用 Reactor -│ R2DBC/Reactive MongoDB │ (如果用响应式DB) -├─────────────────────────────────────────┤ -│ Stream Processing Layer │ ← 必须 Reactor -│ Source → Operator → Sink │ -└─────────────────────────────────────────┘ -``` - -### 本项目的使用策略 - -#### 核心流处理 - 100% Reactor -```java -// Pipeline执行 -public Mono execute() { - return source.read() // Flux - .transform(operatorChain::execute) // Flux - .as(sink::write) // Mono - .then(Mono.just(result)); -} -``` - -#### Job管理 - 大部分 Reactor -```java -// JobScheduler -public Mono schedule(Job job, ScheduleConfig config) { - return Mono.defer(() -> { - // 业务逻辑(同步) - Schedule schedule = createSchedule(job, config); - - // 持久化(响应式) - return scheduleRepository.save(schedule) - .map(this::toScheduleResult); - }); -} -``` - -#### 状态和检查点 - Reactor -```java -// StateManager -public Mono saveState(String name, Object value) { - return stateRepository.save(name, value); // 响应式持久化 -} - -// CheckpointCoordinator -public Mono triggerCheckpoint() { - return stateManager.snapshot() // Mono - .flatMap(snapshot -> { - Checkpoint checkpoint = createCheckpoint(snapshot); - return checkpointStorage.save(checkpoint); // Mono - }) - .thenReturn(checkpoint); -} -``` - -#### 配置和元数据 - 混合使用 -```java -// 启动时加载(同步可接受) -@PostConstruct -public void init() { - List connectors = loadConnectors(); // 同步 - connectors.forEach(connectorRegistry::register); -} - -// 运行时查询(建议响应式) -public Mono getJobConfig(String jobId) { - return configRepository.findById(jobId); // Mono -} -``` - -## 性能考虑 - -### 何时响应式带来好处? - -1. **高并发I/O** - - 大量数据库查询 - - 多个HTTP请求 - - 文件读写 - -2. **长连接和流式数据** - - WebSocket - - Server-Sent Events - - Kafka消费 - -3. **需要背压控制** - - 生产速度 > 消费速度 - - 需要限流 - -### 何时响应式可能降低性能? - -1. **纯CPU密集型计算** - - 响应式的调度开销 > 并行计算收益 - -2. **极简单的操作** - - 一次数据库查询 + 简单转换 - - 响应式的抽象层开销可能更大 - -3. **阻塞操作** - - 必须使用 `subscribeOn(Schedulers.boundedElastic())` - - 引入额外的线程切换开销 - -## 最佳实践 - -### 1. 避免阻塞 -```java -// ❌ 错误:在响应式链中阻塞 -public Mono process(String id) { - Result result = blockingService.get(id); // 阻塞! - return Mono.just(result); -} - -// ✅ 正确:隔离阻塞操作 -public Mono process(String id) { - return Mono.fromCallable(() -> blockingService.get(id)) - .subscribeOn(Schedulers.boundedElastic()); -} -``` - -### 2. 正确的错误处理 -```java -public Flux processData() { - return source.read() - .onErrorContinue((error, data) -> { - log.error("Error processing: {}", data, error); - // 继续处理下一个 - }) - .retryWhen(Retry.backoff(3, Duration.ofSeconds(1))); -} -``` - -### 3. 资源管理 -```java -public Flux readFile(Path path) { - return Flux.using( - () -> Files.newInputStream(path), // 获取资源 - inputStream -> readFromStream(inputStream), // 使用资源 - inputStream -> { // 清理资源 - try { - inputStream.close(); - } catch (IOException e) { - log.warn("Error closing stream", e); - } - } - ); -} -``` - -### 4. 背压处理 -```java -public Flux processWithBackpressure() { - return source.read() - .onBackpressureBuffer(1000) // 缓冲区 - .onBackpressureDrop(data -> // 丢弃策略 - log.warn("Dropped: {}", data)) - .limitRate(100); // 限速 -} -``` - -## 调试建议 - -### 启用日志 -```java -Flux flux = source.read() - .log("source-read") // 记录所有信号 - .map(this::transform) - .log("transform") - .filter(this::validate) - .log("filter"); -``` - -### 检查点(Checkpoint) -```java -Flux flux = source.read() - .checkpoint("after-source") // 标记位置 - .map(this::transform) - .checkpoint("after-transform") - .filter(this::validate); -``` - -### 订阅追踪 -```java -// 启用订阅追踪 -Hooks.onOperatorDebug(); - -// 生产环境禁用(性能影响) -Hooks.resetOnOperatorDebug(); -``` - -## 总结 - -### Pipeline Framework 中的 Reactor 使用原则 - -1. **数据流处理**:必须全程使用 Reactor(Source → Operator → Sink) -2. **外部I/O**:建议使用 Reactor(数据库、缓存、消息队列、HTTP) -3. **业务逻辑**:简单的可以同步,复杂的组合建议 Reactor -4. **配置管理**:启动时可同步,运行时建议 Reactor -5. **日志和监控**:同步即可 -6. **纯计算**:同步即可 - -### 记住三个原则 - -1. **I/O 边界必须响应式** - 所有与外部系统交互的地方 -2. **数据流必须响应式** - 从源到目标的整个流程 -3. **其他地方看情况** - 根据并发需求和调用频率决定 diff --git a/pipeline-framework/README.md b/pipeline-framework/README.md index c4d5f018f..c9f1696a9 100644 --- a/pipeline-framework/README.md +++ b/pipeline-framework/README.md @@ -1,244 +1,443 @@ -# Reactive ETL Framework +# Pipeline Framework -基于Spring Boot和Project Reactor的响应式ETL数据处理框架。 +基于 Spring Boot 和 Project Reactor 的响应式 ETL 数据处理框架。 -## 项目简介 - -本项目是一个轻量级的ETL(Extract-Transform-Load)数据采集框架,借鉴Apache Flink的设计理念,采用Source、Operator、Sink的经典数据处理模型,并基于Project Reactor实现完全响应式的数据流处理。 +--- -### 核心特性 +## 🎯 核心特性 -- ✅ **响应式流处理**: 基于Reactor实现非阻塞、背压支持的数据流处理 -- ✅ **模块化设计**: 清晰的任务调度、图转换、执行引擎分层架构 -- ✅ **高性能**: 充分利用响应式编程的优势,支持高吞吐量数据处理 -- ✅ **易用性**: 提供简洁的API,支持声明式任务定义 -- ✅ **可观测性**: 内置监控指标和日志,方便运维调试 -- ✅ **可扩展性**: 基于Connectors的插件化扩展机制 +- ✅ **插件化 Connector 机制** - Connector 不依赖 Reactor,可独立开发和测试 +- ✅ **强类型泛型约束** - 多层次泛型参数,提供类型安全保障 +- ✅ **丰富的设计模式** - 工厂、适配器、模板方法、策略、建造者等模式应用 +- ✅ **灵活的架构分层** - Connector 层、Adapter 层、Component 层清晰分离 +- ✅ **响应式数据流** - 基于 Project Reactor,支持背压、异步、非阻塞 +- ✅ **多种任务类型** - STREAMING(流式)、BATCH(批处理)、SQL_BATCH(SQL 批处理) -## 技术栈 +--- -- **Java**: 17 -- **Spring Boot**: 3.2.0 -- **Project Reactor**: 3.6.0 -- **数据库**: MySQL 8.0 (R2DBC) -- **消息队列**: Apache Kafka -- **缓存**: Redis -- **监控**: Micrometer + Prometheus + Grafana -- **构建工具**: Maven 3.9+ +## 🏗️ 核心架构 -## 项目结构 +### 分层设计 ``` -pipeline-framework/ -├── etl-api/ # 核心API定义 -├── etl-core/ # 核心运行时实现 -├── etl-connectors/ # 连接器实现(JDBC、Kafka等) -├── etl-operators/ # 算子实现(Map、Filter等) -├── etl-scheduler/ # 任务调度 -├── etl-executor/ # 任务执行引擎 -├── etl-state/ # 状态管理 -├── etl-checkpoint/ # 检查点机制 -├── etl-metrics/ # 监控指标 -├── etl-web/ # Web API -├── etl-starter/ # Spring Boot启动模块 -├── docs/ # 设计文档 -├── Dockerfile # Docker镜像构建 -└── docker-compose.yml # Docker Compose配置 +┌─────────────────────────────────────────────────────────┐ +│ Application Layer │ +│ (Job Definition & Execution) │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Component Layer │ +│ (DataSource, Operator, DataSink) │ +│ [依赖 Reactor] │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Adapter Layer │ +│ (Reader→Source, Writer→Sink 适配) │ +└──────────────────────┬──────────────────────────────────┘ + │ +┌──────────────────────┴──────────────────────────────────┐ +│ Connector Layer │ +│ (ConnectorReader, ConnectorWriter) │ +│ [不依赖 Reactor] │ +└──────────────────────┬──────────────────────────────────┘ + │ + External Systems + (JDBC, Kafka, Redis, File...) ``` -## 快速开始 +### 关键设计模式 + +| 模式 | 应用场景 | 类/接口 | +|------|---------|---------| +| 🏭 工厂模式 | Connector 创建 | `ConnectorFactory`, `ConnectorFactoryRegistry` | +| 🔌 适配器模式 | Connector → Component | `DefaultReaderToSourceAdapter`, `DefaultWriterToSinkAdapter` | +| 📋 模板方法模式 | 通用流程骨架 | `AbstractJdbcConnector`, `AbstractConnectorAdapter` | +| 🎯 策略模式 | 可替换的算法 | `ConnectorType` 枚举 + 多种 Connector 实现 | +| 🔧 建造者模式 | 复杂对象构建 | `ConnectorMetadata.Builder`, `ComponentMetadata.Builder` | +| 📝 注册表模式 | 动态注册 | `ConnectorFactoryRegistry` | -### 前置要求 +--- + +## 📦 模块结构 + +``` +pipeline-framework/ +├── pipeline-api/ # 核心接口定义 +│ ├── connector/ # Connector 接口 +│ │ ├── adapter/ # 适配器接口 +│ │ └── factory/ # 工厂接口 +│ ├── component/ # Component 基础接口 +│ ├── source/ # DataSource 接口 +│ ├── sink/ # DataSink 接口 +│ └── operator/ # Operator 接口 +│ +├── pipeline-core/ # 核心实现 +│ ├── connector/ # Adapter 实现 +│ ├── builder/ # Pipeline 构建器 +│ └── runtime/ # 运行时 +│ +├── pipeline-connectors/ # Connector 实现 +│ ├── jdbc/ # JDBC Connector +│ ├── kafka/ # Kafka Connector +│ └── console/ # Console Connector +│ +├── pipeline-operators/ # Operator 实现 +├── pipeline-executor/ # 执行器 +├── pipeline-scheduler/ # 调度器 +├── pipeline-state/ # 状态管理 +├── pipeline-checkpoint/ # 检查点 +├── pipeline-metrics/ # 监控指标 +└── pipeline-starter/ # Spring Boot 启动器 +``` -- Java 17+ -- Maven 3.9+ -- Docker & Docker Compose (可选) +--- -### 本地开发 +## 🚀 快速开始 -1. **克隆项目** +### 1. 创建 Connector(不依赖 Reactor) -```bash -git clone -cd pipeline-framework +```java +// 配置 +JdbcConnectorConfig config = new JdbcConnectorConfig(); +config.setUrl("jdbc:mysql://localhost:3306/test"); +config.setUsername("root"); +config.setPassword("password"); +config.setQuerySql("SELECT * FROM users"); + +// 创建 Reader +JdbcConnectorReader reader = new JdbcConnectorReader(config); +reader.open(); + +// 读取数据 +List> batch = reader.readBatch(1000); +System.out.println("Read: " + batch.size() + " records"); + +reader.close(); ``` -2. **编译项目** +### 2. 使用工厂模式 -```bash -mvn clean install +```java +// 注册工厂 +ConnectorFactoryRegistry registry = ConnectorFactoryRegistry.getInstance(); +registry.register(ConnectorType.JDBC, new JdbcConnectorFactory()); + +// 创建 Connector +ConnectorReader, JdbcConnectorConfig> reader = + registry.createReader(ConnectorType.JDBC, config); ``` -3. **启动数据库** +### 3. 转换为 Component(集成 Reactor) + +```java +// 创建适配器 +DefaultReaderToSourceAdapter, JdbcConnectorConfig> adapter = + new DefaultReaderToSourceAdapter<>(reader, 1000); -```bash -# 使用Docker Compose启动MySQL -docker-compose up -d mysql +// 获取 DataSource +DataSource> source = adapter.adapt(reader); -# 初始化数据库 -mysql -h localhost -u root -p < docs/database-schema.sql +// 使用响应式流 +Flux> stream = source.read(); +stream.subscribe(data -> System.out.println(data)); ``` -4. **启动应用** +### 4. 完整 ETL 流程 -```bash -cd etl-starter -mvn spring-boot:run +```java +// 源和目标 +ConnectorReader reader = registry.createReader(ConnectorType.JDBC, sourceConfig); +ConnectorWriter writer = registry.createWriter(ConnectorType.JDBC, sinkConfig); + +// 适配为 Component +DataSource source = new DefaultReaderToSourceAdapter(reader, 1000).adapt(reader); +DataSink sink = new DefaultWriterToSinkAdapter(writer, 1000).adapt(writer); + +// 执行 ETL +source.read() + .map(data -> { + // 数据转换 + data.put("migrated_at", System.currentTimeMillis()); + return data; + }) + .filter(data -> data.get("email") != null) // 过滤 + .transform(dataStream -> sink.write(dataStream)) + .block(); ``` -5. **访问应用** +--- -- Web UI: http://localhost:8080 -- Actuator: http://localhost:8080/actuator -- Health Check: http://localhost:8080/actuator/health +## 💡 核心接口 -### Docker部署 +### Connector 层(不依赖 Reactor) -1. **构建并启动所有服务** +```java +// 顶层接口 +public interface Connector { + String getName(); + ConnectorType getType(); + C getConfig(); + ConnectorMetadata getMetadata(); +} + +// Reader 接口 +public interface ConnectorReader extends Connector { + void open() throws Exception; + List readBatch(int batchSize) throws Exception; + boolean hasNext(); + void close() throws Exception; + + // 可选能力 + Object getCheckpoint(); + void seekToCheckpoint(Object checkpoint) throws Exception; + boolean supportsCheckpoint(); + double getProgress(); + long getReadCount(); +} -```bash -docker-compose up -d +// Writer 接口 +public interface ConnectorWriter extends Connector { + void open() throws Exception; + void write(T record) throws Exception; + void writeBatch(List records) throws Exception; + void flush() throws Exception; + void close() throws Exception; + + // 事务能力 + boolean supportsTransaction(); + void beginTransaction() throws Exception; + void commit() throws Exception; + void rollback() throws Exception; +} ``` -2. **查看日志** +### Component 层(依赖 Reactor) -```bash -docker-compose logs -f etl-framework -``` +```java +// Component 基础接口 +public interface Component { + String getName(); + ComponentType getComponentType(); + C getConfig(); +} -3. **停止服务** +// DataSource 接口 +public interface DataSource extends Component, LifecycleAware { + Flux read(); + SourceType getType(); +} + +// DataSink 接口 +public interface DataSink extends Component, LifecycleAware { + Mono write(Flux data); + Mono flush(); +} -```bash -docker-compose down +// Operator 接口 +public interface Operator extends StreamingComponent { + Flux apply(Flux input); + OperatorType getType(); +} ``` -## 开发指南 +--- + +## 📚 开发指南 -### 添加自定义Connector +### 创建自定义 Connector -1. 在`etl-connectors`模块创建新的Connector类 -2. 实现`DataSource`或`DataSink`接口 -3. 使用`@Component`注解注册到Spring容器 +1. **定义配置类** ```java -@Component -public class CustomSource implements DataSource { +public class MyConnectorConfig extends ConnectorConfig { + private String endpoint; + @Override - public Flux getDataStream() { - // 实现数据读取逻辑 + public void validate() { + if (endpoint == null) { + throw new IllegalArgumentException("Endpoint required"); + } } - // ... 其他方法实现 } ``` -### 添加自定义Operator +2. **实现 Reader** + +```java +public class MyConnectorReader extends AbstractMyConnector + implements ReadableConnector { + + @Override + protected void doOpen() throws Exception { + // 初始化连接 + } + + @Override + public List readBatch(int batchSize) throws Exception { + // 读取数据 + } + + @Override + protected void doClose() throws Exception { + // 清理资源 + } +} +``` -1. 在`etl-operators`模块创建新的Operator类 -2. 实现`Operator`接口 -3. 使用`@Component`注解注册 +3. **实现工厂** ```java -@Component -public class CustomOperator implements Operator { +public class MyConnectorFactory + implements ConnectorFactory { + + @Override + public ConnectorReader createReader( + MyConnectorConfig config) throws ConnectorException { + return new MyConnectorReader(config); + } + @Override - public Flux apply(Flux input) { - return input.map(this::transform); + public ConnectorType getSupportedType() { + return ConnectorType.CUSTOM; } - // ... 其他方法实现 } ``` -### 代码规范 +4. **注册使用** -- 遵循Google Java Style -- 所有公共方法必须有JavaDoc -- 使用SLF4J进行日志记录 -- 使用泛型提高代码复用性 -- 资源必须正确关闭和清理 +```java +ConnectorFactoryRegistry.getInstance() + .register(ConnectorType.CUSTOM, new MyConnectorFactory()); +``` -## 配置说明 +详细开发指南请参考:[CONNECTOR_DEVELOPMENT_GUIDE.md](CONNECTOR_DEVELOPMENT_GUIDE.md) + +--- + +## 📖 文档 + +- [架构设计文档](ARCHITECTURE_DESIGN.md) - 详细的架构说明和设计模式应用 +- [Connector 开发指南](CONNECTOR_DEVELOPMENT_GUIDE.md) - 如何开发自定义 Connector +- [项目结构说明](STRUCTURE.md) - 模块结构和目录说明 +- [快速开始](QUICK_START.md) - 从零开始构建第一个 Pipeline + +--- -### application.yml +## 🎨 设计亮点 -主要配置项: +### 1. 职责分离 -```yaml -spring: - application: - name: pipeline-framework - r2dbc: - url: r2dbc:mysql://localhost:3306/etl_framework - username: root - password: password +- **Connector 层**:专注 I/O 操作,不依赖 Reactor,易于测试 +- **Adapter 层**:负责转换,将 Connector 适配为 Component +- **Component 层**:响应式数据处理,充分利用 Reactor 的能力 -etl: - framework: - executor: - thread-pool: - core-size: 10 - max-size: 50 - checkpoint: - enabled: true - interval-seconds: 60 - metrics: - enabled: true +### 2. 泛型约束 + +```java +// 多层次泛型参数 +Connector +ConnectorReader +StreamingComponent +ConnectorAdapter, COMP extends Component, C extends ConnectorConfig> ``` -更多配置请参考 `etl-starter/src/main/resources/application-dev.yml` +### 3. 设计模式组合 -## 监控 +- 工厂模式 + 注册表模式 = 动态扩展 +- 适配器模式 + 模板方法模式 = 灵活转换 +- 策略模式 + 泛型约束 = 类型安全 -### Prometheus指标 +### 4. 易于扩展 -访问 http://localhost:8080/actuator/prometheus 查看所有指标 +- 新增 Connector:实现接口 + 注册工厂 +- 新增 Operator:继承 StreamingComponent +- 新增 Job 类型:扩展 JobType 枚举 -### Grafana Dashboard +--- -1. 访问 http://localhost:3000 (默认账号: admin/admin) -2. 添加Prometheus数据源: http://prometheus:9090 -3. 导入Dashboard配置 +## 🔧 技术栈 -## 测试 +- **Spring Boot 3.x** - 应用框架 +- **Project Reactor** - 响应式编程 +- **Java 17+** - 编程语言 +- **Maven** - 构建工具 +- **SLF4J + Logback** - 日志 +- **JUnit 5** - 单元测试 + +--- -### 运行单元测试 +## 📊 示例场景 -```bash -mvn test +### 场景 1:MySQL 到 MySQL 的数据迁移 + +```java +// 源数据库 +JdbcConnectorConfig source = new JdbcConnectorConfig(); +source.setUrl("jdbc:mysql://source:3306/db"); +source.setQuerySql("SELECT * FROM users WHERE active = 1"); + +// 目标数据库 +JdbcConnectorConfig sink = new JdbcConnectorConfig(); +sink.setUrl("jdbc:mysql://target:3306/db"); +sink.setTableName("users_backup"); + +// 执行迁移 +registry.createReader(ConnectorType.JDBC, source) + .adapt() + .read() + .transform(data -> transform(data)) + .writeTo(registry.createWriter(ConnectorType.JDBC, sink)); ``` -### 运行集成测试 +### 场景 2:实时日志处理 -```bash -mvn verify +```java +// Kafka 读取日志 +kafkaSource.read() + .filter(log -> log.getLevel() == Level.ERROR) + .map(log -> enrichLog(log)) + .writeTo(elasticsearchSink); ``` -## 文档 +### 场景 3:批量数据聚合 -详细文档请查看 `docs/` 目录: +```java +// 读取订单数据 +jdbcSource.read() + .buffer(Duration.ofSeconds(10)) + .map(orders -> aggregateOrders(orders)) + .writeTo(redisSink); +``` + +--- + +## 🤝 贡献 -- [系统架构设计](docs/pipeline-framework-design.md) -- [数据库设计](docs/database-design.md) -- [StreamGraph配置](docs/graph-definition-examples.md) -- [JSON示例](docs/graph-definition-json-examples.json) +欢迎提交 Issue 和 Pull Request! -## 贡献指南 +开发指南: +1. Fork 本仓库 +2. 创建特性分支:`git checkout -b feature/my-feature` +3. 提交更改:`git commit -am 'Add my feature'` +4. 推送分支:`git push origin feature/my-feature` +5. 提交 Pull Request -1. Fork项目 -2. 创建特性分支 (`git checkout -b feature/amazing-feature`) -3. 提交更改 (`git commit -m 'Add some amazing feature'`) -4. 推送到分支 (`git push origin feature/amazing-feature`) -5. 创建Pull Request +--- + +## 📄 许可证 -## 许可证 +MIT License -[MIT License](LICENSE) +--- -## 联系方式 +## 👥 团队 -- 问题反馈: [GitHub Issues](/issues) -- 邮件: etl-framework-team@example.com +Pipeline Framework Team --- -**版本**: 1.0.0-SNAPSHOT -**最后更新**: 2025-11-09 +**版本**:1.0.0 +**最后更新**:2025-11-10 + +🚀 快速开始,立即体验强大的 ETL 框架! diff --git a/pipeline-framework/REFACTORING_ARCHITECTURE.md b/pipeline-framework/REFACTORING_ARCHITECTURE.md deleted file mode 100644 index 81bf37a39..000000000 --- a/pipeline-framework/REFACTORING_ARCHITECTURE.md +++ /dev/null @@ -1,451 +0,0 @@ -# Pipeline Framework 架构重构说明 - -## 🎯 重构目标 - -1. **消除所有 switch case**:使用策略模式替代 -2. **增强抽象能力**:多层接口继承,泛型支持 -3. **删除无用类**:清理冗余代码 -4. **提升可扩展性**:符合 SOLID 原则 - ---- - -## 📐 新的接口层次结构 - -### 1. 组件基础接口(最顶层) - -``` -Component -├── ComponentType getComponentType() -├── String getName() -├── C getConfig() -└── ComponentMetadata getMetadata() -``` - -**职责**:定义所有组件的通用属性和行为。 - -### 2. 生命周期接口 - -``` -LifecycleAware -├── Mono start() -├── Mono stop() -└── boolean isRunning() -``` - -**职责**:提供组件生命周期管理能力。 - -### 3. 流式组件接口(中间层) - -``` -StreamingComponent extends Component -├── Flux process(Flux input) -├── Class getInputType() -└── Class getOutputType() -``` - -**职责**:定义流式数据处理能力,使用泛型增强类型安全。 - -### 4. 具体组件接口(底层) - -#### DataSource - -``` -DataSource extends Component, LifecycleAware -├── Flux read() -├── SourceType getType() -└── Class getOutputType() -``` - -#### Operator - -``` -Operator extends StreamingComponent -├── Flux apply(Flux input) -└── OperatorType getType() -``` - -#### DataSink - -``` -DataSink extends Component, LifecycleAware -├── Mono write(Flux data) -├── Mono writeBatch(Flux data, int batchSize) -├── SinkType getType() -└── Class getInputType() -``` - ---- - -## 🚀 策略模式架构 - -### 1. 节点执行器(NodeExecutor) - -**接口定义**: - -```java -public interface NodeExecutor { - Flux buildFlux(StreamNode node, NodeExecutionContext context); - NodeType getSupportedNodeType(); - int getOrder(); -} -``` - -**实现类**: - -| 类名 | 支持的节点类型 | 职责 | -|-----|-------------|------| -| `SourceNodeExecutor` | SOURCE | 从 DataSource 读取数据 | -| `OperatorNodeExecutor` | OPERATOR | 应用算子转换 | -| `SinkNodeExecutor` | SINK | 获取上游数据流 | - -**Spring 自动注册**: - -```java -@Component -public class NodeExecutorRegistry { - // Spring 自动注入所有 NodeExecutor 实现 - public NodeExecutorRegistry(List> executors) { - for (NodeExecutor executor : executors) { - executorMap.put(executor.getSupportedNodeType(), executor); - } - } -} -``` - -### 2. 执行上下文(NodeExecutionContext) - -**职责**: -- 提供 Graph 和组件访问 -- 缓存节点的 Flux,避免重复构建 -- 存储执行过程中的上下文信息 - -**接口方法**: - -```java -public interface NodeExecutionContext { - StreamGraph getGraph(); - Optional> getSource(String nodeId); - Optional> getOperator(String nodeId); - Optional> getSink(String nodeId); - Optional> getCachedFlux(String nodeId); - void cacheFlux(String nodeId, Flux flux); -} -``` - -### 3. 增强的图执行器(EnhancedGraphExecutor) - -**核心逻辑**: - -```java -@Component -public class EnhancedGraphExecutor { - - private final NodeExecutorRegistry executorRegistry; - - // Spring 注入执行器注册表 - public EnhancedGraphExecutor(NodeExecutorRegistry executorRegistry) { - this.executorRegistry = executorRegistry; - } - - private void buildAllNodes(List sortedNodes, NodeExecutionContext context) { - for (StreamNode node : sortedNodes) { - // 策略模式:根据节点类型获取对应的执行器 - NodeExecutor executor = executorRegistry.getExecutor(node.getNodeType()); - - // 执行器自动处理缓存和构建逻辑 - executor.buildFlux(node, context); - } - } -} -``` - -**对比旧代码**: - -```java -// ❌ 旧代码:使用 switch case -switch (node.getNodeType()) { - case SOURCE: - flux = buildSourceFlux(node); - break; - case OPERATOR: - flux = buildOperatorFlux(node); - break; - case SINK: - flux = buildOperatorFlux(node); - break; - default: - throw new IllegalStateException("Unknown node type"); -} - -// ✅ 新代码:使用策略模式 -NodeExecutor executor = executorRegistry.getExecutor(node.getNodeType()); -executor.buildFlux(node, context); -``` - ---- - -## 🗑️ 删除的无用类 - -| 类名 | 原因 | 替代方案 | -|-----|------|---------| -| `DefaultPipeline` | 功能重复 | `SimplePipeline` | -| `GraphBasedPipelineBuilder` | 未使用 Spring | `SpringGraphBasedPipelineBuilder` | -| `PipelineBuilder` | 无实际用途 | - | -| `GraphExecutor` | 使用 switch case | `EnhancedGraphExecutor` | -| `OperatorChain` | 过度抽象 | 直接在 `SimplePipeline` 中实现 | -| `DefaultOperatorChain` | 过度抽象 | 直接在 `SimplePipeline` 中实现 | - ---- - -## 📊 完整的架构图 - -``` -┌─────────────────────────────────────────────────────────┐ -│ API 层(接口定义) │ -├─────────────────────────────────────────────────────────┤ -│ Component │ -│ ├── ComponentType │ -│ ├── ComponentMetadata │ -│ └── LifecycleAware │ -│ │ -│ StreamingComponent extends Component │ -│ │ -│ DataSource Operator DataSink │ -│ extends Component extends Streaming extends Component│ -│ │ -│ NodeExecutor │ -│ ├── getSupportedNodeType() │ -│ └── buildFlux() │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ Core 层(核心实现) │ -├─────────────────────────────────────────────────────────┤ -│ NodeExecutorRegistry (管理所有 NodeExecutor) │ -│ ├── SourceNodeExecutor │ -│ ├── OperatorNodeExecutor │ -│ └── SinkNodeExecutor │ -│ │ -│ EnhancedGraphExecutor (无 switch case!) │ -│ └── execute() │ -│ │ -│ SimplePipeline │ -│ └── execute() │ -│ │ -│ SpringGraphBasedPipelineBuilder │ -│ └── buildFromGraph() │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ Connectors 层(具体实现) │ -├─────────────────────────────────────────────────────────┤ -│ KafkaSource, ConsoleSource │ -│ KafkaSourceCreator, ConsoleSourceCreator │ -│ │ -│ ConsoleSink │ -│ ConsoleSinkCreator │ -└─────────────────────────────────────────────────────────┘ - ↓ -┌─────────────────────────────────────────────────────────┐ -│ Operators 层(具体实现) │ -├─────────────────────────────────────────────────────────┤ -│ FilterOperator, MapOperator │ -│ FilterOperatorCreator, MapOperatorCreator │ -└─────────────────────────────────────────────────────────┘ -``` - ---- - -## 🎓 设计模式应用 - -### 1. 策略模式(Strategy Pattern) - -**应用场景**: -- `NodeExecutor` 体系:根据节点类型选择执行策略 -- `ComponentCreator` 体系:根据组件类型选择创建策略 - -**优势**: -- ✅ 消除 switch case -- ✅ 符合开闭原则 -- ✅ 易于扩展 - -### 2. 工厂模式(Factory Pattern) - -**应用场景**: -- `SpringSourceFactory` -- `SpringSinkFactory` -- `SpringOperatorFactory` - -**特点**: -- Spring 自动注入所有 Creator -- 使用 Map 存储类型到 Creator 的映射 - -### 3. 模板方法模式(Template Method Pattern) - -**应用场景**: -- `AbstractNodeExecutor`:定义构建流程,子类实现具体逻辑 - -```java -public abstract class AbstractNodeExecutor implements NodeExecutor { - - @Override - public final Flux buildFlux(StreamNode node, NodeExecutionContext context) { - // 1. 检查缓存 - // 2. 构建 Flux(模板方法) - Flux flux = doBuildFlux(node, context); - // 3. 缓存结果 - return flux; - } - - // 子类实现 - protected abstract Flux doBuildFlux(StreamNode node, NodeExecutionContext context); -} -``` - -### 4. 组合模式(Composite Pattern) - -**应用场景**: -- `SimplePipeline`:将 Source、Operators、Sink 组合成一个整体 - ---- - -## 🔄 泛型应用 - -### 1. 组件接口 - -```java -// 基础组件 -Component // C 是配置类型 - -// 流式组件 -StreamingComponent // IN 输入,OUT 输出,C 配置 -``` - -### 2. 具体实现 - -```java -// Source:只有输出类型 -DataSource extends Component - -// Operator:有输入和输出类型 -Operator extends StreamingComponent - -// Sink:只有输入类型 -DataSink extends Component -``` - -### 3. 执行器 - -```java -// 节点执行器 -NodeExecutor - -// 具体实现 -SourceNodeExecutor extends AbstractNodeExecutor -OperatorNodeExecutor extends AbstractNodeExecutor -``` - ---- - -## ✅ SOLID 原则遵守 - -### 1. 单一职责原则(SRP) - -- `NodeExecutor`:只负责构建节点的 Flux -- `NodeExecutionContext`:只负责提供上下文信息 -- `EnhancedGraphExecutor`:只负责协调执行 - -### 2. 开闭原则(OCP) - -- 新增节点类型:添加一个 `@Component` 的 `NodeExecutor` 实现 -- 新增组件类型:添加一个 `@Component` 的 `ComponentCreator` 实现 -- 无需修改现有代码 - -### 3. 里氏替换原则(LSP) - -- 所有 `NodeExecutor` 实现可互相替换 -- 所有 `Component` 实现可互相替换 - -### 4. 接口隔离原则(ISP) - -- `Component`:通用属性 -- `LifecycleAware`:生命周期管理 -- `StreamingComponent`:流式处理 -- 客户端只依赖需要的接口 - -### 5. 依赖倒置原则(DIP) - -- 依赖抽象(`NodeExecutor`),不依赖具体实现 -- 通过 Spring 注入,实现依赖倒置 - ---- - -## 📈 性能和可维护性提升 - -| 方面 | 改进前 | 改进后 | -|-----|-------|--------| -| switch case 数量 | 3+ | 0 | -| 接口层次 | 1-2 层 | 4-5 层(清晰的抽象) | -| 泛型使用 | 少 | 广泛使用,类型安全 | -| 可扩展性 | 需修改代码 | 添加 @Component 即可 | -| 代码重复 | 有缓存重复逻辑 | 统一在 AbstractNodeExecutor | -| 测试性 | 较难 | 每个执行器独立测试 | - ---- - -## 🚀 如何扩展 - -### 示例:添加自定义节点类型 - -```java -// 1. 定义新的节点类型 -public enum NodeType { - SOURCE, OPERATOR, SINK, - CUSTOM_TRANSFORM // 新增 -} - -// 2. 实现 NodeExecutor(添加 @Component) -@Component -public class CustomTransformNodeExecutor extends AbstractNodeExecutor { - - @Override - protected Flux doBuildFlux(StreamNode node, NodeExecutionContext context) { - // 实现自定义逻辑 - return Flux.just("custom"); - } - - @Override - public NodeType getSupportedNodeType() { - return NodeType.CUSTOM_TRANSFORM; - } -} - -// 3. 完成!Spring 自动发现并注册 -``` - ---- - -## 📝 总结 - -### 核心改进 - -1. ✅ **消除所有 switch case**:使用策略模式 -2. ✅ **增强抽象能力**:4-5 层接口继承 -3. ✅ **广泛使用泛型**:类型安全 -4. ✅ **删除无用类**:6 个类被删除 -5. ✅ **提升可扩展性**:符合 SOLID 原则 - -### 关键优势 - -- 🚀 **易扩展**:新增类型只需添加 @Component 类 -- 🧪 **易测试**:每个组件独立 -- 📖 **易理解**:清晰的层次结构 -- 🔧 **易维护**:低耦合、高内聚 -- ⚡ **高性能**:缓存机制、响应式流 - -### 架构特点 - -- **分层清晰**:API → Core → Impl -- **职责明确**:每个类只做一件事 -- **依赖倒置**:依赖抽象,不依赖具体 -- **开闭原则**:对扩展开放,对修改关闭 diff --git a/pipeline-framework/REFACTORING_SUMMARY.md b/pipeline-framework/REFACTORING_SUMMARY.md deleted file mode 100644 index c8cb039f6..000000000 --- a/pipeline-framework/REFACTORING_SUMMARY.md +++ /dev/null @@ -1,481 +0,0 @@ -# Pipeline Framework 重构总结 - -## 🎉 重构完成 - -本次重构主要聚焦三个方面: -1. **使用设计模式替代 switch case** -2. **使用 Spring 注解管理所有组件** -3. **配置 Reactor 线程池** - ---- - -## 📋 主要改动 - -### 1. 策略模式替代 Switch Case - -#### ❌ 重构前 - -```java -public Operator createOperator(OperatorType type, OperatorConfig config) { - switch (type) { - case FILTER: - return new FilterOperator(config); - case MAP: - return new MapOperator(config); - case AGGREGATE: - return new AggregateOperator(config); - default: - throw new IllegalArgumentException("Unsupported type: " + type); - } -} -``` - -**问题**: -- 每增加一个类型都要修改这个方法 -- 违反开闭原则 -- 代码耦合度高 - -#### ✅ 重构后 - -```java -// 1. 定义策略接口 -public interface OperatorCreator extends ComponentCreator, OperatorConfig> { - Mono> create(OperatorConfig config); - String getType(); -} - -// 2. 实现具体策略(每个类型一个 @Component 类) -@Component -public class FilterOperatorCreator implements OperatorCreator { - @Override - public Mono> create(OperatorConfig config) { - return Mono.fromCallable(() -> new FilterOperator<>(config)); - } - - @Override - public String getType() { - return "filter"; - } -} - -// 3. Spring 工厂自动注入所有策略 -@Component -public class SpringOperatorFactory { - private final Map creatorMap; - - // Spring 自动注入所有 OperatorCreator 实现 - public SpringOperatorFactory(List creators) { - this.creatorMap = new ConcurrentHashMap<>(); - for (OperatorCreator creator : creators) { - creatorMap.put(creator.getType(), creator); - } - } - - public Mono> createOperator(OperatorConfig config) { - String type = config.getType().name().toLowerCase(); - OperatorCreator creator = creatorMap.get(type); - return creator.create(config); // 无需 switch! - } -} -``` - -**优势**: -- ✅ 符合开闭原则:新增类型只需添加一个 `@Component` 类 -- ✅ 低耦合:每个策略独立 -- ✅ 易于测试:可以单独测试每个策略 -- ✅ Spring 自动管理:无需手动注册 - ---- - -### 2. Spring 注解管理组件 - -#### 新增的 Spring 组件 - -| 组件类型 | 注解 | 示例 | -|---------|-----|------| -| Creator(策略) | `@Component` | `FilterOperatorCreator` | -| Factory(工厂) | `@Component` | `SpringSourceFactory` | -| Builder(构建器) | `@Component` | `SpringGraphBasedPipelineBuilder` | -| Service(服务) | `@Service` | `PipelineExecutionService` | -| Config(配置) | `@Configuration` | `ReactorSchedulerConfig` | -| Properties(属性) | `@ConfigurationProperties` | `ReactorSchedulerProperties` | - -#### 依赖注入示例 - -```java -@Component -public class SpringGraphBasedPipelineBuilder { - - private final SpringSourceFactory sourceFactory; - private final SpringSinkFactory sinkFactory; - private final SpringOperatorFactory operatorFactory; - private final Scheduler pipelineScheduler; - - // 构造函数注入所有依赖 - public SpringGraphBasedPipelineBuilder( - SpringSourceFactory sourceFactory, - SpringSinkFactory sinkFactory, - SpringOperatorFactory operatorFactory, - @Qualifier("pipelineScheduler") Scheduler pipelineScheduler) { - this.sourceFactory = sourceFactory; - this.sinkFactory = sinkFactory; - this.operatorFactory = operatorFactory; - this.pipelineScheduler = pipelineScheduler; - } -} -``` - ---- - -### 3. Reactor 线程池配置 - -#### 配置文件(application.yml) - -```yaml -reactor: - scheduler: - # IO 密集型操作线程池 - io: - pool-size: 100 - queue-size: 1000 - thread-name-prefix: reactor-io- - - # CPU 密集型操作线程池 - compute: - pool-size: 0 # 0 = CPU 核心数 - thread-name-prefix: reactor-compute- - - # 有界弹性线程池(阻塞操作) - bounded-elastic: - pool-size: 200 - queue-size: 10000 - ttl-seconds: 60 - thread-name-prefix: reactor-bounded- - - # Pipeline 执行专用线程池 - pipeline: - pool-size: 50 - queue-size: 500 - thread-name-prefix: pipeline-exec- -``` - -#### Scheduler Bean 定义 - -```java -@Configuration -public class ReactorSchedulerConfig { - - @Bean(name = "ioScheduler", destroyMethod = "dispose") - public Scheduler ioScheduler(ReactorSchedulerProperties properties) { - ReactorSchedulerProperties.SchedulerConfig config = properties.getIo(); - return Schedulers.newBoundedElastic( - config.getPoolSize(), - config.getQueueSize(), - config.getThreadNamePrefix(), - 60, - true - ); - } - - // ... 其他 Scheduler Bean -} -``` - -#### 使用 Scheduler - -```java -@Component -public class KafkaSourceCreator implements SourceCreator { - - private final Scheduler ioScheduler; - - public KafkaSourceCreator(@Qualifier("ioScheduler") Scheduler ioScheduler) { - this.ioScheduler = ioScheduler; - } - - @Override - public Mono> create(SourceConfig config) { - return Mono.fromCallable(() -> new KafkaSource<>(config)) - .subscribeOn(ioScheduler); // 在 IO 线程池执行 - } -} -``` - ---- - -## 📊 架构对比 - -### 重构前 - -``` -┌──────────────────────────────────┐ -│ 手动创建工厂和组件 │ -│ - switch case 判断类型 │ -│ - 硬编码组件创建逻辑 │ -│ - 无线程池管理 │ -└──────────────────────────────────┘ -``` - -### 重构后 - -``` -┌──────────────────────────────────┐ -│ Spring 容器 │ -│ - 自动扫描 @Component │ -│ - 依赖注入 │ -│ - 生命周期管理 │ -└──────────────────────────────────┘ - ↓ -┌──────────────────────────────────┐ -│ 策略模式 (Creator) │ -│ - FilterOperatorCreator │ -│ - MapOperatorCreator │ -│ - KafkaSourceCreator │ -│ - ConsoleSinkCreator │ -└──────────────────────────────────┘ - ↓ -┌──────────────────────────────────┐ -│ 工厂模式 (Factory) │ -│ - SpringSourceFactory │ -│ - SpringSinkFactory │ -│ - SpringOperatorFactory │ -└──────────────────────────────────┘ - ↓ -┌──────────────────────────────────┐ -│ 构建器 (Builder) │ -│ - SpringGraphBasedPipelineBuilder│ -└──────────────────────────────────┘ - ↓ -┌──────────────────────────────────┐ -│ 服务层 (Service) │ -│ - PipelineExecutionService │ -└──────────────────────────────────┘ -``` - ---- - -## 📁 新增文件列表 - -### API 层(策略接口) -- `pipeline-api/src/main/java/com/pipeline/framework/api/strategy/ComponentCreator.java` -- `pipeline-api/src/main/java/com/pipeline/framework/api/strategy/SourceCreator.java` -- `pipeline-api/src/main/java/com/pipeline/framework/api/strategy/SinkCreator.java` -- `pipeline-api/src/main/java/com/pipeline/framework/api/strategy/OperatorCreator.java` - -### Core 层(工厂、配置) -- `pipeline-core/src/main/java/com/pipeline/framework/core/factory/SpringSourceFactory.java` -- `pipeline-core/src/main/java/com/pipeline/framework/core/factory/SpringSinkFactory.java` -- `pipeline-core/src/main/java/com/pipeline/framework/core/factory/SpringOperatorFactory.java` -- `pipeline-core/src/main/java/com/pipeline/framework/core/builder/SpringGraphBasedPipelineBuilder.java` -- `pipeline-core/src/main/java/com/pipeline/framework/core/service/PipelineExecutionService.java` -- `pipeline-core/src/main/java/com/pipeline/framework/core/config/ReactorSchedulerConfig.java` -- `pipeline-core/src/main/java/com/pipeline/framework/core/config/ReactorSchedulerProperties.java` - -### Connectors 层(具体策略实现) -- `pipeline-connectors/src/main/java/com/pipeline/framework/connectors/console/ConsoleSourceCreator.java` -- `pipeline-connectors/src/main/java/com/pipeline/framework/connectors/console/ConsoleSinkCreator.java` -- `pipeline-connectors/src/main/java/com/pipeline/framework/connectors/kafka/KafkaSourceCreator.java` - -### Operators 层(具体策略实现) -- `pipeline-operators/src/main/java/com/pipeline/framework/operators/filter/FilterOperatorCreator.java` -- `pipeline-operators/src/main/java/com/pipeline/framework/operators/map/MapOperatorCreator.java` - -### 文档 -- `DESIGN_PATTERN_EXPLANATION.md` - 设计模式详解 -- `SPRING_REACTOR_GUIDE.md` - Spring + Reactor 集成指南 -- `REFACTORING_SUMMARY.md` - 重构总结(本文档) - ---- - -## 🎯 如何添加新组件 - -### 示例:添加一个新的 AggregateOperator - -#### 步骤 1:实现 Operator - -```java -public class AggregateOperator implements Operator { - - @Override - public Flux apply(Flux input) { - return input - .window(Duration.ofSeconds(5)) - .flatMap(window -> window.reduce(...)) - .cast(...); - } -} -``` - -#### 步骤 2:创建 Creator(添加 @Component) - -```java -@Component // 就这么简单! -public class AggregateOperatorCreator implements OperatorCreator { - - private final Scheduler computeScheduler; - - public AggregateOperatorCreator(@Qualifier("computeScheduler") Scheduler computeScheduler) { - this.computeScheduler = computeScheduler; - } - - @Override - public Mono> create(OperatorConfig config) { - return Mono.fromCallable(() -> new AggregateOperator<>(config)) - .subscribeOn(computeScheduler); - } - - @Override - public String getType() { - return "aggregate"; - } -} -``` - -#### 步骤 3:完成! - -不需要修改任何其他代码: -- ✅ Spring 自动扫描 `AggregateOperatorCreator` -- ✅ 自动注入到 `SpringOperatorFactory` -- ✅ 自动在 `creatorMap` 中注册 - ---- - -## 🚀 使用示例 - -### 完整的 Pipeline 创建和执行 - -```java -@Service -public class MyPipelineService { - - private final PipelineExecutionService executionService; - - public MyPipelineService(PipelineExecutionService executionService) { - this.executionService = executionService; - } - - public Mono runPipeline() { - // 1. 创建 Graph - StreamGraph graph = buildGraph(); - - // 2. 执行(所有组件创建都由 Spring 管理) - return executionService.execute(graph); - } - - private StreamGraph buildGraph() { - DefaultStreamGraph graph = new DefaultStreamGraph( - "my-pipeline", - "示例数据管道", - GraphType.STREAMING - ); - - // 添加节点 - DefaultStreamNode sourceNode = new DefaultStreamNode( - "source-1", "Console Source", NodeType.SOURCE - ); - sourceNode.setConfig(Map.of( - "type", "console", // Spring 会自动找到 ConsoleSourceCreator - "count", 10 - )); - graph.addNode(sourceNode); - - DefaultStreamNode filterNode = new DefaultStreamNode( - "operator-1", "Filter", NodeType.OPERATOR - ); - filterNode.setOperatorType("FILTER"); // Spring 会自动找到 FilterOperatorCreator - filterNode.setConfig(Map.of("name", "filter-empty")); - graph.addNode(filterNode); - - DefaultStreamNode sinkNode = new DefaultStreamNode( - "sink-1", "Console Sink", NodeType.SINK - ); - sinkNode.setConfig(Map.of( - "type", "console" // Spring 会自动找到 ConsoleSinkCreator - )); - graph.addNode(sinkNode); - - // 添加边 - graph.addEdge(new DefaultStreamEdge("source-1", "operator-1")); - graph.addEdge(new DefaultStreamEdge("operator-1", "sink-1")); - - return graph; - } -} -``` - ---- - -## 📈 性能和可维护性提升 - -### 性能提升 - -| 方面 | 改进 | -|-----|------| -| 线程管理 | 针对不同场景使用专用线程池 | -| 资源利用 | IO/Compute 线程池分离,避免阻塞 | -| 扩展性 | 无需修改核心代码,性能不受组件数量影响 | - -### 可维护性提升 - -| 方面 | 改进 | -|-----|------| -| 代码结构 | 清晰的分层架构 | -| 扩展性 | 新增组件无需修改现有代码 | -| 测试性 | 每个组件独立,易于单元测试 | -| 配置 | 线程池等参数可通过配置文件调整 | - ---- - -## 🔍 Scheduler 使用矩阵 - -| 场景 | 推荐 Scheduler | 配置 Key | -|-----|---------------|---------| -| 数据库查询 | `ioScheduler` | `reactor.scheduler.io` | -| HTTP 请求 | `ioScheduler` | `reactor.scheduler.io` | -| 消息队列 | `ioScheduler` | `reactor.scheduler.io` | -| 数据转换 | `computeScheduler` | `reactor.scheduler.compute` | -| 数据计算 | `computeScheduler` | `reactor.scheduler.compute` | -| JDBC 调用 | `boundedElasticScheduler` | `reactor.scheduler.bounded-elastic` | -| 阻塞 API | `boundedElasticScheduler` | `reactor.scheduler.bounded-elastic` | -| Pipeline 执行 | `pipelineScheduler` | `reactor.scheduler.pipeline` | -| Graph 构建 | `pipelineScheduler` | `reactor.scheduler.pipeline` | - ---- - -## 📚 相关文档 - -1. **DESIGN_PATTERN_EXPLANATION.md** - 详细的设计模式应用说明 -2. **SPRING_REACTOR_GUIDE.md** - Spring 和 Reactor 集成指南 -3. **ARCHITECTURE_EXPLANATION.md** - 整体架构说明 -4. **COMPLETE_EXAMPLE.md** - 完整的使用示例 - ---- - -## ✅ 总结 - -### 核心改进 - -1. **策略模式** - 替代 switch case,符合开闭原则 -2. **Spring 依赖注入** - 自动管理所有组件 -3. **Reactor 线程池** - 针对不同场景优化性能 -4. **清晰的架构** - 分层明确,职责清晰 - -### 设计原则 - -- ✅ 单一职责原则(SRP) -- ✅ 开闭原则(OCP) -- ✅ 里氏替换原则(LSP) -- ✅ 接口隔离原则(ISP) -- ✅ 依赖倒置原则(DIP) - -### 关键优势 - -- 🚀 **高性能** - 专用线程池优化 -- 🔧 **易扩展** - 新增组件只需一个 `@Component` 类 -- 🧪 **易测试** - 组件独立,依赖注入方便 mock -- 📖 **易理解** - 清晰的设计模式和分层架构 -- ⚙️ **易配置** - 通过配置文件调整参数 - ---- - -**重构完成!项目现在拥有更清晰的设计、更好的性能和更强的可扩展性!** 🎉 diff --git a/pipeline-framework/SPRING_REACTOR_GUIDE.md b/pipeline-framework/SPRING_REACTOR_GUIDE.md deleted file mode 100644 index 370645f46..000000000 --- a/pipeline-framework/SPRING_REACTOR_GUIDE.md +++ /dev/null @@ -1,531 +0,0 @@ -# Spring + Reactor 集成指南 - -## 📚 概述 - -本文档详细说明如何在 Pipeline Framework 中使用 Spring 和 Reactor,包括线程池配置、依赖注入和最佳实践。 - -## 🔧 Reactor 线程池配置 - -### 1. 配置文件(application.yml) - -```yaml -reactor: - scheduler: - # IO 密集型操作线程池 - io: - pool-size: 100 - queue-size: 1000 - thread-name-prefix: reactor-io- - - # CPU 密集型操作线程池 - compute: - pool-size: 0 # 0 表示使用 CPU 核心数 - thread-name-prefix: reactor-compute- - - # 有界弹性线程池(阻塞操作) - bounded-elastic: - pool-size: 200 - queue-size: 10000 - ttl-seconds: 60 - thread-name-prefix: reactor-bounded- - - # Pipeline 执行专用线程池 - pipeline: - pool-size: 50 - queue-size: 500 - thread-name-prefix: pipeline-exec- -``` - -### 2. Scheduler Bean 配置 - -```java -@Configuration -public class ReactorSchedulerConfig { - - @Bean(name = "ioScheduler", destroyMethod = "dispose") - public Scheduler ioScheduler(ReactorSchedulerProperties properties) { - ReactorSchedulerProperties.SchedulerConfig config = properties.getIo(); - - return Schedulers.newBoundedElastic( - config.getPoolSize(), - config.getQueueSize(), - config.getThreadNamePrefix(), - 60, - true - ); - } - - @Bean(name = "computeScheduler", destroyMethod = "dispose") - public Scheduler computeScheduler(ReactorSchedulerProperties properties) { - ReactorSchedulerProperties.SchedulerConfig config = properties.getCompute(); - - int poolSize = config.getPoolSize(); - if (poolSize <= 0) { - poolSize = Runtime.getRuntime().availableProcessors(); - } - - return Schedulers.newParallel( - config.getThreadNamePrefix(), - poolSize, - true - ); - } - - @Bean(name = "boundedElasticScheduler", destroyMethod = "dispose") - public Scheduler boundedElasticScheduler(ReactorSchedulerProperties properties) { - ReactorSchedulerProperties.BoundedElasticConfig config = properties.getBoundedElastic(); - - return Schedulers.newBoundedElastic( - config.getPoolSize(), - config.getQueueSize(), - config.getThreadNamePrefix(), - config.getTtlSeconds(), - true - ); - } - - @Bean(name = "pipelineScheduler", destroyMethod = "dispose") - public Scheduler pipelineScheduler(ReactorSchedulerProperties properties) { - ReactorSchedulerProperties.SchedulerConfig config = properties.getPipeline(); - - return Schedulers.newBoundedElastic( - config.getPoolSize(), - config.getQueueSize(), - config.getThreadNamePrefix(), - 60, - true - ); - } -} -``` - -### 3. Scheduler 使用场景 - -#### IO Scheduler -**适用场景**: -- 数据库查询(SELECT 操作) -- HTTP/REST API 调用 -- 消息队列操作(Kafka、RabbitMQ) -- 文件读写 -- 网络 IO - -**示例**: -```java -@Component -public class KafkaSourceCreator implements SourceCreator { - - private final Scheduler ioScheduler; - - public KafkaSourceCreator(@Qualifier("ioScheduler") Scheduler ioScheduler) { - this.ioScheduler = ioScheduler; - } - - @Override - public Mono> create(SourceConfig config) { - return Mono.fromCallable(() -> { - // 创建 Kafka Source(可能涉及网络连接) - return new KafkaSource<>(config); - }) - .subscribeOn(ioScheduler); - } -} -``` - -#### Compute Scheduler -**适用场景**: -- 数据转换 -- 计算密集型任务 -- 数据聚合 -- 编解码 - -**示例**: -```java -@Component -public class MapOperatorCreator implements OperatorCreator { - - private final Scheduler computeScheduler; - - public MapOperatorCreator(@Qualifier("computeScheduler") Scheduler computeScheduler) { - this.computeScheduler = computeScheduler; - } - - @Override - public Mono> create(OperatorConfig config) { - return Mono.fromCallable(() -> { - // 创建计算密集型 Operator - return new MapOperator<>(config); - }) - .subscribeOn(computeScheduler); - } -} -``` - -#### Bounded Elastic Scheduler -**适用场景**: -- 阻塞 API 包装(如 JDBC) -- 同步第三方库调用 -- 文件系统操作 -- 不支持异步的遗留代码 - -**示例**: -```java -@Service -public class JobService { - - private final JobMapper jobMapper; - private final Scheduler boundedElasticScheduler; - - public JobService( - JobMapper jobMapper, - @Qualifier("boundedElasticScheduler") Scheduler boundedElasticScheduler) { - this.jobMapper = jobMapper; - this.boundedElasticScheduler = boundedElasticScheduler; - } - - public Mono getByJobId(String jobId) { - // 将 MyBatis 的阻塞调用包装为响应式 - return Mono.fromCallable(() -> jobMapper.selectByJobId(jobId)) - .subscribeOn(boundedElasticScheduler); - } -} -``` - -#### Pipeline Scheduler -**适用场景**: -- Pipeline 主流程执行 -- Graph 构建 -- Job 调度 -- 任务协调 - -**示例**: -```java -@Component -public class SpringGraphBasedPipelineBuilder { - - private final Scheduler pipelineScheduler; - - public SpringGraphBasedPipelineBuilder( - @Qualifier("pipelineScheduler") Scheduler pipelineScheduler) { - this.pipelineScheduler = pipelineScheduler; - } - - public Mono> buildFromGraph(StreamGraph graph) { - return Mono.defer(() -> { - // 构建 Pipeline 逻辑 - return createPipeline(graph); - }) - .subscribeOn(pipelineScheduler); - } -} -``` - ---- - -## 🎯 Spring 依赖注入最佳实践 - -### 1. 构造函数注入(推荐) - -```java -@Component -public class MyComponent { - - private final Scheduler ioScheduler; - private final SpringSourceFactory sourceFactory; - - // 构造函数注入(Spring 推荐) - public MyComponent( - @Qualifier("ioScheduler") Scheduler ioScheduler, - SpringSourceFactory sourceFactory) { - this.ioScheduler = ioScheduler; - this.sourceFactory = sourceFactory; - } -} -``` - -**优势**: -- 不可变(final 字段) -- 易于测试(可以直接传入 mock 对象) -- 明确依赖关系 - -### 2. 使用 @Qualifier 区分同类型 Bean - -```java -@Component -public class MyService { - - private final Scheduler ioScheduler; - private final Scheduler computeScheduler; - - public MyService( - @Qualifier("ioScheduler") Scheduler ioScheduler, - @Qualifier("computeScheduler") Scheduler computeScheduler) { - this.ioScheduler = ioScheduler; - this.computeScheduler = computeScheduler; - } -} -``` - -### 3. 使用 List 注入所有实现 - -```java -@Component -public class SpringOperatorFactory { - - private final Map creatorMap; - - // Spring 会自动注入所有 OperatorCreator 实现 - public SpringOperatorFactory(List creators) { - this.creatorMap = new ConcurrentHashMap<>(); - for (OperatorCreator creator : creators) { - creatorMap.put(creator.getType(), creator); - } - } -} -``` - ---- - -## 📖 完整示例 - -### 场景:创建一个新的 MySQL Source - -#### 步骤 1:实现 DataSource - -```java -public class MysqlSource implements DataSource> { - - private final SourceConfig config; - private final R2dbcEntityTemplate template; - - public MysqlSource(SourceConfig config, R2dbcEntityTemplate template) { - this.config = config; - this.template = template; - } - - @Override - public Flux> read() { - String sql = config.getProperty("sql"); - - return template - .getDatabaseClient() - .sql(sql) - .fetch() - .all(); - } - - @Override - public String getName() { - return config.getProperty("name", "mysql-source"); - } - - @Override - public SourceType getType() { - return SourceType.MYSQL; - } -} -``` - -#### 步骤 2:创建 Creator(添加 @Component) - -```java -@Component -public class MysqlSourceCreator implements SourceCreator { - - private final Scheduler ioScheduler; - private final R2dbcEntityTemplate template; - - public MysqlSourceCreator( - @Qualifier("ioScheduler") Scheduler ioScheduler, - R2dbcEntityTemplate template) { - this.ioScheduler = ioScheduler; - this.template = template; - } - - @Override - public Mono> create(SourceConfig config) { - return Mono.fromCallable(() -> new MysqlSource(config, template)) - .subscribeOn(ioScheduler); - } - - @Override - public String getType() { - return "mysql"; - } - - @Override - public int getOrder() { - return 10; - } -} -``` - -#### 步骤 3:使用 - -```java -@Service -public class PipelineService { - - private final SpringSourceFactory sourceFactory; - - public PipelineService(SpringSourceFactory sourceFactory) { - this.sourceFactory = sourceFactory; - } - - public Mono> createMysqlSource() { - SourceConfig config = new SimpleSourceConfig(Map.of( - "type", "mysql", - "sql", "SELECT * FROM users" - )); - - // 自动使用 MysqlSourceCreator - return sourceFactory.createSource(config); - } -} -``` - ---- - -## ⚡ 性能优化建议 - -### 1. 合理设置线程池大小 - -**IO 密集型**: -```yaml -reactor: - scheduler: - io: - pool-size: 100 # 可以较大,因为线程大部分时间在等待 IO -``` - -**CPU 密集型**: -```yaml -reactor: - scheduler: - compute: - pool-size: 0 # 使用 CPU 核心数,避免过度上下文切换 -``` - -### 2. 避免在 Compute Scheduler 上执行阻塞操作 - -**❌ 错误示例**: -```java -return Mono.fromCallable(() -> { - Thread.sleep(1000); // 阻塞! - return result; -}) -.subscribeOn(computeScheduler); // 不应该在 compute 上执行阻塞操作 -``` - -**✅ 正确示例**: -```java -return Mono.fromCallable(() -> { - Thread.sleep(1000); // 阻塞操作 - return result; -}) -.subscribeOn(boundedElasticScheduler); // 使用 bounded-elastic -``` - -### 3. 使用 subscribeOn vs publishOn - -**subscribeOn**:决定订阅(开始执行)时使用的线程 -```java -Mono.fromCallable(() -> blockingCall()) - .subscribeOn(boundedElasticScheduler) // 在这个线程池执行 -``` - -**publishOn**:切换后续操作的线程 -```java -Flux.range(1, 10) - .map(i -> i * 2) - .publishOn(computeScheduler) // 后续操作在这个线程池执行 - .map(i -> i + 1) -``` - -### 4. 监控线程池 - -```yaml -management: - endpoints: - web: - exposure: - include: health,metrics,prometheus - metrics: - export: - prometheus: - enabled: true -``` - -查看指标: -- `reactor.scheduler.threads.active` -- `reactor.scheduler.threads.max` -- `reactor.scheduler.tasks.pending` - ---- - -## 🔍 调试技巧 - -### 1. 打印当前线程 - -```java -Mono.fromCallable(() -> { - System.out.println("Executing on: " + Thread.currentThread().getName()); - return doWork(); -}) -.subscribeOn(ioScheduler); -``` - -### 2. 使用 Hooks 全局监控 - -```java -@Configuration -public class ReactorDebugConfig { - - @PostConstruct - public void init() { - // 开发环境启用调试 - Hooks.onOperatorDebug(); - } -} -``` - -### 3. 日志配置 - -```yaml -logging: - level: - reactor.core: DEBUG - reactor.netty: DEBUG -``` - ---- - -## 📝 总结 - -### Scheduler 选择矩阵 - -| 场景 | 推荐 Scheduler | 原因 | -|-----|--------------|-----| -| 数据库查询 | `ioScheduler` | IO 密集型 | -| HTTP 请求 | `ioScheduler` | IO 密集型 | -| 数据转换 | `computeScheduler` | CPU 密集型 | -| JDBC 调用 | `boundedElasticScheduler` | 阻塞操作 | -| Pipeline 执行 | `pipelineScheduler` | 任务协调 | - -### Spring 注解使用 - -| 注解 | 用途 | 示例 | -|-----|-----|-----| -| `@Component` | 通用组件 | Creator 类 | -| `@Service` | 业务逻辑 | PipelineService | -| `@Configuration` | 配置类 | ReactorSchedulerConfig | -| `@Bean` | Bean 定义 | Scheduler Bean | -| `@Qualifier` | 区分同类型 Bean | 多个 Scheduler | -| `@ConfigurationProperties` | 配置绑定 | ReactorSchedulerProperties | - -### 核心原则 - -1. **正确的线程池,正确的任务** -2. **构造函数注入优于字段注入** -3. **使用 @Qualifier 明确指定 Bean** -4. **监控线程池使用情况** -5. **开发环境开启调试模式** diff --git a/pipeline-framework/STRUCTURE.md b/pipeline-framework/STRUCTURE.md new file mode 100644 index 000000000..149f6ee04 --- /dev/null +++ b/pipeline-framework/STRUCTURE.md @@ -0,0 +1,201 @@ +# Pipeline Framework 项目结构 + +## 模块说明 + +### pipeline-api +核心API定义模块,包含所有接口定义。 + +``` +pipeline-api/src/main/java/com/pipeline/framework/api/ +├── connector/ # Connector接口 +│ ├── ConnectorReader # 数据读取器接口 +│ └── ConnectorWriter # 数据写入器接口 +├── source/ # Source接口 +├── sink/ # Sink接口 +├── operator/ # Operator接口 +├── job/ # Job接口 +├── graph/ # StreamGraph接口 +├── executor/ # Executor接口 +└── scheduler/ # Scheduler接口 +``` + +### pipeline-core +框架核心实现模块。 + +``` +pipeline-core/src/main/java/com/pipeline/framework/core/ +├── connector/ # Connector适配器 +│ ├── ReaderSourceAdapter # Reader → Flux适配 +│ └── WriterSinkAdapter # Writer → Mono适配 +├── builder/ # Pipeline构建器 +├── factory/ # 组件工厂 +├── graph/ # Graph执行器 +├── pipeline/ # Pipeline实现 +├── runtime/ # 运行时 +├── scheduler/ # 调度器配置 +└── service/ # 服务层 +``` + +### pipeline-connectors +Connector实现模块。 + +``` +pipeline-connectors/src/main/java/com/pipeline/framework/connectors/ +├── jdbc/ # JDBC Connector +│ ├── JdbcConnectorReader +│ └── JdbcConnectorWriter +├── kafka/ # Kafka Connector +├── console/ # Console Connector +└── ... # 其他Connector +``` + +### 其他模块 +- **pipeline-operators**: 数据处理算子实现 +- **pipeline-scheduler**: 任务调度实现 +- **pipeline-executor**: 任务执行器实现 +- **pipeline-state**: 状态管理 +- **pipeline-checkpoint**: 检查点容错 +- **pipeline-metrics**: 监控指标 +- **pipeline-web**: Web API +- **pipeline-starter**: Spring Boot启动模块 + +## Connector开发 + +### 1. 实现ConnectorReader + +```java +package com.pipeline.framework.connectors.custom; + +import com.pipeline.framework.api.connector.ConnectorReader; +import java.util.List; + +public class MyReader implements ConnectorReader { + + @Override + public void open() throws Exception { + // 初始化,打开连接 + } + + @Override + public List readBatch(int batchSize) throws Exception { + // 批量读取数据 + List batch = new ArrayList<>(); + // ... 读取逻辑 + return batch; + } + + @Override + public boolean hasNext() { + // 是否还有数据 + return true; + } + + @Override + public void close() throws Exception { + // 清理资源,关闭连接 + } +} +``` + +### 2. 实现ConnectorWriter + +```java +package com.pipeline.framework.connectors.custom; + +import com.pipeline.framework.api.connector.ConnectorWriter; +import java.util.List; + +public class MyWriter implements ConnectorWriter { + + @Override + public void open() throws Exception { + // 初始化,打开连接 + } + + @Override + public void write(YourDataType record) throws Exception { + // 单条写入 + } + + @Override + public void writeBatch(List records) throws Exception { + // 批量写入 + } + + @Override + public void flush() throws Exception { + // 刷新缓冲 + } + + @Override + public void close() throws Exception { + // 清理资源,关闭连接 + } +} +``` + +### 3. 在框架中使用 + +```java +// 创建Reader +MyReader reader = new MyReader(); + +// 使用适配器转换为Source +ReaderSourceAdapter source = + new ReaderSourceAdapter<>(reader, 1000, config); + +// 获取响应式流 +Flux stream = source.getDataStream(); +``` + +## 依赖关系 + +``` +pipeline-starter + ├── pipeline-web + ├── pipeline-executor + ├── pipeline-scheduler + └── pipeline-core + ├── pipeline-api + ├── pipeline-connectors + │ └── pipeline-api + ├── pipeline-operators + │ └── pipeline-api + ├── pipeline-state + │ └── pipeline-api + └── pipeline-checkpoint + └── pipeline-api +``` + +## 编译和运行 + +```bash +# 编译整个项目 +mvn clean install + +# 只编译某个模块 +cd pipeline-connectors +mvn clean install + +# 运行应用 +cd pipeline-starter +mvn spring-boot:run +``` + +## 添加新的Connector + +1. 在 `pipeline-connectors` 模块创建新包 +2. 实现 `ConnectorReader` 和/或 `ConnectorWriter` +3. 添加必要的依赖到 `pipeline-connectors/pom.xml` +4. 使用 `ReaderSourceAdapter` 或 `WriterSinkAdapter` 进行集成 + +## 注意事项 + +- Connector接口位于 `pipeline-api` 模块,不依赖Reactor +- 适配器位于 `pipeline-core` 模块,负责转换为响应式流 +- Connector实现位于 `pipeline-connectors` 模块 +- 外部依赖(如JDBC驱动)标记为 `optional`,按需引入 + +--- + +**简洁、清晰、易用** 🚀 diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/Connector.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/Connector.java new file mode 100644 index 000000000..0baf366c8 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/Connector.java @@ -0,0 +1,59 @@ +package com.pipeline.framework.api.connector; + +/** + * Connector基础接口。 + *

+ * 所有连接器的顶层抽象,提供生命周期管理和元数据访问。 + * 不依赖Reactor,可以独立使用。 + *

+ * + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface Connector { + + /** + * 获取连接器名称。 + * + * @return 连接器名称 + */ + String getName(); + + /** + * 获取连接器类型。 + * + * @return 连接器类型 + */ + ConnectorType getType(); + + /** + * 获取连接器配置。 + * + * @return 配置对象 + */ + C getConfig(); + + /** + * 获取连接器元数据。 + * + * @return 元数据 + */ + default ConnectorMetadata getMetadata() { + return ConnectorMetadata.builder() + .name(getName()) + .type(getType()) + .version("1.0.0") + .build(); + } + + /** + * 验证配置是否有效。 + * + * @return true表示配置有效 + * @throws ConnectorException 配置无效时抛出 + */ + default boolean validate() throws ConnectorException { + return getConfig() != null; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorConfig.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorConfig.java new file mode 100644 index 000000000..da7eabb0c --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorConfig.java @@ -0,0 +1,104 @@ +package com.pipeline.framework.api.connector; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Connector配置基类。 + *

+ * 所有连接器配置的基类,提供通用配置能力。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public abstract class ConnectorConfig implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 连接器名称 + */ + private String name; + + /** + * 扩展属性 + */ + private Map properties = new HashMap<>(); + + /** + * 连接超时时间(毫秒) + */ + private int connectionTimeout = 30000; + + /** + * 是否启用重试 + */ + private boolean retryEnabled = true; + + /** + * 最大重试次数 + */ + private int maxRetries = 3; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setProperty(String key, Object value) { + this.properties.put(key, value); + } + + public Object getProperty(String key) { + return this.properties.get(key); + } + + public T getProperty(String key, Class type) { + Object value = this.properties.get(key); + return type.cast(value); + } + + public int getConnectionTimeout() { + return connectionTimeout; + } + + public void setConnectionTimeout(int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + public boolean isRetryEnabled() { + return retryEnabled; + } + + public void setRetryEnabled(boolean retryEnabled) { + this.retryEnabled = retryEnabled; + } + + public int getMaxRetries() { + return maxRetries; + } + + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + /** + * 验证配置是否有效。 + * + * @throws IllegalArgumentException 配置无效时抛出 + */ + public abstract void validate() throws IllegalArgumentException; +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorException.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorException.java new file mode 100644 index 000000000..47a5b3b66 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorException.java @@ -0,0 +1,47 @@ +package com.pipeline.framework.api.connector; + +/** + * Connector异常。 + * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class ConnectorException extends Exception { + + private static final long serialVersionUID = 1L; + + private final ConnectorType connectorType; + private final String connectorName; + + public ConnectorException(String message) { + super(message); + this.connectorType = null; + this.connectorName = null; + } + + public ConnectorException(String message, Throwable cause) { + super(message, cause); + this.connectorType = null; + this.connectorName = null; + } + + public ConnectorException(String message, ConnectorType connectorType, String connectorName) { + super(message); + this.connectorType = connectorType; + this.connectorName = connectorName; + } + + public ConnectorException(String message, Throwable cause, ConnectorType connectorType, String connectorName) { + super(message, cause); + this.connectorType = connectorType; + this.connectorName = connectorName; + } + + public ConnectorType getConnectorType() { + return connectorType; + } + + public String getConnectorName() { + return connectorName; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorMetadata.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorMetadata.java new file mode 100644 index 000000000..37dbb9d55 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorMetadata.java @@ -0,0 +1,106 @@ +package com.pipeline.framework.api.connector; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +/** + * Connector元数据。 + * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class ConnectorMetadata { + + private final String name; + private final ConnectorType type; + private final String version; + private final String description; + private final Instant createTime; + private final Map attributes; + + private ConnectorMetadata(Builder builder) { + this.name = builder.name; + this.type = builder.type; + this.version = builder.version; + this.description = builder.description; + this.createTime = builder.createTime; + this.attributes = new HashMap<>(builder.attributes); + } + + public String getName() { + return name; + } + + public ConnectorType getType() { + return type; + } + + public String getVersion() { + return version; + } + + public String getDescription() { + return description; + } + + public Instant getCreateTime() { + return createTime; + } + + public Map getAttributes() { + return new HashMap<>(attributes); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String name; + private ConnectorType type; + private String version = "1.0.0"; + private String description; + private Instant createTime = Instant.now(); + private Map attributes = new HashMap<>(); + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder type(ConnectorType type) { + this.type = type; + return this; + } + + public Builder version(String version) { + this.version = version; + return this; + } + + public Builder description(String description) { + this.description = description; + return this; + } + + public Builder createTime(Instant createTime) { + this.createTime = createTime; + return this; + } + + public Builder attribute(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public Builder attributes(Map attributes) { + this.attributes.putAll(attributes); + return this; + } + + public ConnectorMetadata build() { + return new ConnectorMetadata(this); + } + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorReader.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorReader.java new file mode 100644 index 000000000..2352b5d20 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorReader.java @@ -0,0 +1,94 @@ +package com.pipeline.framework.api.connector; + +import java.util.List; + +/** + * Connector数据读取器。 + *

+ * 提供批量数据读取能力,不依赖Reactor。 + * 支持泛型配置,提供更强的类型安全。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface ConnectorReader extends Connector { + + /** + * 打开读取器。 + * + * @throws Exception 打开失败 + */ + void open() throws Exception; + + /** + * 批量读取数据。 + * + * @param batchSize 批次大小 + * @return 数据列表,如果没有更多数据返回null或空列表 + * @throws Exception 读取失败 + */ + List readBatch(int batchSize) throws Exception; + + /** + * 是否还有更多数据。 + * + * @return true表示还有数据 + */ + boolean hasNext(); + + /** + * 关闭读取器,释放资源。 + * + * @throws Exception 关闭失败 + */ + void close() throws Exception; + + /** + * 获取当前读取位置(用于断点续传)。 + * + * @return 位置信息 + */ + default Object getCheckpoint() { + return null; + } + + /** + * 从指定位置开始读取(断点续传)。 + * + * @param checkpoint 检查点位置 + * @throws Exception 定位失败 + */ + default void seekToCheckpoint(Object checkpoint) throws Exception { + // 默认不支持 + } + + /** + * 是否支持断点续传。 + * + * @return true表示支持 + */ + default boolean supportsCheckpoint() { + return false; + } + + /** + * 获取读取进度(0.0-1.0)。 + * + * @return 进度百分比 + */ + default double getProgress() { + return -1.0; + } + + /** + * 获取已读取的记录数。 + * + * @return 记录数 + */ + default long getReadCount() { + return 0; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorType.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorType.java new file mode 100644 index 000000000..d4e200b4c --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorType.java @@ -0,0 +1,54 @@ +package com.pipeline.framework.api.connector; + +/** + * Connector类型枚举。 + * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public enum ConnectorType { + /** + * JDBC数据库连接器 + */ + JDBC, + + /** + * Kafka消息队列连接器 + */ + KAFKA, + + /** + * Redis缓存连接器 + */ + REDIS, + + /** + * 文件系统连接器 + */ + FILE, + + /** + * HTTP/REST API连接器 + */ + HTTP, + + /** + * MongoDB连接器 + */ + MONGODB, + + /** + * Elasticsearch连接器 + */ + ELASTICSEARCH, + + /** + * 控制台连接器(用于调试) + */ + CONSOLE, + + /** + * 自定义连接器 + */ + CUSTOM +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorWriter.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorWriter.java new file mode 100644 index 000000000..8cc84a196 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ConnectorWriter.java @@ -0,0 +1,120 @@ +package com.pipeline.framework.api.connector; + +import java.util.List; + +/** + * Connector数据写入器。 + *

+ * 提供批量数据写入能力,不依赖Reactor。 + * 支持泛型配置,提供更强的类型安全。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface ConnectorWriter extends Connector { + + /** + * 打开写入器。 + * + * @throws Exception 打开失败 + */ + void open() throws Exception; + + /** + * 写入单条数据。 + * + * @param record 数据记录 + * @throws Exception 写入失败 + */ + void write(T record) throws Exception; + + /** + * 批量写入数据。 + * + * @param records 数据列表 + * @throws Exception 写入失败 + */ + void writeBatch(List records) throws Exception; + + /** + * 刷新缓冲区,确保数据写入。 + * + * @throws Exception 刷新失败 + */ + void flush() throws Exception; + + /** + * 关闭写入器,释放资源。 + * + * @throws Exception 关闭失败 + */ + void close() throws Exception; + + /** + * 保存检查点(用于容错恢复)。 + * + * @return 检查点信息 + * @throws Exception 保存失败 + */ + default Object saveCheckpoint() throws Exception { + return null; + } + + /** + * 从检查点恢复。 + * + * @param checkpoint 检查点信息 + * @throws Exception 恢复失败 + */ + default void restoreCheckpoint(Object checkpoint) throws Exception { + // 默认不支持 + } + + /** + * 是否支持事务。 + * + * @return true表示支持 + */ + default boolean supportsTransaction() { + return false; + } + + /** + * 开始事务。 + * + * @throws Exception 开始失败 + */ + default void beginTransaction() throws Exception { + // 默认不支持 + } + + /** + * 提交事务。 + * + * @throws Exception 提交失败 + */ + default void commit() throws Exception { + // 默认不支持 + } + + /** + * 回滚事务。 + * + * @throws Exception 回滚失败 + */ + default void rollback() throws Exception { + // 默认不支持 + } + + /** + * 获取已写入的记录数。 + * + * @return 记录数 + */ + default long getWriteCount() { + return 0; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ReadableConnector.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ReadableConnector.java new file mode 100644 index 000000000..50b8390fc --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/ReadableConnector.java @@ -0,0 +1,35 @@ +package com.pipeline.framework.api.connector; + +/** + * 可读连接器接口。 + *

+ * 标记接口,表示该连接器支持读取操作。 + * 结合ConnectorReader使用。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface ReadableConnector extends ConnectorReader { + + /** + * 创建读取器的副本(用于并行读取)。 + * + * @return 新的读取器实例 + * @throws ConnectorException 创建失败 + */ + default ConnectorReader duplicate() throws ConnectorException { + throw new ConnectorException("Duplication not supported for " + getName()); + } + + /** + * 是否支持并行读取。 + * + * @return true表示支持 + */ + default boolean supportsParallelRead() { + return false; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/WritableConnector.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/WritableConnector.java new file mode 100644 index 000000000..41281279c --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/WritableConnector.java @@ -0,0 +1,44 @@ +package com.pipeline.framework.api.connector; + +/** + * 可写连接器接口。 + *

+ * 标记接口,表示该连接器支持写入操作。 + * 结合ConnectorWriter使用。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface WritableConnector extends ConnectorWriter { + + /** + * 创建写入器的副本(用于并行写入)。 + * + * @return 新的写入器实例 + * @throws ConnectorException 创建失败 + */ + default ConnectorWriter duplicate() throws ConnectorException { + throw new ConnectorException("Duplication not supported for " + getName()); + } + + /** + * 是否支持并行写入。 + * + * @return true表示支持 + */ + default boolean supportsParallelWrite() { + return false; + } + + /** + * 是否支持幂等写入。 + * + * @return true表示支持 + */ + default boolean supportsIdempotentWrite() { + return false; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/ConnectorAdapter.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/ConnectorAdapter.java new file mode 100644 index 000000000..5273d9aea --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/ConnectorAdapter.java @@ -0,0 +1,44 @@ +package com.pipeline.framework.api.connector.adapter; + +import com.pipeline.framework.api.component.Component; +import com.pipeline.framework.api.connector.Connector; +import com.pipeline.framework.api.connector.ConnectorConfig; + +/** + * Connector到Component的适配器接口。 + *

+ * 使用适配器模式,将不依赖Reactor的Connector + * 转换为依赖Reactor的Component。 + *

+ * + * @param Connector类型 + * @param Component类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface ConnectorAdapter, COMP extends Component, C extends ConnectorConfig> { + + /** + * 适配Connector为Component。 + * + * @param connector Connector实例 + * @return Component实例 + */ + COMP adapt(CONN connector); + + /** + * 获取源Connector。 + * + * @return Connector实例 + */ + CONN getConnector(); + + /** + * 是否支持适配。 + * + * @param connector Connector实例 + * @return true表示支持 + */ + boolean supports(CONN connector); +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/ReaderToSourceAdapter.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/ReaderToSourceAdapter.java new file mode 100644 index 000000000..6e45e27b6 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/ReaderToSourceAdapter.java @@ -0,0 +1,43 @@ +package com.pipeline.framework.api.connector.adapter; + +import com.pipeline.framework.api.connector.ConnectorConfig; +import com.pipeline.framework.api.connector.ConnectorReader; +import com.pipeline.framework.api.source.DataSource; + +/** + * ConnectorReader到DataSource的适配器接口。 + *

+ * 将ConnectorReader(不依赖Reactor)适配为DataSource(依赖Reactor)。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface ReaderToSourceAdapter + extends ConnectorAdapter, DataSource, C> { + + /** + * 获取批次大小。 + * + * @return 批次大小 + */ + int getBatchSize(); + + /** + * 设置批次大小。 + * + * @param batchSize 批次大小 + */ + void setBatchSize(int batchSize); + + /** + * 是否启用背压。 + * + * @return true表示启用 + */ + default boolean isBackpressureEnabled() { + return true; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/WriterToSinkAdapter.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/WriterToSinkAdapter.java new file mode 100644 index 000000000..ea9edb891 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/adapter/WriterToSinkAdapter.java @@ -0,0 +1,52 @@ +package com.pipeline.framework.api.connector.adapter; + +import com.pipeline.framework.api.connector.ConnectorConfig; +import com.pipeline.framework.api.connector.ConnectorWriter; +import com.pipeline.framework.api.sink.DataSink; + +/** + * ConnectorWriter到DataSink的适配器接口。 + *

+ * 将ConnectorWriter(不依赖Reactor)适配为DataSink(依赖Reactor)。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface WriterToSinkAdapter + extends ConnectorAdapter, DataSink, C> { + + /** + * 获取批次大小。 + * + * @return 批次大小 + */ + int getBatchSize(); + + /** + * 设置批次大小。 + * + * @param batchSize 批次大小 + */ + void setBatchSize(int batchSize); + + /** + * 是否启用自动刷新。 + * + * @return true表示启用 + */ + default boolean isAutoFlushEnabled() { + return true; + } + + /** + * 获取刷新间隔(毫秒)。 + * + * @return 刷新间隔 + */ + default long getFlushInterval() { + return 1000L; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/factory/ConnectorFactory.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/factory/ConnectorFactory.java new file mode 100644 index 000000000..9c27d755d --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/factory/ConnectorFactory.java @@ -0,0 +1,53 @@ +package com.pipeline.framework.api.connector.factory; + +import com.pipeline.framework.api.connector.*; + +/** + * Connector工厂接口。 + *

+ * 使用工厂模式创建各种类型的Connector。 + * 支持泛型,提供类型安全的创建方法。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public interface ConnectorFactory { + + /** + * 创建Reader。 + * + * @param config 配置 + * @return Reader实例 + * @throws ConnectorException 创建失败 + */ + ConnectorReader createReader(C config) throws ConnectorException; + + /** + * 创建Writer。 + * + * @param config 配置 + * @return Writer实例 + * @throws ConnectorException 创建失败 + */ + ConnectorWriter createWriter(C config) throws ConnectorException; + + /** + * 获取支持的Connector类型。 + * + * @return Connector类型 + */ + ConnectorType getSupportedType(); + + /** + * 验证配置是否支持。 + * + * @param config 配置 + * @return true表示支持 + */ + default boolean supports(C config) { + return config != null; + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/factory/ConnectorFactoryRegistry.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/factory/ConnectorFactoryRegistry.java new file mode 100644 index 000000000..2557166e6 --- /dev/null +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/connector/factory/ConnectorFactoryRegistry.java @@ -0,0 +1,106 @@ +package com.pipeline.framework.api.connector.factory; + +import com.pipeline.framework.api.connector.*; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Connector工厂注册中心。 + *

+ * 管理所有Connector工厂,支持动态注册和查找。 + * 使用单例模式 + 注册表模式。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class ConnectorFactoryRegistry { + + private static final ConnectorFactoryRegistry INSTANCE = new ConnectorFactoryRegistry(); + + private final Map> factories = new ConcurrentHashMap<>(); + + private ConnectorFactoryRegistry() { + } + + public static ConnectorFactoryRegistry getInstance() { + return INSTANCE; + } + + /** + * 注册工厂。 + * + * @param type Connector类型 + * @param factory 工厂实例 + * @param 数据类型 + * @param 配置类型 + */ + public void register(ConnectorType type, ConnectorFactory factory) { + factories.put(type, factory); + } + + /** + * 获取工厂。 + * + * @param type Connector类型 + * @param 数据类型 + * @param 配置类型 + * @return 工厂实例(Optional) + */ + @SuppressWarnings("unchecked") + public Optional> getFactory(ConnectorType type) { + return Optional.ofNullable((ConnectorFactory) factories.get(type)); + } + + /** + * 创建Reader。 + * + * @param type Connector类型 + * @param config 配置 + * @param 数据类型 + * @param 配置类型 + * @return Reader实例 + * @throws ConnectorException 创建失败 + */ + public ConnectorReader createReader(ConnectorType type, C config) + throws ConnectorException { + ConnectorFactory factory = this.getFactory(type) + .orElseThrow(() -> new ConnectorException("No factory found for type: " + type)); + return factory.createReader(config); + } + + /** + * 创建Writer。 + * + * @param type Connector类型 + * @param config 配置 + * @param 数据类型 + * @param 配置类型 + * @return Writer实例 + * @throws ConnectorException 创建失败 + */ + public ConnectorWriter createWriter(ConnectorType type, C config) + throws ConnectorException { + ConnectorFactory factory = this.getFactory(type) + .orElseThrow(() -> new ConnectorException("No factory found for type: " + type)); + return factory.createWriter(config); + } + + /** + * 注销工厂。 + * + * @param type Connector类型 + */ + public void unregister(ConnectorType type) { + factories.remove(type); + } + + /** + * 清空所有工厂。 + */ + public void clear() { + factories.clear(); + } +} diff --git a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/job/JobType.java b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/job/JobType.java index 15bb541fe..b676bd66a 100644 --- a/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/job/JobType.java +++ b/pipeline-framework/pipeline-api/src/main/java/com/pipeline/framework/api/job/JobType.java @@ -9,11 +9,19 @@ public enum JobType { /** * 流式任务(持续运行) + * 用于实时数据流处理,如Kafka消费、实时ETL等 */ STREAMING, /** * 批处理任务(一次性) + * 用于一次性数据处理任务,如文件导入、数据迁移等 */ - BATCH + BATCH, + + /** + * SQL批量任务(多表整合) + * 用于大SQL多表关联、复杂聚合等批量数据处理 + */ + SQL_BATCH } diff --git a/pipeline-framework/pipeline-connectors/pom.xml b/pipeline-framework/pipeline-connectors/pom.xml index fbaaecfab..e13707053 100644 --- a/pipeline-framework/pipeline-connectors/pom.xml +++ b/pipeline-framework/pipeline-connectors/pom.xml @@ -23,29 +23,43 @@ pipeline-api + + + com.mysql + mysql-connector-j + true + + + io.projectreactor reactor-core + true - io.projectreactor.kafka reactor-kafka + true + io.lettuce lettuce-core + true + - com.mysql - mysql-connector-j + io.asyncer + r2dbc-mysql + true + - io.asyncer - r2dbc-mysql + org.slf4j + slf4j-api diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/Connector.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/Connector.java deleted file mode 100644 index db52e04ae..000000000 --- a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/Connector.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.pipeline.framework.connectors; - -import com.pipeline.framework.api.sink.DataSink; -import com.pipeline.framework.api.sink.SinkConfig; -import com.pipeline.framework.api.source.DataSource; -import com.pipeline.framework.api.source.SourceConfig; -import reactor.core.publisher.Mono; - -/** - * 连接器接口。 - *

- * 连接器提供Source和Sink的创建能力。 - * 所有操作都是响应式的。 - *

- * - * @author Pipeline Framework Team - * @since 1.0.0 - */ -public interface Connector { - - /** - * 获取连接器类型。 - * - * @return 连接器类型(如:jdbc, kafka, http) - */ - String getType(); - - /** - * 获取连接器名称。 - * - * @return 连接器名称 - */ - String getName(); - - /** - * 获取连接器版本。 - * - * @return 版本号 - */ - String getVersion(); - - /** - * 是否支持Source。 - * - * @return true如果支持 - */ - boolean supportsSource(); - - /** - * 是否支持Sink。 - * - * @return true如果支持 - */ - boolean supportsSink(); - - /** - * 创建Source。 - *

- * 异步创建并初始化Source。 - *

- * - * @param config Source配置 - * @param 数据类型 - * @return DataSource实例的Mono - */ - Mono> createSource(SourceConfig config); - - /** - * 创建Sink。 - *

- * 异步创建并初始化Sink。 - *

- * - * @param config Sink配置 - * @param 数据类型 - * @return DataSink实例的Mono - */ - Mono> createSink(SinkConfig config); - - /** - * 验证配置。 - *

- * 异步验证连接器配置的有效性。 - *

- * - * @param config 配置对象 - * @return 验证结果,true表示有效 - */ - Mono validateConfig(Object config); - - /** - * 健康检查。 - *

- * 检查连接器及其依赖的外部系统是否正常。 - *

- * - * @return 健康状态,true表示健康 - */ - Mono healthCheck(); -} diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/ConnectorRegistry.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/ConnectorRegistry.java deleted file mode 100644 index f391b6b65..000000000 --- a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/ConnectorRegistry.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.pipeline.framework.connectors; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * 连接器注册中心接口。 - *

- * 管理所有已注册的连接器。 - * 使用响应式API。 - *

- * - * @author Pipeline Framework Team - * @since 1.0.0 - */ -public interface ConnectorRegistry { - - /** - * 注册连接器。 - *

- * 异步注册连接器到注册中心。 - *

- * - * @param connector 连接器实例 - * @return 注册完成信号 - */ - Mono register(Connector connector); - - /** - * 根据类型获取连接器。 - *

- * 异步查找并返回连接器。 - *

- * - * @param type 连接器类型 - * @return 连接器实例的Mono - */ - Mono getConnector(String type); - - /** - * 获取所有已注册的连接器。 - *

- * 返回所有连接器的响应式流。 - *

- * - * @return 连接器流 - */ - Flux getAllConnectors(); - - /** - * 判断连接器是否已注册。 - * - * @param type 连接器类型 - * @return true如果已注册 - */ - Mono isRegistered(String type); - - /** - * 注销连接器。 - * - * @param type 连接器类型 - * @return 注销完成信号 - */ - Mono unregister(String type); - - /** - * 重新加载连接器。 - *

- * 重新加载指定类型的连接器。 - *

- * - * @param type 连接器类型 - * @return 重新加载完成信号 - */ - Mono reload(String type); -} diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/AbstractJdbcConnector.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/AbstractJdbcConnector.java new file mode 100644 index 000000000..ad17b090b --- /dev/null +++ b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/AbstractJdbcConnector.java @@ -0,0 +1,182 @@ +package com.pipeline.framework.connectors.jdbc; + +import com.pipeline.framework.api.connector.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +/** + * JDBC Connector抽象基类。 + *

+ * 使用模板方法模式,定义JDBC连接的通用逻辑。 + * 子类实现具体的读写操作。 + *

+ * + * @param 数据类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public abstract class AbstractJdbcConnector implements Connector { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final JdbcConnectorConfig config; + protected Connection connection; + protected volatile boolean opened = false; + + protected AbstractJdbcConnector(JdbcConnectorConfig config) { + this.config = config; + this.config.validate(); + } + + /** + * 打开连接(模板方法)。 + * + * @throws Exception 打开失败 + */ + public void open() throws Exception { + if (opened) { + logger.warn("Connector already opened: {}", getName()); + return; + } + + logger.info("Opening JDBC connector: {}", getName()); + + // 加载驱动 + loadDriver(); + + // 建立连接 + establishConnection(); + + // 配置连接 + configureConnection(); + + // 初始化(钩子方法) + doOpen(); + + opened = true; + logger.info("JDBC connector opened successfully: {}", getName()); + } + + /** + * 加载JDBC驱动。 + * + * @throws ClassNotFoundException 驱动类找不到 + */ + protected void loadDriver() throws ClassNotFoundException { + Class.forName(config.getDriverClassName()); + } + + /** + * 建立数据库连接。 + * + * @throws SQLException 连接失败 + */ + protected void establishConnection() throws SQLException { + connection = DriverManager.getConnection( + config.getUrl(), + config.getUsername(), + config.getPassword() + ); + } + + /** + * 配置连接参数。 + * + * @throws SQLException 配置失败 + */ + protected void configureConnection() throws SQLException { + connection.setAutoCommit(config.isAutoCommit()); + } + + /** + * 子类初始化逻辑(钩子方法)。 + * + * @throws Exception 初始化失败 + */ + protected void doOpen() throws Exception { + // 默认空实现,子类可覆盖 + } + + /** + * 关闭连接(模板方法)。 + * + * @throws Exception 关闭失败 + */ + public void close() throws Exception { + if (!opened) { + return; + } + + logger.info("Closing JDBC connector: {}", getName()); + + // 清理(钩子方法) + doClose(); + + // 关闭连接 + if (connection != null && !connection.isClosed()) { + connection.close(); + } + + opened = false; + logger.info("JDBC connector closed: {}", getName()); + } + + /** + * 子类清理逻辑(钩子方法)。 + * + * @throws Exception 清理失败 + */ + protected void doClose() throws Exception { + // 默认空实现,子类可覆盖 + } + + @Override + public ConnectorType getType() { + return ConnectorType.JDBC; + } + + @Override + public JdbcConnectorConfig getConfig() { + return config; + } + + @Override + public boolean validate() throws ConnectorException { + try { + config.validate(); + return true; + } catch (IllegalArgumentException e) { + throw new ConnectorException("Validation failed", e, getType(), getName()); + } + } + + /** + * 检查连接是否有效。 + * + * @return true表示有效 + */ + protected boolean isConnectionValid() { + try { + return connection != null && !connection.isClosed() && connection.isValid(5); + } catch (SQLException e) { + logger.error("Connection validation failed", e); + return false; + } + } + + /** + * 重连(如果连接失效)。 + * + * @throws Exception 重连失败 + */ + protected void reconnectIfNecessary() throws Exception { + if (!isConnectionValid()) { + logger.warn("Connection invalid, reconnecting..."); + close(); + open(); + } + } +} diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorConfig.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorConfig.java new file mode 100644 index 000000000..b6ee200bc --- /dev/null +++ b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorConfig.java @@ -0,0 +1,171 @@ +package com.pipeline.framework.connectors.jdbc; + +import com.pipeline.framework.api.connector.ConnectorConfig; + +/** + * JDBC Connector配置。 + * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class JdbcConnectorConfig extends ConnectorConfig { + + private static final long serialVersionUID = 1L; + + /** + * JDBC URL + */ + private String url; + + /** + * 用户名 + */ + private String username; + + /** + * 密码 + */ + private String password; + + /** + * 驱动类名 + */ + private String driverClassName = "com.mysql.cj.jdbc.Driver"; + + /** + * 查询SQL(用于Reader) + */ + private String querySql; + + /** + * 表名(用于Writer) + */ + private String tableName; + + /** + * 批次大小 + */ + private int batchSize = 1000; + + /** + * 连接池最大连接数 + */ + private int maxPoolSize = 10; + + /** + * 是否自动提交 + */ + private boolean autoCommit = false; + + /** + * 查询超时时间(秒) + */ + private int queryTimeout = 60; + + /** + * Fetch Size + */ + private int fetchSize = 1000; + + @Override + public void validate() throws IllegalArgumentException { + if (url == null || url.trim().isEmpty()) { + throw new IllegalArgumentException("JDBC URL cannot be null or empty"); + } + if (username == null) { + throw new IllegalArgumentException("Username cannot be null"); + } + if (password == null) { + throw new IllegalArgumentException("Password cannot be null"); + } + } + + // Getters and Setters + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getDriverClassName() { + return driverClassName; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getQuerySql() { + return querySql; + } + + public void setQuerySql(String querySql) { + this.querySql = querySql; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public int getBatchSize() { + return batchSize; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + public int getMaxPoolSize() { + return maxPoolSize; + } + + public void setMaxPoolSize(int maxPoolSize) { + this.maxPoolSize = maxPoolSize; + } + + public boolean isAutoCommit() { + return autoCommit; + } + + public void setAutoCommit(boolean autoCommit) { + this.autoCommit = autoCommit; + } + + public int getQueryTimeout() { + return queryTimeout; + } + + public void setQueryTimeout(int queryTimeout) { + this.queryTimeout = queryTimeout; + } + + public int getFetchSize() { + return fetchSize; + } + + public void setFetchSize(int fetchSize) { + this.fetchSize = fetchSize; + } +} diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorFactory.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorFactory.java new file mode 100644 index 000000000..3b178c17e --- /dev/null +++ b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorFactory.java @@ -0,0 +1,73 @@ +package com.pipeline.framework.connectors.jdbc; + +import com.pipeline.framework.api.connector.*; +import com.pipeline.framework.api.connector.factory.ConnectorFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * JDBC Connector工厂。 + *

+ * 使用工厂模式创建JDBC Reader和Writer。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class JdbcConnectorFactory implements ConnectorFactory, JdbcConnectorConfig> { + + private static final Logger logger = LoggerFactory.getLogger(JdbcConnectorFactory.class); + + @Override + public ConnectorReader, JdbcConnectorConfig> createReader(JdbcConnectorConfig config) + throws ConnectorException { + try { + logger.info("Creating JDBC reader with config: {}", config.getName()); + + if (config.getQuerySql() == null || config.getQuerySql().trim().isEmpty()) { + throw new ConnectorException("Query SQL is required for JDBC reader", ConnectorType.JDBC, config.getName()); + } + + JdbcConnectorReader reader = new JdbcConnectorReader(config); + + logger.info("JDBC reader created successfully: {}", config.getName()); + return reader; + } catch (Exception e) { + throw new ConnectorException("Failed to create JDBC reader", e, ConnectorType.JDBC, config.getName()); + } + } + + @Override + public ConnectorWriter, JdbcConnectorConfig> createWriter(JdbcConnectorConfig config) + throws ConnectorException { + try { + logger.info("Creating JDBC writer with config: {}", config.getName()); + + if (config.getTableName() == null || config.getTableName().trim().isEmpty()) { + throw new ConnectorException("Table name is required for JDBC writer", ConnectorType.JDBC, config.getName()); + } + + JdbcConnectorWriter writer = new JdbcConnectorWriter(config); + + logger.info("JDBC writer created successfully: {}", config.getName()); + return writer; + } catch (Exception e) { + throw new ConnectorException("Failed to create JDBC writer", e, ConnectorType.JDBC, config.getName()); + } + } + + @Override + public ConnectorType getSupportedType() { + return ConnectorType.JDBC; + } + + @Override + public boolean supports(JdbcConnectorConfig config) { + return config != null + && config.getUrl() != null + && config.getUsername() != null + && config.getPassword() != null; + } +} diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorReader.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorReader.java new file mode 100644 index 000000000..abfc25164 --- /dev/null +++ b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorReader.java @@ -0,0 +1,179 @@ +package com.pipeline.framework.connectors.jdbc; + +import com.pipeline.framework.api.connector.*; + +import java.sql.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * JDBC Connector Reader实现。 + *

+ * 实现ReadableConnector接口,提供JDBC数据库读取能力。 + * 不依赖Reactor,可以独立使用。 + * 支持断点续传、进度跟踪、并行读取。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class JdbcConnectorReader extends AbstractJdbcConnector> + implements ReadableConnector, JdbcConnectorConfig> { + + private PreparedStatement statement; + private ResultSet resultSet; + private ResultSetMetaData metaData; + private long readCount = 0; + private long totalRows = -1; + private Object checkpoint; + + public JdbcConnectorReader(JdbcConnectorConfig config) { + super(config); + } + + @Override + protected void doOpen() throws Exception { + // 创建查询语句 + statement = connection.prepareStatement( + config.getQuerySql(), + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY + ); + statement.setFetchSize(config.getFetchSize()); + statement.setQueryTimeout(config.getQueryTimeout()); + + // 执行查询 + resultSet = statement.executeQuery(); + metaData = resultSet.getMetaData(); + + // 计算总行数(用于进度跟踪) + calculateTotalRows(); + } + + @Override + protected void doClose() throws Exception { + if (resultSet != null && !resultSet.isClosed()) { + resultSet.close(); + } + if (statement != null && !statement.isClosed()) { + statement.close(); + } + } + + @Override + public List> readBatch(int batchSize) throws Exception { + reconnectIfNecessary(); + + List> batch = new ArrayList<>(batchSize); + + int count = 0; + while (count < batchSize && resultSet.next()) { + Map row = new HashMap<>(); + int columnCount = metaData.getColumnCount(); + + for (int i = 1; i <= columnCount; i++) { + String columnName = metaData.getColumnLabel(i); + Object value = resultSet.getObject(i); + row.put(columnName, value); + } + + batch.add(row); + count++; + readCount++; + } + + // 更新检查点 + checkpoint = readCount; + + return batch; + } + + @Override + public boolean hasNext() { + try { + return resultSet != null && !resultSet.isAfterLast(); + } catch (SQLException e) { + logger.error("Error checking hasNext", e); + return false; + } + } + + @Override + public double getProgress() { + if (totalRows <= 0) { + return -1.0; + } + return (double) readCount / totalRows; + } + + @Override + public long getReadCount() { + return readCount; + } + + @Override + public Object getCheckpoint() { + return checkpoint; + } + + @Override + public void seekToCheckpoint(Object checkpoint) throws Exception { + this.checkpoint = checkpoint; + if (checkpoint instanceof Long) { + // 跳过已读取的行 + long skipRows = (Long) checkpoint; + for (long i = 0; i < skipRows && resultSet.next(); i++) { + // 跳过 + } + readCount = skipRows; + logger.info("Seeked to checkpoint: {}", skipRows); + } + } + + @Override + public boolean supportsCheckpoint() { + return true; + } + + @Override + public boolean supportsParallelRead() { + return false; // JDBC ResultSet不支持并行读取 + } + + @Override + public ConnectorReader, JdbcConnectorConfig> duplicate() throws ConnectorException { + try { + JdbcConnectorReader newReader = new JdbcConnectorReader(config); + newReader.open(); + return newReader; + } catch (Exception e) { + throw new ConnectorException("Failed to duplicate reader", e, getType(), getName()); + } + } + + @Override + public String getName() { + return config.getName() != null ? config.getName() : "jdbc-reader"; + } + + /** + * 计算总行数(用于进度跟踪)。 + */ + private void calculateTotalRows() { + try (Statement countStmt = connection.createStatement()) { + // 从SQL中提取表名(简单实现) + String countSql = "SELECT COUNT(*) FROM (" + config.getQuerySql() + ") AS temp"; + try (ResultSet rs = countStmt.executeQuery(countSql)) { + if (rs.next()) { + totalRows = rs.getLong(1); + logger.info("Total rows: {}", totalRows); + } + } + } catch (Exception e) { + logger.warn("Failed to calculate total rows: {}", e.getMessage()); + totalRows = -1; + } + } +} diff --git a/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorWriter.java b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorWriter.java new file mode 100644 index 000000000..48d6e35eb --- /dev/null +++ b/pipeline-framework/pipeline-connectors/src/main/java/com/pipeline/framework/connectors/jdbc/JdbcConnectorWriter.java @@ -0,0 +1,190 @@ +package com.pipeline.framework.connectors.jdbc; + +import com.pipeline.framework.api.connector.*; + +import java.sql.*; +import java.util.List; +import java.util.Map; + +/** + * JDBC Connector Writer实现。 + *

+ * 实现WritableConnector接口,提供JDBC数据库写入能力。 + * 不依赖Reactor,可以独立使用。 + * 支持批量写入、事务、检查点、幂等写入。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class JdbcConnectorWriter extends AbstractJdbcConnector> + implements WritableConnector, JdbcConnectorConfig> { + + private PreparedStatement statement; + private long writeCount = 0; + private Object checkpoint; + private boolean inTransaction = false; + + public JdbcConnectorWriter(JdbcConnectorConfig config) { + super(config); + } + + @Override + protected void doOpen() throws Exception { + // 动态生成INSERT语句(简化示例,实际应该从配置获取) + String insertSql = generateInsertSql(); + statement = connection.prepareStatement(insertSql); + + logger.info("JDBC writer prepared with SQL: {}", insertSql); + } + + @Override + protected void doClose() throws Exception { + if (statement != null && !statement.isClosed()) { + statement.close(); + } + } + + @Override + public void write(Map record) throws Exception { + reconnectIfNecessary(); + + setStatementParameters(statement, record); + statement.executeUpdate(); + writeCount++; + } + + @Override + public void writeBatch(List> records) throws Exception { + reconnectIfNecessary(); + + for (Map record : records) { + setStatementParameters(statement, record); + statement.addBatch(); + } + + int[] results = statement.executeBatch(); + writeCount += results.length; + + logger.debug("Batch write completed: {} records", results.length); + } + + @Override + public void flush() throws Exception { + if (connection != null && !connection.getAutoCommit()) { + connection.commit(); + logger.debug("Flushed and committed transaction"); + } + } + + @Override + public Object saveCheckpoint() throws Exception { + checkpoint = writeCount; + return checkpoint; + } + + @Override + public void restoreCheckpoint(Object checkpoint) throws Exception { + if (checkpoint instanceof Long) { + this.checkpoint = checkpoint; + this.writeCount = (Long) checkpoint; + logger.info("Restored checkpoint: {}", checkpoint); + } + } + + @Override + public boolean supportsTransaction() { + return true; + } + + @Override + public void beginTransaction() throws Exception { + if (!inTransaction) { + connection.setAutoCommit(false); + inTransaction = true; + logger.debug("Transaction began"); + } + } + + @Override + public void commit() throws Exception { + if (inTransaction) { + connection.commit(); + inTransaction = false; + logger.debug("Transaction committed"); + } + } + + @Override + public void rollback() throws Exception { + if (inTransaction) { + connection.rollback(); + inTransaction = false; + logger.debug("Transaction rolled back"); + } + } + + @Override + public long getWriteCount() { + return writeCount; + } + + @Override + public boolean supportsParallelWrite() { + return true; // JDBC支持多个连接并行写入 + } + + @Override + public boolean supportsIdempotentWrite() { + return false; // 需要业务层保证 + } + + @Override + public ConnectorWriter, JdbcConnectorConfig> duplicate() throws ConnectorException { + try { + JdbcConnectorWriter newWriter = new JdbcConnectorWriter(config); + newWriter.open(); + return newWriter; + } catch (Exception e) { + throw new ConnectorException("Failed to duplicate writer", e, getType(), getName()); + } + } + + @Override + public String getName() { + return config.getName() != null ? config.getName() : "jdbc-writer"; + } + + /** + * 生成INSERT SQL语句(简化实现)。 + * + * @return INSERT SQL + */ + private String generateInsertSql() { + // 简化示例:INSERT INTO table (col1, col2, ...) VALUES (?, ?, ...) + // 实际应该从配置或元数据获取列信息 + String tableName = config.getTableName(); + if (tableName == null || tableName.isEmpty()) { + throw new IllegalArgumentException("Table name is required for writer"); + } + + // 这里简化处理,实际应该查询表结构 + return String.format("INSERT INTO %s VALUES (?)", tableName); + } + + /** + * 设置PreparedStatement参数。 + * + * @param stmt PreparedStatement + * @param record 数据记录 + * @throws SQLException SQL异常 + */ + private void setStatementParameters(PreparedStatement stmt, Map record) throws SQLException { + // 简化实现:按照Map的顺序设置参数 + // 实际应该按照表结构的列顺序设置 + int index = 1; + for (Object value : record.values()) { + stmt.setObject(index++, value); + } + } +} diff --git a/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/AbstractConnectorAdapter.java b/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/AbstractConnectorAdapter.java new file mode 100644 index 000000000..9256187be --- /dev/null +++ b/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/AbstractConnectorAdapter.java @@ -0,0 +1,101 @@ +package com.pipeline.framework.core.connector; + +import com.pipeline.framework.api.component.Component; +import com.pipeline.framework.api.connector.Connector; +import com.pipeline.framework.api.connector.ConnectorConfig; +import com.pipeline.framework.api.connector.adapter.ConnectorAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Connector适配器抽象基类。 + *

+ * 使用模板方法模式,定义适配流程的骨架。 + * 子类实现具体的适配逻辑。 + *

+ * + * @param Connector类型 + * @param Component类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public abstract class AbstractConnectorAdapter, COMP extends Component, C extends ConnectorConfig> + implements ConnectorAdapter { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final CONN connector; + + protected AbstractConnectorAdapter(CONN connector) { + this.connector = connector; + validate(); + } + + @Override + public COMP adapt(CONN connector) { + logger.debug("Adapting connector: {}", connector.getName()); + + // 模板方法:前置处理 + preAdapt(connector); + + // 模板方法:执行适配 + COMP component = doAdapt(connector); + + // 模板方法:后置处理 + postAdapt(connector, component); + + logger.debug("Adapter completed for connector: {}", connector.getName()); + return component; + } + + /** + * 前置处理(钩子方法)。 + * + * @param connector Connector实例 + */ + protected void preAdapt(CONN connector) { + // 默认空实现,子类可覆盖 + } + + /** + * 执行适配(抽象方法,子类必须实现)。 + * + * @param connector Connector实例 + * @return Component实例 + */ + protected abstract COMP doAdapt(CONN connector); + + /** + * 后置处理(钩子方法)。 + * + * @param connector Connector实例 + * @param component Component实例 + */ + protected void postAdapt(CONN connector, COMP component) { + // 默认空实现,子类可覆盖 + } + + @Override + public CONN getConnector() { + return connector; + } + + @Override + public boolean supports(CONN connector) { + return connector != null && connector.validate(); + } + + /** + * 验证Connector。 + * + * @throws IllegalArgumentException 验证失败 + */ + protected void validate() { + if (connector == null) { + throw new IllegalArgumentException("Connector cannot be null"); + } + if (!connector.validate()) { + throw new IllegalArgumentException("Connector validation failed: " + connector.getName()); + } + } +} diff --git a/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/DefaultReaderToSourceAdapter.java b/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/DefaultReaderToSourceAdapter.java new file mode 100644 index 000000000..666366a34 --- /dev/null +++ b/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/DefaultReaderToSourceAdapter.java @@ -0,0 +1,155 @@ +package com.pipeline.framework.core.connector; + +import com.pipeline.framework.api.component.ComponentType; +import com.pipeline.framework.api.connector.ConnectorConfig; +import com.pipeline.framework.api.connector.ConnectorReader; +import com.pipeline.framework.api.connector.adapter.ReaderToSourceAdapter; +import com.pipeline.framework.api.source.DataSource; +import com.pipeline.framework.api.source.SourceConfig; +import com.pipeline.framework.api.source.SourceType; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; + +/** + * ConnectorReader到DataSource的默认适配器实现。 + *

+ * 将ConnectorReader(批量读取)转换为DataSource(响应式流)。 + * 支持背压、检查点、进度跟踪。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class DefaultReaderToSourceAdapter + extends AbstractConnectorAdapter, DataSource, C> + implements ReaderToSourceAdapter { + + private int batchSize = 1000; + private boolean backpressureEnabled = true; + + public DefaultReaderToSourceAdapter(ConnectorReader reader) { + super(reader); + } + + public DefaultReaderToSourceAdapter(ConnectorReader reader, int batchSize) { + super(reader); + this.batchSize = batchSize; + } + + @Override + protected DataSource doAdapt(ConnectorReader reader) { + return new AdaptedDataSource(reader); + } + + @Override + public int getBatchSize() { + return batchSize; + } + + @Override + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + @Override + public boolean isBackpressureEnabled() { + return backpressureEnabled; + } + + public void setBackpressureEnabled(boolean backpressureEnabled) { + this.backpressureEnabled = backpressureEnabled; + } + + /** + * 适配后的DataSource内部类。 + */ + private class AdaptedDataSource implements DataSource { + + private final ConnectorReader reader; + private volatile boolean running = false; + + public AdaptedDataSource(ConnectorReader reader) { + this.reader = reader; + } + + @Override + public Flux read() { + return Flux.defer(() -> { + try { + reader.open(); + running = true; + } catch (Exception e) { + return Flux.error(e); + } + + return Flux.create(sink -> { + try { + while (reader.hasNext() && !sink.isCancelled()) { + var batch = reader.readBatch(batchSize); + if (batch != null && !batch.isEmpty()) { + for (T item : batch) { + sink.next(item); + } + } + } + sink.complete(); + } catch (Exception e) { + sink.error(e); + } finally { + try { + reader.close(); + running = false; + } catch (Exception e) { + logger.error("Error closing reader", e); + } + } + }) + .subscribeOn(Schedulers.boundedElastic()); + }); + } + + @Override + public SourceType getType() { + return SourceType.CUSTOM; + } + + @Override + public String getName() { + return reader.getName() + "-adapted-source"; + } + + @Override + public SourceConfig getConfig() { + SourceConfig config = new SourceConfig(); + config.setName(getName()); + config.setType(getType()); + return config; + } + + @Override + public Mono start() { + return Mono.fromRunnable(() -> logger.info("Starting adapted source: {}", getName())); + } + + @Override + public Mono stop() { + return Mono.fromRunnable(() -> { + running = false; + logger.info("Stopping adapted source: {}", getName()); + }); + } + + @Override + public boolean isRunning() { + return running; + } + + @Override + public ComponentType getComponentType() { + return ComponentType.SOURCE; + } + } +} diff --git a/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/DefaultWriterToSinkAdapter.java b/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/DefaultWriterToSinkAdapter.java new file mode 100644 index 000000000..b71cefca9 --- /dev/null +++ b/pipeline-framework/pipeline-core/src/main/java/com/pipeline/framework/core/connector/DefaultWriterToSinkAdapter.java @@ -0,0 +1,196 @@ +package com.pipeline.framework.core.connector; + +import com.pipeline.framework.api.component.ComponentType; +import com.pipeline.framework.api.connector.ConnectorConfig; +import com.pipeline.framework.api.connector.ConnectorWriter; +import com.pipeline.framework.api.connector.adapter.WriterToSinkAdapter; +import com.pipeline.framework.api.sink.DataSink; +import com.pipeline.framework.api.sink.SinkConfig; +import com.pipeline.framework.api.sink.SinkType; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +/** + * ConnectorWriter到DataSink的默认适配器实现。 + *

+ * 将ConnectorWriter(批量写入)转换为DataSink(响应式流)。 + * 支持批量写入、事务、自动刷新。 + *

+ * + * @param 数据类型 + * @param 配置类型 + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class DefaultWriterToSinkAdapter + extends AbstractConnectorAdapter, DataSink, C> + implements WriterToSinkAdapter { + + private int batchSize = 1000; + private boolean autoFlushEnabled = true; + private long flushInterval = 1000L; + + public DefaultWriterToSinkAdapter(ConnectorWriter writer) { + super(writer); + } + + public DefaultWriterToSinkAdapter(ConnectorWriter writer, int batchSize) { + super(writer); + this.batchSize = batchSize; + } + + @Override + protected DataSink doAdapt(ConnectorWriter writer) { + return new AdaptedDataSink(writer); + } + + @Override + public int getBatchSize() { + return batchSize; + } + + @Override + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + @Override + public boolean isAutoFlushEnabled() { + return autoFlushEnabled; + } + + public void setAutoFlushEnabled(boolean autoFlushEnabled) { + this.autoFlushEnabled = autoFlushEnabled; + } + + @Override + public long getFlushInterval() { + return flushInterval; + } + + public void setFlushInterval(long flushInterval) { + this.flushInterval = flushInterval; + } + + /** + * 适配后的DataSink内部类。 + */ + private class AdaptedDataSink implements DataSink { + + private final ConnectorWriter writer; + private volatile boolean running = false; + + public AdaptedDataSink(ConnectorWriter writer) { + this.writer = writer; + } + + @Override + public Mono write(Flux data) { + return Mono.defer(() -> { + try { + writer.open(); + running = true; + + if (writer.supportsTransaction()) { + writer.beginTransaction(); + } + } catch (Exception e) { + return Mono.error(e); + } + + return data + .buffer(batchSize) + .flatMap(batch -> Mono.fromRunnable(() -> { + try { + writer.writeBatch(batch); + } catch (Exception e) { + throw new RuntimeException("Failed to write batch", e); + } + })) + .then(Mono.fromRunnable(() -> { + try { + if (autoFlushEnabled) { + writer.flush(); + } + if (writer.supportsTransaction()) { + writer.commit(); + } + } catch (Exception e) { + if (writer.supportsTransaction()) { + try { + writer.rollback(); + } catch (Exception rollbackEx) { + logger.error("Rollback failed", rollbackEx); + } + } + throw new RuntimeException("Failed to flush/commit", e); + } + })) + .doFinally(signalType -> { + try { + writer.close(); + running = false; + } catch (Exception e) { + logger.error("Error closing writer", e); + } + }); + }); + } + + @Override + public Mono flush() { + return Mono.fromRunnable(() -> { + try { + writer.flush(); + } catch (Exception e) { + throw new RuntimeException("Flush failed", e); + } + }); + } + + @Override + public SinkType getType() { + return SinkType.CUSTOM; + } + + @Override + public String getName() { + return writer.getName() + "-adapted-sink"; + } + + @Override + public SinkConfig getConfig() { + SinkConfig config = new SinkConfig(); + config.setName(getName()); + config.setType(getType()); + return config; + } + + @Override + public Mono start() { + return Mono.fromRunnable(() -> logger.info("Starting adapted sink: {}", getName())); + } + + @Override + public Mono stop() { + return Mono.fromRunnable(() -> { + running = false; + logger.info("Stopping adapted sink: {}", getName()); + }); + } + + @Override + public boolean isRunning() { + return running; + } + + @Override + public ComponentType getComponentType() { + return ComponentType.SINK; + } + } +} diff --git a/pipeline-framework/pipeline-executor/src/main/java/com/pipeline/framework/executor/batch/BatchJobExecutor.java b/pipeline-framework/pipeline-executor/src/main/java/com/pipeline/framework/executor/batch/BatchJobExecutor.java new file mode 100644 index 000000000..87e2705d8 --- /dev/null +++ b/pipeline-framework/pipeline-executor/src/main/java/com/pipeline/framework/executor/batch/BatchJobExecutor.java @@ -0,0 +1,224 @@ +package com.pipeline.framework.executor.batch; + +import com.pipeline.framework.api.executor.ExecutionMetrics; +import com.pipeline.framework.api.executor.ExecutionStatus; +import com.pipeline.framework.api.executor.JobExecutor; +import com.pipeline.framework.api.executor.JobResult; +import com.pipeline.framework.api.graph.StreamGraph; +import com.pipeline.framework.api.job.Job; +import com.pipeline.framework.api.job.JobType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; + +import java.time.Duration; +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 批量任务执行器。 + *

+ * 用于执行批处理任务(BATCH)和SQL批量任务(SQL_BATCH)。 + * 与流式任务不同,批量任务执行完成后会自动结束。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class BatchJobExecutor implements JobExecutor { + + private static final Logger log = LoggerFactory.getLogger(BatchJobExecutor.class); + + private final Scheduler executorScheduler; + private final Map runningJobs; + + public BatchJobExecutor() { + this(Schedulers.boundedElastic()); + } + + public BatchJobExecutor(Scheduler executorScheduler) { + this.executorScheduler = executorScheduler; + this.runningJobs = new ConcurrentHashMap<>(); + } + + @Override + public Mono execute(Job job) { + if (job.getType() == JobType.STREAMING) { + return Mono.error(new IllegalArgumentException( + "BatchJobExecutor does not support STREAMING jobs. Use StreamingJobExecutor instead.")); + } + + String jobId = job.getJobId(); + log.info("Starting batch job execution: jobId={}, type={}", jobId, job.getType()); + + return Mono.defer(() -> { + Instant startTime = Instant.now(); + JobExecutionContext context = new JobExecutionContext(job, startTime); + runningJobs.put(jobId, context); + + return executeBatchJob(job) + .map(metrics -> { + Instant endTime = Instant.now(); + Duration duration = Duration.between(startTime, endTime); + + log.info("Batch job completed: jobId={}, duration={}ms, recordsProcessed={}", + jobId, duration.toMillis(), metrics.getRecordsProcessed()); + + return createJobResult(jobId, ExecutionStatus.COMPLETED, metrics, null); + }) + .onErrorResume(error -> { + log.error("Batch job failed: jobId={}", jobId, error); + + ExecutionMetrics errorMetrics = ExecutionMetrics.builder() + .recordsProcessed(context.getRecordsProcessed()) + .recordsFailed(context.getRecordsFailed()) + .build(); + + return Mono.just(createJobResult(jobId, ExecutionStatus.FAILED, errorMetrics, error)); + }) + .doFinally(signal -> { + runningJobs.remove(jobId); + log.debug("Batch job removed from running jobs: jobId={}, signal={}", jobId, signal); + }); + }).subscribeOn(executorScheduler); + } + + private Mono executeBatchJob(Job job) { + StreamGraph graph = job.getStreamGraph(); + + // TODO: 实际的批量任务执行逻辑 + // 这里应该遍历StreamGraph,执行Source -> Operators -> Sink + // 目前只是一个简单的示例实现 + + return Mono.fromCallable(() -> { + long recordsProcessed = 0; + long recordsFailed = 0; + + // 模拟批量处理 + log.debug("Executing batch job: {}", job.getJobId()); + + // 这里应该调用实际的图执行逻辑 + // 例如: graphExecutor.execute(graph).blockLast(); + + return ExecutionMetrics.builder() + .recordsProcessed(recordsProcessed) + .recordsFailed(recordsFailed) + .bytesProcessed(0L) + .build(); + }).subscribeOn(executorScheduler); + } + + @Override + public Mono cancel(String jobId) { + log.info("Cancelling batch job: jobId={}", jobId); + + JobExecutionContext context = runningJobs.get(jobId); + if (context != null) { + context.cancel(); + runningJobs.remove(jobId); + return Mono.empty(); + } + + return Mono.error(new IllegalArgumentException("Job not found: " + jobId)); + } + + @Override + public Mono getJobResult(String jobId) { + JobExecutionContext context = runningJobs.get(jobId); + + if (context == null) { + return Mono.error(new IllegalArgumentException("Job not found: " + jobId)); + } + + ExecutionMetrics metrics = ExecutionMetrics.builder() + .recordsProcessed(context.getRecordsProcessed()) + .recordsFailed(context.getRecordsFailed()) + .build(); + + return Mono.just(createJobResult(jobId, ExecutionStatus.RUNNING, metrics, null)); + } + + private JobResult createJobResult(String jobId, ExecutionStatus status, + ExecutionMetrics metrics, Throwable error) { + return new JobResult() { + @Override + public String getJobId() { + return jobId; + } + + @Override + public ExecutionStatus getStatus() { + return status; + } + + @Override + public ExecutionMetrics getMetrics() { + return metrics; + } + + @Override + public Throwable getError() { + return error; + } + + @Override + public Map getDetails() { + Map details = new HashMap<>(); + details.put("jobId", jobId); + details.put("status", status); + details.put("metrics", metrics); + if (error != null) { + details.put("errorMessage", error.getMessage()); + } + return details; + } + }; + } + + /** + * 任务执行上下文 + */ + private static class JobExecutionContext { + private final Job job; + private final Instant startTime; + private volatile long recordsProcessed; + private volatile long recordsFailed; + private volatile boolean cancelled; + + public JobExecutionContext(Job job, Instant startTime) { + this.job = job; + this.startTime = startTime; + this.recordsProcessed = 0; + this.recordsFailed = 0; + this.cancelled = false; + } + + public void cancel() { + this.cancelled = true; + } + + public boolean isCancelled() { + return cancelled; + } + + public long getRecordsProcessed() { + return recordsProcessed; + } + + public long getRecordsFailed() { + return recordsFailed; + } + + public void incrementRecordsProcessed() { + this.recordsProcessed++; + } + + public void incrementRecordsFailed() { + this.recordsFailed++; + } + } +} diff --git a/pipeline-framework/pipeline-starter/src/main/java/com/pipeline/framework/starter/example/ConnectorUsageExample.java b/pipeline-framework/pipeline-starter/src/main/java/com/pipeline/framework/starter/example/ConnectorUsageExample.java new file mode 100644 index 000000000..f6138e280 --- /dev/null +++ b/pipeline-framework/pipeline-starter/src/main/java/com/pipeline/framework/starter/example/ConnectorUsageExample.java @@ -0,0 +1,233 @@ +package com.pipeline.framework.starter.example; + +import com.pipeline.framework.api.connector.*; +import com.pipeline.framework.api.connector.factory.ConnectorFactoryRegistry; +import com.pipeline.framework.api.sink.DataSink; +import com.pipeline.framework.api.source.DataSource; +import com.pipeline.framework.connectors.jdbc.JdbcConnectorConfig; +import com.pipeline.framework.connectors.jdbc.JdbcConnectorFactory; +import com.pipeline.framework.core.connector.DefaultReaderToSourceAdapter; +import com.pipeline.framework.core.connector.DefaultWriterToSinkAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * Connector使用示例。 + *

+ * 展示如何使用新架构进行数据ETL处理。 + *

+ * + * @author Pipeline Framework Team + * @since 1.0.0 + */ +public class ConnectorUsageExample { + + private static final Logger logger = LoggerFactory.getLogger(ConnectorUsageExample.class); + + public static void main(String[] args) { + try { + // 示例1:基础用法 + basicUsage(); + + // 示例2:工厂模式用法 + factoryUsage(); + + // 示例3:适配器用法 + adapterUsage(); + + // 示例4:完整ETL流程 + fullEtlPipeline(); + + } catch (Exception e) { + logger.error("Example execution failed", e); + } + } + + /** + * 示例1:基础用法。 + */ + private static void basicUsage() throws Exception { + logger.info("=== 示例1:基础用法 ==="); + + // 创建配置 + JdbcConnectorConfig config = new JdbcConnectorConfig(); + config.setName("my-jdbc-reader"); + config.setUrl("jdbc:mysql://localhost:3306/test"); + config.setUsername("root"); + config.setPassword("password"); + config.setQuerySql("SELECT * FROM users LIMIT 10"); + config.setBatchSize(5); + + // 创建Reader + ConnectorReader, JdbcConnectorConfig> reader = + new com.pipeline.framework.connectors.jdbc.JdbcConnectorReader(config); + + // 使用Reader + reader.open(); + + while (reader.hasNext()) { + var batch = reader.readBatch(5); + logger.info("Read batch: {} records", batch.size()); + + for (Map record : batch) { + logger.info("Record: {}", record); + } + } + + reader.close(); + logger.info("Total read: {} records", reader.getReadCount()); + } + + /** + * 示例2:工厂模式用法。 + */ + private static void factoryUsage() throws Exception { + logger.info("=== 示例2:工厂模式用法 ==="); + + // 注册工厂 + ConnectorFactoryRegistry registry = ConnectorFactoryRegistry.getInstance(); + registry.register(ConnectorType.JDBC, new JdbcConnectorFactory()); + + // 创建Reader配置 + JdbcConnectorConfig sourceConfig = new JdbcConnectorConfig(); + sourceConfig.setName("source-reader"); + sourceConfig.setUrl("jdbc:mysql://localhost:3306/source_db"); + sourceConfig.setUsername("root"); + sourceConfig.setPassword("password"); + sourceConfig.setQuerySql("SELECT * FROM products WHERE price > 100"); + + // 使用工厂创建Reader + ConnectorReader, JdbcConnectorConfig> reader = + registry.createReader(ConnectorType.JDBC, sourceConfig); + + // 创建Writer配置 + JdbcConnectorConfig sinkConfig = new JdbcConnectorConfig(); + sinkConfig.setName("sink-writer"); + sinkConfig.setUrl("jdbc:mysql://localhost:3306/target_db"); + sinkConfig.setUsername("root"); + sinkConfig.setPassword("password"); + sinkConfig.setTableName("high_price_products"); + + // 使用工厂创建Writer + ConnectorWriter, JdbcConnectorConfig> writer = + registry.createWriter(ConnectorType.JDBC, sinkConfig); + + // 获取元数据 + logger.info("Reader metadata: {}", reader.getMetadata()); + logger.info("Writer metadata: {}", writer.getMetadata()); + + // 清理 + registry.clear(); + } + + /** + * 示例3:适配器用法。 + */ + private static void adapterUsage() throws Exception { + logger.info("=== 示例3:适配器用法 ==="); + + // 创建Connector + JdbcConnectorConfig config = new JdbcConnectorConfig(); + config.setName("adapted-reader"); + config.setUrl("jdbc:mysql://localhost:3306/test"); + config.setUsername("root"); + config.setPassword("password"); + config.setQuerySql("SELECT * FROM orders LIMIT 100"); + + ConnectorReader, JdbcConnectorConfig> reader = + new com.pipeline.framework.connectors.jdbc.JdbcConnectorReader(config); + + // 创建适配器 + DefaultReaderToSourceAdapter, JdbcConnectorConfig> adapter = + new DefaultReaderToSourceAdapter<>(reader, 20); + + // 获取DataSource + DataSource> source = adapter.adapt(reader); + + // 启动 + source.start().block(); + + // 使用响应式流 + source.read() + .take(50) + .doOnNext(data -> logger.info("Received: {}", data)) + .doOnComplete(() -> logger.info("Stream completed")) + .blockLast(); + + // 停止 + source.stop().block(); + } + + /** + * 示例4:完整ETL流程。 + */ + private static void fullEtlPipeline() throws Exception { + logger.info("=== 示例4:完整ETL流程 ==="); + + // 注册工厂 + ConnectorFactoryRegistry registry = ConnectorFactoryRegistry.getInstance(); + registry.register(ConnectorType.JDBC, new JdbcConnectorFactory()); + + // 源配置 + JdbcConnectorConfig sourceConfig = new JdbcConnectorConfig(); + sourceConfig.setName("etl-source"); + sourceConfig.setUrl("jdbc:mysql://localhost:3306/source"); + sourceConfig.setUsername("root"); + sourceConfig.setPassword("password"); + sourceConfig.setQuerySql("SELECT id, name, email, created_at FROM users WHERE status = 'active'"); + sourceConfig.setBatchSize(100); + + // 目标配置 + JdbcConnectorConfig sinkConfig = new JdbcConnectorConfig(); + sinkConfig.setName("etl-sink"); + sinkConfig.setUrl("jdbc:mysql://localhost:3306/target"); + sinkConfig.setUsername("root"); + sinkConfig.setPassword("password"); + sinkConfig.setTableName("migrated_users"); + + // 创建Connector + ConnectorReader, JdbcConnectorConfig> reader = + registry.createReader(ConnectorType.JDBC, sourceConfig); + ConnectorWriter, JdbcConnectorConfig> writer = + registry.createWriter(ConnectorType.JDBC, sinkConfig); + + // 创建适配器 + DefaultReaderToSourceAdapter, JdbcConnectorConfig> sourceAdapter = + new DefaultReaderToSourceAdapter<>(reader, 100); + DefaultWriterToSinkAdapter, JdbcConnectorConfig> sinkAdapter = + new DefaultWriterToSinkAdapter<>(writer, 100); + + // 获取Component + DataSource> source = sourceAdapter.adapt(reader); + DataSink> sink = sinkAdapter.adapt(writer); + + // 启动 + source.start().block(); + sink.start().block(); + + logger.info("Starting ETL pipeline..."); + + // 执行ETL + source.read() + .doOnNext(data -> logger.debug("Processing: {}", data.get("id"))) + .map(data -> { + // 数据转换逻辑 + data.put("migrated_at", System.currentTimeMillis()); + data.put("source", "legacy_system"); + return data; + }) + .filter(data -> data.get("email") != null) // 过滤 + .transform(dataStream -> sink.write(dataStream)) + .doOnError(error -> logger.error("ETL failed", error)) + .doOnSuccess(v -> logger.info("ETL completed successfully")) + .block(); + + // 停止 + sink.stop().block(); + source.stop().block(); + + logger.info("ETL pipeline finished"); + } +} diff --git a/pipeline-framework/pipeline-starter/src/main/resources/application.yml b/pipeline-framework/pipeline-starter/src/main/resources/application.yml index 53108b362..4174b29af 100644 --- a/pipeline-framework/pipeline-starter/src/main/resources/application.yml +++ b/pipeline-framework/pipeline-starter/src/main/resources/application.yml @@ -9,6 +9,57 @@ spring: baseline-on-migrate: true baseline-version: 0 +# Pipeline 框架配置 +pipeline: + framework: + enabled: true + # 执行器配置 + executor: + core-pool-size: 10 + max-pool-size: 50 + queue-capacity: 500 + keep-alive-seconds: 60 + thread-name-prefix: pipeline-exec- + execution-timeout-seconds: 0 + allow-core-thread-timeout: false + # 调度器配置 + scheduler: + enabled: true + pool-size: 5 + schedule-check-interval-ms: 1000 + # 检查点配置 + checkpoint: + enabled: true + interval-seconds: 60 + timeout-seconds: 300 + min-pause-between-seconds: 10 + max-concurrent-checkpoints: 1 + storage-path: ./checkpoints + externalized-checkpoint: false + retained-checkpoints: 3 + # 指标配置 + metrics: + enabled: true + report-interval-seconds: 30 + jvm-metrics: true + system-metrics: true + prefix: pipeline.framework + # 状态管理配置 + state: + backend: memory + storage-path: ./state + incremental-checkpoints: false + # SQL批量任务配置 + sql-batch: + enabled: true + batch-size: 1000 + fetch-size: 500 + query-timeout-seconds: 300 + parallel-query: true + parallelism: 4 + max-memory-mb: 512 + auto-commit: false + # Reactor 线程池配置 reactor: scheduler: