資料流的三種模式 (Modes of Dataflow)

★★★★Draft

FromDDIA

📚 From the Books

資料流的三種模式 (Modes of Dataflow)Encoding & Evolution

選好編碼格式、談完前後向相容之後,還有一個問題:編碼過的資料到底怎麼從一個程序流到另一個? DDIA 把它收斂成三條路——透過資料庫、透過服務呼叫 (REST/RPC)、透過非同步訊息。三條路各自對「相容性」提出不同的要求。

🧠 Intuition

TIP

相容性不是抽象潔癖,而是滾動升級 (rolling upgrade) 逼出來的現實:程式碼無法一次全換,新舊版本必然並存一段時間。於是每條資料流都得同時扛住向後相容(新碼讀舊資料)向前相容(舊碼讀新資料)——向後通常容易,向前才是難點(舊碼得學會忽略它看不懂的新內容)。

⚖️ Tradeoffs

透過資料庫:資料比程式碼活得久
  • 寫入方編碼、讀取方解碼;同一個資料庫可能被新舊版程式同時讀寫,所以要同時撐向後與向前相容。
  • 最容易被忽略的坑——保留未知欄位 (preserving unknown fields):舊版程式讀到含新欄位的記錄、更新後寫回時可能把新欄位弄丟。這是應用層要主動處理的責任。
  • 為什麼向前相容非有不可?因為資料比程式碼活得久 (data outlives code):程式幾分鐘就能全數替換,但資料庫裡同時躺著五毫秒前和五年前寫入的資料,除非主動遷移。多數關聯庫允許「加一個帶 null 預設的欄位」而不必重寫既有資料,schema 演化讓整庫看起來像單一 schema,底層其實混著各歷史版本。
透過服務:REST vs RPC
  • 最常見是客戶端-伺服器 (client-server);伺服器又可以是別的服務的客戶端,串成 SOA / 微服務 (microservices)——核心目標是各服務能獨立部署與演化,因此服務間編碼必須跨版本相容。
  • REST:建立在 HTTP 原則上的設計哲學,用 URL 識別資源 (resource)、善用 HTTP 特性;常搭 OpenAPI (Swagger) 產文件。相對地 SOAP 基於 XML、用 WSDL 描述,因複雜與互通問題在小公司式微。
  • 選型:REST 適合公開 API(好實驗、好除錯、生態豐富);RPC 框架(gRPC / Finagle / Rest.li)主要用於同組織內部服務,可用二進位編碼換更好效能。
RPC 的原罪:把網路呼叫偽裝成本地呼叫
  • RPC 想營造位置透明性 (location transparency)——讓遠端呼叫看起來像本地函數。但這個抽象本質有缺陷,因為兩者根本不同:
    • 網路請求會不可預測地失敗;可能逾時而不知是否已被處理
    • 重試可能造成重複執行,除非有冪等性 (idempotence)
    • 延遲劇烈變動;參數得編碼成 bytes(大物件尤難);client / server 可能用不同語言,型別轉換出錯。
  • 新一代框架乾脆明確區分遠端與本地,用 futures / promises 封裝非同步(gRPC 還支援串流)。相容性上可假設伺服器先升級:請求端維持向後相容、回應端維持向前相容;跨組織時無法逼客戶端升級,只能長期維護多個 API 版本(URL 帶版本號或用 Accept header)。
透過非同步訊息:broker 與 actor
  • 非同步訊息傳遞介於 RPC 與資料庫之間:像 RPC 一樣低延遲送達,又像資料庫一樣經訊息代理 (message broker) 暫存。
  • broker(RabbitMQ / ActiveMQ / NATS / Kafka)的好處:收件者掛了能緩衝、崩潰重啟能重送、發送者不需知道對方 IP/port(位置透明)、一則訊息可多播給多個 consumer、sender 與 recipient 邏輯解耦。訊息通常單向,要回覆得走另一條 channel。
  • broker 不強制資料模型——訊息只是帶 metadata 的 bytes,任何編碼皆可;只要格式前後向相容,publisher 與 consumer 就能各自獨立升級(但若 consumer 轉發到別的 topic,一樣要小心保留未知欄位)。
  • Actor 模型:把邏輯封進 actor,各有局部狀態、只靠非同步訊息溝通。分散式 actor 框架(Akka / Orleans / Erlang OTP)讓同節點或跨節點都用同一套訊息機制——位置透明在這裡比 RPC 自然,因為 actor 模型本就假設訊息可能遺失。但滾動升級下各家編碼策略是坑:Akka 預設 Java 序列化(不相容,需換 Protobuf)、Orleans 預設格式不支援滾動升級、Erlang OTP 改 record schema 出乎意料地難。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading

🔗 Dive Deeper