Etl.Net 2.2.0 项目深度分析
-
- [Etl.Net 2.2.0 项目深度分析](#Etl.Net 2.2.0 项目深度分析)
-
- [1. 项目定位](#1. 项目定位)
- [2. Nuget 包](#2. Nuget 包)
- [3. 整体架构](#3. 整体架构)
- [4. 核心引擎架构(`Paillave.Etl`)](#4. 核心引擎架构(
Paillave.Etl)) -
- [4.1 响应式内核 --- 自研的 Push-based Reactive 框架](#4.1 响应式内核 — 自研的 Push-based Reactive 框架)
- [4.2 ETL 流层](#4.2 ETL 流层)
- [4.3 执行引擎](#4.3 执行引擎)
- [5. 39 个流操作符扩展方法](#5. 39 个流操作符扩展方法)
- [6. 关联机制 (Correlation)](#6. 关联机制 (Correlation))
- [7. 文件抽象层](#7. 文件抽象层)
- [8. 数据库持久化](#8. 数据库持久化)
- [9. 文本文件解析](#9. 文本文件解析)
- [10. 设计亮点](#10. 设计亮点)
- [11. 不足](#11. 不足)
Etl.Net 2.2.0 项目深度分析
说明:分析项目源码版本,https://github.com/paillave/Etl.Net/releases/tag/v2.2.0
1. 项目定位
ETL.NET 是一个用纯 .NET 实现的大规模数据处理引擎 ,提供类似 LINQ 的流式 API,具备 SSIS(SQL Server Integration Services)的全部功能甚至更多。核心特点是响应式 (Reactive) 架构,支持多流并行、高性能、低内存占用,即使处理百万行数据也不在话下。
- 作者 : Stéphane Royer
- 许可证 : MIT
- 当前版本 : 2.4.0-beta.1 (SharedSettings.props)
csharp
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<Version>2.4.0-beta.1</Version>
<PackageIcon>NugetIcon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Authors>Stéphane Royer</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://paillave.github.io/Etl.Net/</PackageProjectUrl>
</PropertyGroup>
<ItemGroup>
<None Include="../../README.md" Pack="true" PackagePath=""/>
<None Include="../NugetIcon.png" Pack="true" Visible="false" PackagePath="" />
</ItemGroup>
</Project>
- Github 项目地址,
https://github.com/paillave/Etl.Net - 官方文档,
https://paillave.github.io/Etl.Net/
2. Nuget 包
ETL.NET 完全用 .NET 编写,适用于多平台使用,并且可以直接集成到任何应用中。
3. 整体架构
项目采用核心 + 插件式连接器 的模块化架构,解决方案包含 27 个项目:
| 层级 | 项目 | 职责 |
|---|---|---|
| 核心引擎 | Paillave.Etl |
流式处理引擎、响应式内核、所有操作符 |
| 数据格式 | TextFile, ExcelFile, JsonFile, XmlFile, Pdf |
文件解析/序列化 |
| 存储连接器 | SqlServer, EntityFrameworkCore |
数据库读写 |
| 文件系统 | FileSystem, Zip, S3, AzureStorageAccount, Dropbox |
文件源/目标 |
| 传输协议 | Ftp, Sftp, Http, Mail |
网络传输 |
| 安全 | Pgp |
加密/解密 |
| 外部集成 | GraphApi, Bloomberg |
第三方 API |
| 辅助 | ExecutionToolkit, FromConfigurationConnectors, Paillave.EntityFrameworkCoreExtension |
工具/扩展 |
| 测试 | Tests, XmlFileTests |
单元/集成测试 |
| 教程 | SimpleTutorial, BlogTutorial, Samples |
示例代码 |
4. 核心引擎架构(Paillave.Etl)
核心引擎分为两大部分:响应式内核 (Reactive Core) 和 ETL 流层 (Stream Layer)。
4.1 响应式内核 --- 自研的 Push-based Reactive 框架
项目没有 使用 System.Reactive (Rx.NET),而是实现了一套自己的响应式原语:
- IPushObservable --- 核心可观察序列接口,支持
Subscribe(onPush, onComplete, onException) - IPushSubject --- 同时是
Observable + Observer + Disposable - PushSubject --- 核心
Subject实现(218行),管理订阅列表、值推送、完成和异常传播,支持CancellationToken
59 个响应式操作符 位于 Reactive/Operators/ 下:
MapSubject,FilterSubject,FlatMapSubject--- 基础变换AggregateSubject,AggregateGroupedSubject--- 聚合DistinctSubject,GroupSubject,SortedGroupSubject--- 去重/分组LeftJoinSubject,CombineWithLatestSubject--- 流连接ChunkSubject,SkipSubject,TakeSubject--- 分块/截取ConcatenateSubject,MergeSubject--- 流合并SubstractSubject--- 流差集
关键工厂类 PushObservable 提供 FromEnumerable, FromSingle, Merge, CombineWithLatest, Empty, Range 等静态方法。
4.2 ETL 流层
四种流类型:
| 接口 | 实现 | 含义 |
|---|---|---|
IStream<T> |
Stream | 普通无序流 |
ISingleStream<T> |
SingleStream<T> |
只包含一个元素的流(如配置/上下文) |
ISortedStream<T,TKey> |
SortedStream<T,TKey> |
按 Key 排序的流 |
IKeyedStream<T,TKey> |
KeyedStream<T,TKey> |
按键分组的流 |
节点基类 StreamNodeBase<TOut, TOutStream, TArgs>(225 行):
- 每个操作都是一个节点,包含名称、执行上下文、输入/输出流
- 自动追踪节点间的依赖关系(
GetInputStreams通过反射读取Args中的IStream属性) - 自动管理 IDisposable 资源(
WrapSelectForDisposal系列方法) - 创建输出流时自动注入追踪逻辑(计数、异常捕获)
4.3 执行引擎
StreamProcessRunner 是入口点:
config → PushSubject → SingleStream<TConfig> → 用户定义的 Job Pipeline
↓
TraceStream → 追踪/日志/统计
执行流程:
- 创建
JobExecutionContext(管理节点、任务、追踪)和TraceExecutionContext - 构建
DI容器(ServiceCollection+Logging+MemoryCache) - 注册取消令牌回调
- 调用用户定义的
_jobDefinition(startupStream)构建流图 - 推送
config值启动流水线 - 等待所有
Observable完成(Task.WhenAll) - 返回
ExecutionStatus(成功/失败 + 统计 + 错误信息)
- 所有节点的任务(
_tasksToWait) - 资源释放(
CollectionDisposableManager) - 追踪事件推送(
_traceSubject) - 错误时自动取消(
_internalCancellationTokenSource.Cancel)
5. 39 个流操作符扩展方法
位于 Extensions/ 目录,采用流式 DSL 风格 ,每个操作符的第一个参数是字符串名称(用于追踪和调试):
| 类别 | 操作符 |
|---|---|
| 变换 | Select (567行!), SelectResolution (431行), SelectSection |
| 过滤 | Where, Distinct, OfType, Top, Skip, First, Last |
| 聚合 | Aggregate, GroupBy, Pivot, Chunk |
| 关联 | CorrelateToSingle, CorrelateToMany, LeftJoin, Lookup |
| 组合 | Union, UnionAll, Substract, CombineAllSingles |
| 应用 | CrossApply, Do, SubProcess |
| 排序 | Sort, EnsureSorted, EnsureKeyed, ReKey |
| 追踪 | GetStreamStatistics, KeepLastTracesPerNode, Fix |
| 控制 | WaitWhenDone, SetForCorrelation, UnsetForCorrelation |
6. 关联机制 (Correlation)
Correlated 是 ETL.NET 的独有特性:
- 通过
SetForCorrelation为每行数据分配关联键(HashSet<Guid>) - 通过
CorrelateToSingle/CorrelateToMany实现跨流关联 (类似数据库JOIN) - 典型场景:从扁平文件读取数据,分发到多张表(作者、分类、文章),通过关联键维护外键关系
7. 文件抽象层
IFileValue 统一了所有文件源的抽象:
IFileValueProvider--- 提供文件(FileSystem, S3, Dropbox, Azure...)IFileValueProcessor--- 处理文件(Zip解压、PDF解析...)FileValueConnectors--- 连接提供者与处理器
8. 数据库持久化
SqlServer 直连 --- SqlServerSaveStreamNode:
- 自动生成
MERGE SQL(IF EXISTS → UPDATE ELSE INSERT) - 支持
SeekOn(匹配键)、DoNotSave(忽略列) - 支持
ODBC/OLE DB适配
EF Core --- EfCoreSaveStreamNode:
- 批量处理(
Chunk(batchSize)→ProcessChunk) - 双模式:
SaveMode.EntityFrameworkCore(标准)和SaveMode.SqlServerBulk(Bulk Insert) - 支持
SeekOn+AlternativelySeekOn(多键匹配) - 自动
Detach实体避免ChangeTracker内存泄漏
9. 文本文件解析
FlatFileDefinition 提供了强类型的平面文件映射:
- 表达式树定义列映射:
i.ToColumn("email"),i.ToDateColumn("date", "yyyy-MM-dd"),i.ToNumberColumn<int?>("amount", ".") - 支持分隔符文件(
IsColumnSeparated(',')) 和固定宽度文件(HasFixedColumnWidth) - 支持编码、
CultureInfo、行预处理、值预处理
10. 设计亮点
- 完全响应式 : 自研
Push-based Reactive框架,数据按需推送,天然支持并行多流 - Fluent API: 所有操作链式调用,每个节点有名称用于追踪
- 内存高效 : 流式处理不缓存全量数据,
StreamWithResource确保流资源正确释放 - 可观测性 : 内置追踪系统(
TraceEvent → TraceStream),支持自定义追踪处理 - 可扩展 : 新增连接器只需实现
Provider/Processor接口,5 分钟即可完成 - Correlation: 独有的跨流关联机制,优雅解决数据规范化问题
- 内存泄漏修复 : 代码中有大量注释标注了内存泄漏修复点(
CancellationToken注册释放、Entity Detach、Subscription清理等)
11. 不足
- 文档不完整 :
README.md中明确提到文档严重不足 - 测试覆盖低: 测试项目主要是文档示例测试,缺乏核心引擎的单元测试
- 部分代码有 TODO : 如
IStream.GetMatchingStream返回object的类型安全问题 - 自研 Reactive : 没有使用成熟的
Rx.NET,可能在边界场景有未覆盖的bug
