這篇怎麼用:讀其他篇遇到不熟的詞回來搜(
Ctrl+F)。每條 1-2 行說清楚是什麼,不教怎麼用 ⸺ 想深入再點「→ 看哪篇」。
整個 Production EDA 系列 跟 #15 / #25 共用這份 dictionary。
基礎概念
broker(訊息中介) 事件的中介者。Producer 推進去、consumer 從另一端拉出來。例:RabbitMQ、Kafka、Redis Streams、NATS。 → 詳見 #14、篇 1 §3
event(事件)
「某件事發生了」這件事本身的 record,過去式語意。例:order_created、payment_completed。
message(訊息) broker 裡傳遞的資料單位。event / job / 命令都裝在 message 裡。實務上常跟 event 混用。
job(任務)
「要做某件事」的命令式 record。例:send_email、deduct_inventory。Laravel dispatch(new SendEmailJob()) 是 job。
handler / listener / consumer 收到 event 後跑業務邏輯的 code。三個詞意思一樣,本系列統一叫 handler。
producer / publisher 產生並推 event 到 broker 的一方。
consumer / subscriber 從 broker 取 event 並交給 handler 的一方。
synchronous(同步) A 呼叫 B 就等 B 回來,A 中間什麼都不做。
asynchronous(非同步) A 把工作丟給 B,不等、立刻回去做別的。EDA 的「event-driven」就是非同步串組件。
fan-out(扇出) 一個 event 觸發多個 handler 並行處理。例:訂單建立 → email + inventory + analytics 三條路同時跑。
in-process bus / in-memory bus(程式內事件匯流排)
Node EventEmitter / Laravel event() / Rails ActiveSupport::Notifications 這類「同 process 內」的 pub-sub 工具。不跨機器,不是真 EDA。 → 詳見 #25 §3
半 EDA 用了 event + queue + listener 但沒做 production reliability(outbox / dedup / retry / DLQ / audit)的狀態。Framework 入門 80% 都到這裡。 → 詳見 篇 1 §4
交付語意(delivery semantics)
at-least-once delivery(至少一次交付) broker 保證至少送一次,可能送 N 次。絕大多數 broker(RabbitMQ / Kafka / Redis Streams / SQS)的預設。 → 詳見 篇 3 §2
at-most-once delivery(至多一次交付) broker 最多送一次,可能完全沒送到。少數場景才會選(best-effort metric 之類)。
exactly-once delivery(恰好一次交付) broker 保證精準送一次。Kafka transactions 之類技術上做得到,但代價極高、到外部系統那一刻又退回 at-least-once。業界共識:不追。
effectively-once processing(效果上一次處理) 業界 EDA 的核心 thesis:at-least-once delivery + idempotent consumer = effectively-once processing。對使用者來說「動作只發生一次」就夠了,不追技術上的 exactly-once delivery。 → 詳見 篇 3 §6
redelivery(重投) broker 把同一個 event 又送一次給 consumer。原因:ack 丟失、consumer crash、rebalance、人工觸發 retry。
ack(acknowledgement) consumer 告訴 broker「我處理完了」的訊號。Broker 沒收到 ack 就重投。
Reliability patterns
outbox pattern
producer 把要送的 event 寫進自己 DB 的 outbox 表(跟業務寫入同 transaction),背景 worker 再讀表 publish 到 broker。解決 dual-write 問題。 → 詳見 篇 2
dual-write problem 要同時寫 DB 跟 broker,但沒共用 transaction → 任何一邊失敗造成不一致。
transactional outbox outbox pattern 的正式名稱。
inbox pattern consumer 端的對稱解 ⸺ event 進來先存 inbox 表,handler 從表處理。比 dedup 表更嚴格。本系列 demo 沒實作(→ 未來 Wave 3)。
dedup table(processed_events 表) consumer 端冪等鎖。PK = (event_id, handler)。每個 handler 處理過一個 event 就 INSERT 一筆,下次同 event 來查得到就 skip。 → 詳見 篇 3 §4
idempotency(冪等) 跑 1 次跟跑 N 次的最終結果一樣。N 是任何正整數。 → 詳見 篇 4a §2
Idempotency-Key client 加在 HTTP request 的 header,server 用它當 producer-side dedup 的 key。Stripe / PayPal API 都用。 → 詳見 篇 3 §3
retry with backoff handler 失敗後等一段時間再重試。Backoff 種類:linear / exponential / jitter。 → 詳見 篇 5 §3
dead-letter queue (DLQ) retry 用盡仍失敗的 event 丟去的「永久失敗 sink」。等人工或自動流程處理。 → 詳見 篇 5 §6
transient failure(暫時失敗) 能 retry 救回(API 偶發 504 / DB temp lock / network blip)。
permanent failure(永久失敗) retry N 次也救不回(payload 結構錯 / 外鍵不存在 / 業務規則改了)。該進 DLQ。
poison message(毒訊息) 永遠處理不了但會被 broker 一直重投的 message。Naive retry 會被它卡住整條 pipeline。
timeout
handler 跑超過設定時間就強制 throw,讓 retry / DLQ 機制接手。Node 用 Promise.race。
handler policy / HandlerPolicy 每個 handler 自帶的 retry 政策(maxAttempts / backoffMs / timeoutMs / dedupEnabled)。不該全系統共用一個 config。 → 詳見 篇 5 §4
觀察性 / Audit
application audit
用業務 DB 的多張表 join 出「這個 event_id 跑到哪了」的輕量觀察 pattern。本系列 demo 用 /audit/event/:id 落地。 → 詳見 篇 6
distributed tracing 跨 process / 跨 service 把同一條業務鏈接起來的觀察方式。業界標準是 OpenTelemetry (OTel)。
correlation_id
同一條業務鏈所有 event 共享的 ID(通常是鏈起點 event 的 id)。/audit/correlation/:id 拉整條鏈。
causation_id 「是哪個 event 觸發我」⸺ 補上 correlation_id 沒有的順序資訊,組起來是樹。
schema_version event payload 的格式版本,給 forward / backward compat 用。
span / trace OpenTelemetry 的 unit。一個 span = 一段工作(一次 API call / 一次 DB query)。trace = 整條鏈的 span tree。
OpenTelemetry (OTel) 業界標準的 distributed tracing 規格 + SDK。
3 支柱(observability three pillars) Logs(結構化日誌)/ Metrics(時序量化資料)/ Traces(跨 process 鏈)。 → 詳見 #26 (待寫)
SQL idioms(篇 4a 重點)
ACID DB transaction 的 4 個保證:Atomicity(要嘛全成)/ Consistency(保持完整性)/ Isolation(彼此看不見)/ Durability(不會掉)。
transaction(交易)
多條 SQL 包成原子單位。BEGIN; ... ; COMMIT;。其中一條失敗就全 rollback。
ON CONFLICT
PostgreSQL / MySQL / SQLite 都有的 keyword,定義 INSERT 撞 PK / UNIQUE 時要 DO NOTHING 還是 DO UPDATE。冪等寫入的核心 idiom。 → 詳見 篇 4a §2
UPSERT
「沒就 INSERT、有就 UPDATE」。PG: INSERT ... ON CONFLICT DO UPDATE、MySQL: ON DUPLICATE KEY UPDATE。
absolute UPDATE(絕對值 UPDATE)
SET status = 'paid' 這種。跑 N 次結果一樣,天然冪等。 → 詳見 篇 4a §4.2
delta UPDATE(增量 UPDATE)
SET qty = qty - 1 這種。不冪等 ⸺ 跑 N 次扣 N。Laravel decrement() 就生這種 SQL。 → 詳見 篇 4a §4.3
ledger + balance pattern 解 delta UPDATE 不冪等的標準解法。Ledger 是 append-only 流水(event_id PK 防重)、balance 是 snapshot。兩者同 transaction 寫。 → 詳見 篇 4a §4.4
optimistic concurrency(樂觀並發)
讀資料時記下 version、寫入時 WHERE version=$expected 檢查。撞到衝突就拒絕(不是 lock)。 → 詳見 篇 4b §1
SELECT FOR UPDATE(悲觀鎖) PostgreSQL / MySQL 都有,鎖住該 row 直到 transaction 結束。其他 transaction 等。
SKIP LOCKED
配合 FOR UPDATE 用:被鎖住的 row 就跳過、不等。給多 worker 水平擴展的標準 idiom。outbox / delayed-events worker 都用。
advisory lock PostgreSQL 的「跟 row 無關」的鎖,給跨 row 跨 table 的互斥用。本系列 demo 沒實作。
read-modify-write race SELECT 後判斷 → UPDATE 中間有 race window。兩個 worker 都讀到 qty=1 → 都通過 if 判斷 → 都 UPDATE → qty=-1 超扣。 → 詳見 篇 4b §2
SETNX(SET if Not eXists) Redis 命令:key 不存在才設值。常用來當 dedup / lock。不能跟 SQL transaction 一起 commit,所以 handler 寫業務 DB 時不適合用它當 dedup。
Async primitives
queue / job queue 事件驅動 + 單一 consumer 的 async 工具。Producer 推 job、worker 從 queue 拉。例:Laravel queue、Sidekiq。 → 詳見 #15
event-driven / pub-sub 事件驅動 + 多 consumer fan-out。
cron / scheduler
時間驅動 ⸺ 固定週期跑(每分鐘 / 每天凌晨 3 點)。例:unix crontab、K8s CronJob、Laravel schedule:run。
delayed event
排程在「未來某時間點」觸發的 event。本系列用 delayed_events 表 + polling worker 實作。 → 詳見 #25 §4
polling worker 主動定期查 DB / queue 看有沒有新工作。對比是 push(broker 主動丟訊息給 worker)。
FOR UPDATE SKIP LOCKED (見 SQL idioms section)outbox / delayed-events worker 都用,給多 worker 水平擴展。
worker / consumer process 跑 handler 的背景 process。跟 web process 分離部署是 production 標準。 → 詳見 #25 §3
部署 / Scaling
deployment topology(部署拓樸) web / worker / cron 各自跑在哪些 process / pod / 機器。本系列分 4 層:mono → process 分 → deployment 分 → 物理機分。 → 詳見 #25 §9
K8s CronJob K8s 的 cron 資源,到時間 spawn pod 跑、跑完 pod 終止。比常駐 worker 省資源。
HPA (Horizontal Pod Autoscaler) K8s 根據 metric 自動調 pod 數量。Web 看 RPS、worker 看 queue depth。
queue depth / consumer lag broker queue 裡未處理的 event 數量。worker 過載最直接的訊號(CPU 可能還很低)。
outbox lag outbox 表內 pending status 的最老 row 滯留時間。Producer 端 publish 卡住的訊號。
graceful shutdown process 收到 SIGTERM 後優雅關閉(處理完當下工作、回 ack、釋放資源)。跟 broker redelivery 一起構成可靠性的基礎。
框架 / 工具
Laravel queue / Horizon PHP 框架的 queue 系統。Horizon 是 UI + 多 worker 管理。
Laravel Telescope 單 application 的 debug tool(HTTP / SQL / mail / job 全部一份)。同 process 看得全、跨 process 看不到。
Laravel event() + ShouldQueue 推 event 到 queue 由 ShouldQueue listener 非同步處理 ⸺ 入門 80% 但只是「半 EDA」。
Laravel DispatchAfterCommit 保證 DB transaction commit 後才 dispatch job。解了 partial(DB rollback 還是 dispatch 了)但沒解 broker 掛。 → 詳見 篇 2 §7
testcontainers 本系列 demo 跑整合測試用的套件,自動起 Postgres / Kafka / RabbitMQ / NATS / Redis container。
你還不熟的話從這幾篇開始
- 完全沒接觸 EDA → 篇 1 Production EDA 入門
- 想了解 cron / queue / event 差別 → #15 async 原語
- 已在用 Laravel queue 想升級 → 篇 1 §7 Laravel 對譯
整個系列地圖在 篇 1 §5。