🎯 什麼情境該想到我
當你「想讓兩個獨立系統交換資料,但不想讓它們彼此直接相依,能接受不是即時、批次同步就好」的時候(例如一方每晚匯出一份檔案,另一方隔天匯入)。
⚙️ 怎麼用(步驟 / 公式)
意圖:讓每個應用程式各自產生與消費檔案,藉由共享檔案來交換資料,彼此不需知道對方的技術細節。
做法 / 結構要點:
- 產生方把資料匯出成一個雙方講好格式的檔案(欄位、編碼、檔名慣例)。
- 消費方在約定的位置與時間讀取並匯入該檔案。
- 需明確約定:檔案格式、命名、放置位置、產生頻率,以及「檔案何時算寫完可讀」。
★ 今天的檔案傳輸長什麼樣(2003 → 2026)
| EIP 書裡 | 今天的實作 |
|---|---|
| FTP/共享目錄 + 固定檔名 | ★ 物件儲存(S3/GCS/R2)+ 事件通知(S3 Event → SQS/Lambda) |
| CSV/固定寬度文字 | ★ Parquet/Avro(欄式、自帶 schema、可壓縮) |
| cron 每晚跑一次 | Airflow/Dagster/Step Functions 排程;或事件觸發 |
| 「檔案寫完了」靠時間猜 | ★ 寫入 _SUCCESS 標記檔,或寫到 temp 名再 rename |
★ 這個模式完全沒有過時。 它今天的名字叫「資料湖」「批次 ETL」「S3 落地區」——
大量資料的搬運,批次檔案至今仍然比逐筆訊息便宜一到兩個數量級。
★★ 最重要的一個陷阱:怎麼知道「檔案寫完了」
// ❌ 消費端直接去讀 —— 可能讀到寫了一半的檔案
if _, err := os.Stat("/inbox/orders.csv"); err == nil { process(…) }三種正確做法:
// ① ★ 寫到暫時名稱,完成後 rename(同一檔案系統上 rename 是原子的)
tmp := dst + ".tmp"
writeAll(tmp); os.Rename(tmp, dst) // ★ 消費端只會看到完整的檔案
// ② ★ 額外寫一個標記檔(Hadoop/Spark 的慣例)
writeAll("/inbox/2026-09-25/data.parquet")
writeAll("/inbox/2026-09-25/_SUCCESS") // ★ 消費端只認有 _SUCCESS 的目錄
// ③ ★ 物件儲存本身就是原子的
// S3 的 PutObject 沒有「寫一半可見」的狀態 —— 物件要嘛完整存在,要嘛不存在
// 多段上傳(multipart)也是在 CompleteMultipartUpload 那一刻才可見⚠️ os.Rename 只有在「同一個檔案系統」上是原子的。 跨掛載點會退化成 copy + delete,
中間狀態是可見的——這是 NFS 共享目錄最常出事的地方。
★ 冪等與重複處理:檔案傳輸沒有「訊息 ID」
// ★ 用「檔名 + ETag/大小 + 已處理表」自己做去重
type processed struct{ Key, ETag string } // ★ 唯一索引在 (Key, ETag)
// → 同一個檔案被重放(例如上游重跑)時,ETag 相同就跳過;ETag 不同代表內容真的變了★ 移動檔案到 done/ 目錄是最常見的做法,但它不是交易性的:
process(f) 成功、move(f) 失敗 → 下次重跑會再處理一次。
→ 所以還是要冪等(見 冪等接收者)。
★ 什麼時候選檔案傳輸而不是訊息
| 選檔案 | 選訊息 |
|---|---|
| ★ 資料量大(GB 級以上)——逐筆訊息的 per-message overhead 會壓垮 broker | 資料量小、筆數多 |
| ★ 延遲容忍高(小時/天級) | 需要秒級以下 |
| ★ 需要重跑整批(回填、對帳) | 只關心增量 |
| 兩邊技術棧差異極大(大型主機、第三方廠商) | 兩邊都是你的服務 |
| ★ 需要人看得懂的中間產物(稽核、爭議處理) | — |
★ 判準:這是「資料同步」還是「事件通知」? 同步 → 檔案;通知 → 訊息。
兩者常常並存:檔案落地 → 發一則「檔案已就緒」的訊息(見 寄物單)。
🧪 我實際套用的紀錄
- (待填)
⚠️ 注意 / 什麼時候不適用
-
即時性差:資料只在檔案產生後才更新,通常是批次、有延遲。
-
缺乏交易與一致性保證,兩邊可能短暫看到不同步的資料。
-
格式一旦要改,雙方要一起協調;不適合需要即時互動或高頻交換的場景。
-
★★ 「檔案寫完了嗎」是這個模式的頭號 bug 來源。用 rename、
_SUCCESS標記檔,或物件儲存的原子語義。 -
★
os.Rename只在同一檔案系統上原子。跨 NFS 掛載點會退化成 copy + delete。 -
★ 沒有訊息 ID,去重要自己做(檔名 + ETag + 已處理表)。「移到 done/ 目錄」不是交易性的。
-
★ 檔案格式就是契約,而且沒有版本協商。CSV 多一欄可能讓下游整個錯位。用 Parquet/Avro 之類自帶 schema 的格式(見 工具-資料編碼與演進)。
-
CSV 的地雷很多:BOM、換行在引號內、時區、千分位、Excel 把長數字變科學記號。這些每一個都造成過真實事故。
-
★ 延遲是小時/天級。需要即時就不要用這個模式。
-
檔案堆積要監控:
inbox/累積代表消費端掛了,而它不會自己告警。 -
重跑要能安全重跑。回填是檔案傳輸最大的優勢,但前提是處理端冪等。
🔗 相關工具
- 共享資料庫 Shared Database(同為整合風格,改用共用資料庫而非檔案交換)
- 遠端程序呼叫 Remote Procedure Invocation(同為整合風格,改用同步呼叫換取即時性)
- 訊息傳遞 Messaging(同為整合風格,改用非同步訊息兼顧解耦與即時)
- 企業整合模式
- 共享資料庫、遠端程序呼叫、訊息傳遞 —— 另外三種整合風格
- 寄物單 —— 大檔案走檔案傳輸、只在訊息裡放位址
- 冪等接收者 —— 檔案重放時的去重
- 工具-資料編碼與演進 —— 為什麼該用 Parquet/Avro 而非 CSV