🎯 什麼情境該想到我

當你「通道裡殘留了過期或不該處理的舊訊息(例如測試 / 重啟後),想把它們清掉以免干擾」的時候。

⚙️ 怎麼用(步驟 / 公式)

意圖:移除通道中殘留或不再需要的訊息,避免舊訊息在測試、重啟或重跑時造成非預期行為。

做法 / 結構要點:

  • 用一個清除器連上目標通道,把其中的訊息取出並丟棄。
  • 可全清,或依條件(如過期、特定屬性)選擇性清除。
  • 常在測試前 / 系統重啟後執行,確保通道乾淨。

★★ 這是一個「破壞性操作」,設計時要當成危險工具

★ 它的正當用途幾乎只有三種:
① ★★ 測試/開發環境的重置(★ 最主要、也最安全的用途)
② ★ 生產事故:通道裡全是毒訊息,重新部署也救不了,必須清空才能前進
③ ★ 已知過期且無意義的積壓(限時活動結束後累積的通知)

★★ 除此之外,「清空通道」等於「刻意丟棄資料」。
而在事故當下做這件事,是最容易造成不可逆損失的操作之一。

★★ 各家 broker 的清除方式與可逆性

查證 2026-09-25。

broker方式★ 可逆嗎
RabbitMQrabbitmqctl purge_queue / 管理介面★★ 不可逆
SQSPurgeQueue API★★ 不可逆;且 60 秒內只能呼叫一次
Kafka★ 不清訊息,而是「移動 consumer offset」★★ 可逆!(offset 可以移回去,只要還在 retention 內)
Kafka(真的刪)kafka-delete-records 或縮短 retention不可逆

★★ Kafka 的「跳過積壓」是可逆的,這是它最被低估的優勢:

# ★ 把 consumer group 的 offset 移到最新 —— 訊息還在 log 裡
kafka-consumer-groups --reset-offsets --to-latest --group g1 --topic t1 --execute
# ★★ 發現不該跳 → 移回去就好(只要還在 retention 內)
kafka-consumer-groups --reset-offsets --to-datetime 2026-09-25T10:00:00.000 --group g1 --topic t1 --execute

★ 所以在 Kafka 上,「清除通道」應該一律用「移 offset」而不是刪訊息。

★★ 清除前必做的三件事

① ★★ 先備份:把要清掉的訊息落地
   → ★ RabbitMQ/SQS 上:先用一個消費者把它們讀進 [[訊息存放區]]
   → ★★ 沒有備份的 purge 是不可逆的資料遺失
② ★ 先暫停生產者(或至少知道還在進多少)
   → ★ 否則你清完的下一秒又滿了(見 [[控制匯流排]] 的暫停能力)
③ ★ 記錄:誰、什麼時候、清了哪個通道、清了幾則、為什麼(ticket)
   → ★★ 事後一定會有人問「那些訊息去哪了」

★★ 權限與防護:這個操作必須難以誤觸

★ 四層防護:
① ★★ 生產環境的 purge 權限不該給一般開發者的日常帳號
② ★ 一定要有「環境確認」步驟(★ 打錯環境是最常見的事故)
③ ★ 不要把 purge 做成一個 HTTP GET 端點(★ 會被瀏覽器預取、被爬蟲觸發)
④ ★★ 要有審計日誌
// ★ 如果真的要提供這個端點
mux.HandleFunc("/admin/purge", requireAuth(requireConfirm(func(w, r) {
    if r.Method != http.MethodPost { http.Error(w, "", 405); return }   // ★ 不能是 GET
    if r.FormValue("env") != cfg.Env { http.Error(w, "環境不符", 400); return }
    if r.FormValue("channel") != r.FormValue("confirm_channel") {       // ★ 打兩次名字
        http.Error(w, "確認不符", 400); return
    }
    audit.Log(r, "purge", …)                                            // ★ 先記錄再執行
    …
})))

★★ 更好的替代方案(按推薦度)

① ★★ Kafka:移 offset(可逆)
② ★★ 把訊息「搬到別處」而不是刪掉
   → 一個消費者把它們全部轉發到 archive.<channel> 通道
   → ★ 通道空了,而訊息還在 ⇒ 可以事後分析、可以選擇性重放
③ ★ 設定 TTL 讓它自然過期(見 [[訊息過期]])
   → ⚠️ 但注意 RabbitMQ 的陷阱:整個佇列過期時訊息不進 DLX
④ ★ 修好問題然後讓它正常消化
   → ★★ 多數「必須清空」的情況其實是「消費者壞了」——修消費者比清訊息正確

★★ ② 是最好的通用做法:它達成了「清空」的效果,但保留了回頭路。

★ 兄弟辨析

差別
控制匯流排★ 清除指令透過它下達
訊息過期★ 被動的、逐則的過期;清除是主動的、整批的
死信通道★ 毒訊息應該進 DLQ 而不是逼你清空整條通道
訊息存放區★ 清除前的備份去處

🧪 我實際套用的紀錄

  • (待填)

⚠️ 注意 / 什麼時候不適用

  • 清除是破壞性動作,誤清會丟掉有效訊息,務必謹慎。

  • 生產環境要嚴格控管權限與時機。

  • 多用於測試、除錯、維運情境。

  • ★★ 清空通道是刻意丟棄資料,而且 RabbitMQ/SQS 上不可逆。 事故當下做這件事最容易造成不可逆損失。

  • ★★ Kafka 上一律用「移 offset」而非刪訊息——它是可逆的(只要還在 retention 內)。

  • ★★ 清除前先備份(用消費者把訊息讀進 訊息存放區)。沒有備份的 purge 就是資料遺失。

  • ★★ 更好的做法是「搬到 archive 通道」而不是刪掉:達成清空效果但保留回頭路。

  • ★ 清除前先暫停生產者,否則清完下一秒又滿了。

  • ★★ purge 端點不能是 GET(會被瀏覽器預取、爬蟲觸發),要有環境確認與二次確認,並記審計日誌。

  • ★ 生產環境的 purge 權限不該在一般開發者的日常帳號上。 打錯環境是最常見的事故。

  • ★★ 多數「必須清空」的情況其實是「消費者壞了」 —— 修消費者比清訊息正確。

  • 毒訊息應該進 DLQ,如果它逼得你清空整條通道,代表 DLQ 機制沒做好。

🔗 相關工具