「這個系統用了 MSMQ,聽說已經廢除了,是不是要趕快遷移比較好?」──這是過去一年左右,在遺留系統遷移諮詢中反覆被問到的問題。
答案有點曲折。MSMQ(Microsoft Message Queuing)別說廢除了,連官方的淘汰(deprecated)都稱不上。截至 2026 年 7 月,Microsoft 的淘汰功能清單中並沒有 MSMQ 的名字,現行的 Windows 也仍以選用功能的形式隨附。儘管如此,現場之所以覺得它是「已經結束的技術」,是因為.NET 端的官方受管理 API 已經封閉。處理 MSMQ 的標準函式庫 System.Messaging 只存在於 .NET Framework,並未移植到 .NET(Core 以後)。
也就是說,MSMQ 並不是「明天就會停用」的技術,而是「一旦想把應用程式升級到 .NET,就會立刻撞牆」的技術。
本文的目標讀者是負責維護、遷移既有 MSMQ 系統的開發者,以及被要求做出這項判斷的資訊系統部門人員。目標是協助各位確認「聽說已經廢除」這個傳聞的真偽,並依照自己所處環境的條件,判斷是否要進行遷移。
本文將把傳聞與事實區分開來,整理繼續使用或遷移的判斷,以及遷移目標的選擇方式。至於從 VB6 或 .NET Framework 遷移本身,已經在「VB6 遷移到 .NET 的實務做法」「.NET Framework → .NET 遷移前檢核表」中處理過,本文將聚焦在佇列的部分。
1. 先講結論
- MSMQ 官方並未將其列為淘汰項目。無論是 Windows 用戶端的淘汰功能清單,還是 Windows Server 的移除・停止開發功能清單,都沒有列出它(截至 2026 年 7 月)。12
- 但 .NET 沒有官方的受管理 API。System.Messaging 的適用範圍僅限於 .NET Framework 1.1〜4.8.1,3 也不包含在 .NET 遷移時的相容手段「Windows 相容套件」中。4 透過 P/Invoke 呼叫原生 Win32 API 的路仍然存在,但那只是延命手段的定位(第 3 章)。
- 雖然有 CoreWCF 這條移植路徑,但適用對象僅限於經由佇列呼叫的 WCF 服務(接收端)遷移,是需要先確認條件才能使用的路徑。CoreWCF 本身有 Microsoft 的支援政策,但 MSMQ 傳輸(CoreWCF.MSMQ)的實作依賴 .NET Framework 版 System.Messaging 的社群移植版本。5
- 判斷的軸心在於「是否要把應用程式升級到 .NET」。若要升級,原則上佇列也應一併遷移(雖然也有透過 P/Invoke 或 CoreWCF 延命的路徑,但那等於是選擇承擔維護負擔)。若不升級(擱置不動),只要 .NET Framework 4.8 還作為作業系統元件受到支援,就可以規劃讓系統持續運作下去。6
- 遷移目標的首選,不是訊息代理服務,而是資料庫的資料表佇列。MSMQ+分散式交易(DTC)所維護的一致性,用能與業務資料庫在同一交易中處理的資料表佇列來取代,才是最直接的做法(第 5 章)。
- 請優先盤點訊息格式。BinaryFormatter 系列的序列化已在 .NET 9 中從執行環境移除,7 若讓新舊系統以舊格式並存,會在遷移中卡關(第 6 章)。
2. 30 秒讀懂 MSMQ
MSMQ 是隨附於 Windows 的訊息佇列基礎架構。應用程式將訊息寫入佇列,另一個應用程式(不論在同一台機器上或不同機器上)可以在方便的時機取出。其特徵可以歸納為以下三點。
- store-and-forward(儲存並轉發): 即使接收端當掉,訊息也會先儲存在本機端,等恢復後再送達,對於據點之間不穩定的線路很有韌性。不過這種持久性並非無條件成立,預設的高速(express)訊息可能只停留在記憶體上,一旦 MSMQ 服務或機器重新啟動就會遺失。只有傳送端指定為 Recoverable(可復原)的訊息,或交易訊息,才會被持久化到磁碟。
- 交易: 可以把佇列操作納入交易,搭配 MS DTC(分散式交易協調器)使用時,可以將「從佇列取出」與「更新資料庫」放在同一個分散式交易中確定。
- 隨作業系統附帶: 由於不需要額外導入中介軟體就能使用,因此在 2000 年代的業務系統中被廣泛採用,尤其是訂單資料的串接、報表的非同步處理、製造現場工序之間的串接等場景。
正因為「隨作業系統附帶、支援交易、對離線環境有韌性」這樣的組合十分優秀,MSMQ 至今仍在第一線持續使用。在思考遷移目標時,實際使用到這三項特性中的哪一項,正是判斷的核心。
本文使用的最小詞彙集
第 5 章的判斷表與第 6 章的盤點,都以下列用語為前提。請在此一次掌握。8
- 私人佇列(Private Queue) ── 不會發布到目錄服務(Active Directory),只登錄在該台電腦上的佇列。以
.\private$\佇列名稱這樣的形式指定。相對的,公用佇列(Public Queue) 會登錄到目錄服務,可以在網域內搜尋。中小規模業務系統所使用的,幾乎都是私人佇列。 - express 訊息 ── 預設的傳送模式。無論在配送中或配送後都放在記憶體上,因此速度快,但 MSMQ 服務或機器停止時就會遺失。
- recoverable(可復原)訊息 ── 在傳送端與中繼的各台電腦上都會寫入磁碟,並在目的佇列中同樣保留在磁碟上的模式。可以跨越重新啟動而保留下來,由傳送端明確指定。
- 交易佇列 ── 只處理交易訊息的佇列。在建立佇列時就已決定,之後無法變更(這一點會影響第 6 章的遷移步驟)。
- DTC(分散式交易協調器) ── 用來協調跨越多個資源(如 MSMQ 佇列與資料庫)之交易的 Windows 服務。它是讓「從佇列取出」與「更新資料庫」一併確定的機制,也是第 5 章遷移判斷中最大的爭議點。
- 日誌佇列 / 配送失敗(死信)佇列 ── MSMQ 自動產生的系統佇列。日誌佇列保存「已傳送/已取出訊息的副本」,配送失敗佇列則保存「無法送達的訊息」。放著不管會持續佔用容量,因此在擱置不動的運維方式中會成為監控對象(第 7 章)。
3. 事實關係 ── 「已經廢除」並不正確
「MSMQ 還能不能用」這個問題之所以會讓人混亂,是因為不同層級的答案本來就不一樣,卻被混在一起討論。先用一張表整理全貌。
| 層級 | 目前的狀態 | 實務上的意義 |
|---|---|---|
| 作業系統功能(Windows 的選用功能) | 存續。隨附於現行的 Windows 用戶端/Server,也尚未發布淘汰宣告12 | 只要啟用就能繼續運作。在作業系統的支援期間內可以持續使用 |
Win32 原生 API(MQSendMessage 等) |
已文件化,可以使用9 | 透過 P/Invoke 從 .NET 呼叫的路仍然存在。但格式器與交易整合必須自行撰寫並維護 |
| .NET Framework 的 System.Messaging | 可以使用。但適用範圍僅限 .NET Framework 1.1〜4.8.13 | 既有系統就是運作在這一層上。往後沒有延伸的路 |
| .NET(Core 以後)的官方受管理 API | 不存在。也不包含在 Windows 相容套件中4 | 一旦把應用程式升級到 .NET,就必須重做佇列部分 |
| WCF 的 MSMQ 繫結 → CoreWCF.MSMQ | 存在社群主導的移植版本。CoreWCF 本身有支援政策,但 MSMQ 實作依賴 System.Messaging 的社群移植版本5 | 可用於延命透過佇列呼叫的 WCF 服務(接收端),但既非傳送端的替代方案,也不是通用的佇列 API |
| 新專案採用 | 不建議 | 因為沒有官方的未來路徑 |
以下依序確認此表格各列的根據。
第一,MSMQ 並未列在淘汰清單中。查看 Windows 用戶端的「Deprecated features」清單,可以看到 NTLM、VBScript、WordPad 都在其中,但沒有 MSMQ 的項目。1 在 Windows Server 端的「Features Removed or No Longer Developed」清單中,包含 Windows Server 2025 分頁在內,也同樣沒有列出 MSMQ。2 淘汰(deprecated)是「停止積極開發」這種官方宣告,而目前的現況是,連這個宣告都還沒有發布。
第二,System.Messaging 停留在 .NET Framework 上不再前進。參考文件所涵蓋的版本從 .NET Framework 1.1 到 4.8.1 為止,並不存在 .NET(Core 以後)的版本。3 作為 .NET Framework 專用 API 承接管道的 Windows 相容套件(Microsoft.Windows.Compatibility),雖然提供了登錄、WMI、Windows 服務、EventLog 等約兩萬個 API,但其技術領域清單中並不包含訊息傳遞(System.Messaging)。4 正確地說,被封閉的是受管理 API。MSMQ 的 Win32 原生 API(MQSendMessage、MQReceiveMessage 等)目前仍然有文件記載,從 .NET 透過 P/Invoke 呼叫在技術上是可行的。不過,原本由 System.Messaging 吸收的格式器與交易整合,都必須自行撰寫成包裝層並持續維護,因此它的定位不是「值得長期依賴的正解」,而是「無論如何都要保留時的延命手段」。
第三,WCF 的 MSMQ 整合也處在同一道牆之內。.NET Framework 的 WCF 有以 MSMQ 為底層的繫結,但若想在現代 .NET 上重現這條路徑,最終會走到社群主導的 CoreWCF。CoreWCF 作為佇列系傳輸的一環,發布了支援 MSMQ 的套件(CoreWCF.MSMQ),但其實作明確標示依賴於 .NET Framework 版 System.Messaging 函式庫的社群移植版本。5 若只論能不能動,是能動的,而且 CoreWCF 本身備有 Microsoft 官方的支援政策,所以也不能說它「完全是野生的函式庫」。5 不過,MSMQ 傳輸所依賴的 System.Messaging 社群移植版本,是否享有同樣的待遇則是另一回事。若要採用,請先確認所使用的版本是否在支援政策的涵蓋範圍內,以及這個依賴部分的處理方式,再做判斷。
以上就是文首那張表格中各列的根據。並不是「MSMQ 已經廢除」,而是「從現代 .NET 通往 MSMQ 沒有官方的路徑」──掌握這個區別,公司內部的討論才會對得上焦點。
4. 「明明還在動,卻是問題」的真相
涉及 MSMQ 的系統諮詢,大多不是從故障開始,而是從遷移的估算開始。問題的真正核心並不是 MSMQ 本身,而是 MSMQ 成了把整個應用程式綁在 .NET Framework 上的錨。
.NET Framework 4.8 被視為 Windows 的元件,依所安裝作業系統的生命週期提供支援。6 所以對於「還會不會繼續動」這個問題,可以回答「短期內會持續運作」。即便如此,以下幾點會隨著時間確實變得愈來愈沉重。
- 招不到人、也交接不了。能夠解說 System.Messaging 與 DTC 的技術人員逐年減少。這正是「如何承接沒有原始碼、沒有文件的系統維護工作」一文中所描述情況的典型案例。
- 享受不到執行環境與新函式庫的紅利。許多新版 C# 語法只要更新編譯器,在 .NET Framework 上也能使用,但執行環境端的效能改善與新的標準函式庫都用不到,而且愈來愈多近期的套件已將 .NET Framework 排除在支援對象之外,能選用的套件也逐漸變少。
- 分散式交易會成為遷移中最大的難關。MSMQ+DTC 那種「讓佇列取出與資料庫更新具備原子性」的設計,在雲端佇列服務上無法重現。若放著這部分不管、只把周邊部分改成 .NET 化,最後會留下最難啃的核心。
- 序列化格式的定時炸彈。舊系統的訊息內文有時採用二進位序列化,而 BinaryFormatter 已在 .NET 9 中從執行環境移除。7 遷移時必須連同訊息格式一併重新檢視。
也就是說,「MSMQ 能用到什麼時候」這個問題的實務答案是:「只要作業系統支援就能繼續運作,但遷移的難度會隨著拖延而確實升高」。即使決定把判斷往後延,至少也應該先透過下一章的判斷表掌握「難點在哪裡」。
5. 遷移目標判斷表
選擇遷移目標時,不是去問「MSMQ 的後繼產品是哪一個」,而是依實際使用到的特性來挑選。
5.1. 先用四個問題釐清需求
- 佇列的對象(接收端的處理)是不是更新自家資料庫的處理?
- 是否使用分散式交易(DTC)?(交易佇列 + 資料庫更新)
- 傳送端與接收端是不是不同機器、不同據點?離線韌性(store-and-forward)是不是真的有在使用?
- 訊息量實際上大約多少?(多數業務系統每天是數千到數萬筆,無論選哪個方案都綽綽有餘)
5.2. 判斷表
| 使用到的特性 | 首選方案 | 理由與注意事項 |
|---|---|---|
| 同一系統內的非同步處理(接收端更新資料庫) | 資料庫的資料表佇列 | 可以在同一個本機交易中確定「取出訊息」與「更新業務資料」,不再需要 DTC。既有的備份、監控、維運可以直接沿用。此外,傳送端也可以把「更新業務資料」與「新增訊息資料列」放在同一個交易中,這就是所謂的 Outbox 模式 |
| 用 DTC 讓佇列與資料庫更新具備原子性 | 資料庫的資料表佇列 | 遷移的本質在於把分散式交易替換成本機交易。雲端佇列無法參與 DTC,只要還有這項需求,選擇訊息代理服務就是繞遠路 |
| 多個系統・多種語言之間的鬆散耦合串接 | RabbitMQ(地端) / Azure Service Bus(可用雲端) | 傳遞的扇出與路由是訊息代理服務的強項。採用 RabbitMQ 時,須估算自行維運的負擔(容錯備援、更新) |
| 據點之間的離線韌性(store-and-forward) | Azure Service Bus 等 + 重送設計,或據點端資料表佇列 + 同步 | 能夠透明地取代 MSMQ「先在傳送端本機儲存、之後再送達」這種機制的方案很少。應改為明確地將在傳送端儲存的責任交由應用程式端來承擔的設計 |
| 同一處理程序內的生產者/消費者 | .NET 的 Channels 等記憶體內佇列 | 這種情況原本就不需要跨處理程序的佇列。應在處理程序內完結,若需要持久性再改用資料表佇列 |
| 只用作 Windows 服務之間的行程間通訊 | 具名管道等 IPC | 但只有在雙方隨時同時運作的情況下才能替換。即使接收端停止,MSMQ(即便是 express 訊息)仍會接受傳送並之後送達,而管道則會立刻失敗。若依賴的是停止期間的累積,就要在應用程式端自行實作重送・緩衝,或是保留佇列。選型方式請參考「Windows 的 IPC 判斷表」 |
5.3. 資料表佇列的最小實作
很多人聽到「資料表佇列」還是沒有具體概念,這裡展示最小的形式。首先是資料表定義(SQL Server)。
CREATE TABLE dbo.JobQueue (
Id BIGINT IDENTITY(1,1) PRIMARY KEY,
Payload NVARCHAR(MAX) NOT NULL, -- 內文,以 JSON 形式保存
Status TINYINT NOT NULL DEFAULT 0, -- 0:未處理 1:處理中 2:完成
EnqueuedAt DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME(),
StartedAt DATETIME2 NULL, -- 用於回收「處理中」狀態下當掉的資料列
RetryCount INT NOT NULL DEFAULT 0
);
CREATE INDEX IX_JobQueue_Status ON dbo.JobQueue (Status, Id);
取出時,一次只會替一筆資料標記為「處理中」,同時取得其內容。加上 READPAST 之後,可以跳過其他工作處理程序鎖定的資料列而不必等待(可以讓多個處理程序同時運作)。
WITH next_job AS (
SELECT TOP (1) *
-- READCOMMITTEDLOCK 在 READ_COMMITTED_SNAPSHOT 為 ON 的資料庫中是必要的(詳見後文)。
-- 在 OFF 的資料庫中,行為與預設相同,因此保持加上也能同時適用於兩種情況
FROM dbo.JobQueue WITH (READPAST, UPDLOCK, READCOMMITTEDLOCK)
WHERE Status = 0
ORDER BY Id -- 依投入順序取出。這是成為「佇列」的必要條件
)
UPDATE next_job
SET Status = 1, StartedAt = SYSUTCDATETIME()
OUTPUT inserted.Id, inserted.Payload;
在 READ_COMMITTED_SNAPSHOT 為 ON 的資料庫中,READPAST 沒辦法直接使用。官方文件明確指出:「當 READ_COMMITTED_SNAPSHOT 為 ON,且工作階段的隔離等級為 READ COMMITTED(或查詢中同時使用了 READCOMMITTED 提示)時,無法指定 READPAST」,並提出對策是同時加上 READCOMMITTEDLOCK 提示。10 由於預設的隔離等級就是 READ COMMITTED,在只是啟用了 READ_COMMITTED_SNAPSHOT 的資料庫上,這段取出查詢別說是「跳過其他工作處理程序的資料列」了,連陳述式本身都會出錯。Azure SQL Database 預設就是 ON,而在地端環境中,為了防止讀取阻塞而啟用它的環境也不少見。請先確認遷移目標的資料庫是哪一種情況。
SELECT is_read_committed_snapshot_on FROM sys.databases WHERE name = DB_NAME();
之所以從常見寫法 WITH (READPAST, UPDLOCK, ROWLOCK) 中拿掉了 ROWLOCK,是因為ROWLOCK 與 READCOMMITTEDLOCK 屬於同一個「粒度提示」群組,一張資料表無法同時指定兩者。10 READPAST 能跳過的只有資料列鎖定,無法跳過分頁鎖定,所以本來會想明確指定 ROWLOCK,但對於透過 TOP (1) 索引搜尋所取得的這一筆資料列而言,實務上幾乎不會發生鎖定升級。若已確定 READ_COMMITTED_SNAPSHOT 為 OFF,而想明確指定 ROWLOCK,請把 READCOMMITTEDLOCK 拿掉。
若省略 ORDER BY、只寫成 UPDATE TOP (1),會選中哪一筆資料列就是不確定的。官方明確指出 UPDATE 的 TOP 不會對目標資料列排序,即使有 (Status, Id) 索引,也不能保證取出的順序。11 在以「依投入順序處理」為前提的串接中,會出現舊工作被持續延後處理的問題。
不過,ORDER BY Id 所確保的只有取出的順序。處理完成的順序並不會一致。READPAST 是用來「跳過其他工作處理程序所鎖定的資料列」的指定,因此當工作處理程序 A 取得工作 1 並花費較長時間處理時,工作處理程序 B 有可能取得工作 2 並先行提交。
| 會保持一致的 | 不會保持一致的 |
|---|---|
哪一筆資料列先被取出(ORDER BY Id) |
哪一筆資料列先被反映到業務資料 |
像「對同一個訂單編號的更新順序若對調就會出問題」這種反映順序具有意義的串接,光靠這樣還不夠。可以採取以下兩種做法之一:
- 把消費者限定為單一個。捨棄並行度,換取順序保證。若吞吐量足夠,這是最單純、也最不容易出錯的形式
- 依鍵值分開處理。以訂單編號等鍵值固定對應的工作處理程序,或是在取出端加上「若相同鍵值的資料列正在處理中則不取出」的條件,只在同一個鍵值內保證順序。跨鍵值的順序則放棄保證
若兩者都不採用,就必須在遷移設計中明確寫下:一旦以並行方式運作,整體就沒有 FIFO 保證。從 MSMQ 遷移過來時,這一點特別容易被忽略。MSMQ 的佇列若以並行方式接收,同樣會發生一樣的情況,但若帶著「因為是佇列所以會按順序」這種理解遷移過來,就會看起來像是「一改成資料表佇列,順序問題就冒出來了」。
接收端的工作處理程序,只需要把這段邏輯放在與業務處理相同的本機交易中執行即可(C#,虛擬碼)。
while (!stoppingToken.IsCancellationRequested)
{
using var tx = connection.BeginTransaction();
var job = DequeueOne(connection, tx); // 對應上方的 UPDATE ... OUTPUT
if (job is null)
{
tx.Commit();
await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); // 若為空,則只等待輪詢間隔
continue;
}
ApplyBusinessData(connection, tx, job); // ← 更新業務資料(原本真正想做的工作)
MarkDone(connection, tx, job.Id); // Status = 2
tx.Commit(); // 「取出」與「業務更新」在這一行同時確定
}
這就是取代 MSMQ+DTC 的關鍵所在。在 MSMQ 中,「從佇列取出」與「更新資料庫」是不同的資源,因此需要分散式交易(DTC)才能把兩者整合在一起。若佇列本身就是資料庫的資料表,兩者都是對同一個資料庫的更新,只需要普通的本機交易就足夠了。一旦導入 RabbitMQ 或 Azure Service Bus,這項特性就會消失(佇列與資料庫又變回不同的資源),冪等性與重送就必須由應用程式端自行實作。
在實際維運中,大致需要再補上以下三點。
- 回收「處理中」狀態下當掉的資料列。將
Status = 1且StartedAt早於某個時間門檻的資料列,加入定期執行的復原處理,把它們的狀態改回Status = 0。 - 重試上限與退避。
RetryCount超過上限的資料列,移到另一張資料表(或標記為Status = 9)。這相當於 MSMQ 配送失敗佇列的存放位置。 - 清理已完成的資料列。定期刪除或封存
Status = 2的資料列。放著不管會讓資料表變得肥大,索引效率也會下降。
在傳送端,把業務資料的更新與訊息資料列的 INSERT 放進同一個交易中(Outbox 模式)。這樣一來,「業務資料已經更新,但訊息卻沒有送出」這種不一致的情況也會消失。
想強調的重點是,在中小規模的業務系統中,資料表佇列往往會成為首選。訊息佇列產品的比較文章常常會變成「RabbitMQ vs Kafka vs Service Bus」這種形式,但 MSMQ 世代的系統放在佇列上的,多半是「更新同一個資料庫的非同步工作」,而這用資料庫的資料表加上輪詢(或通知)就已足夠,而且能用更簡單的方式實現。多增加一套新的中介軟體所帶來的維運成本(監控、容錯備援、修補、人員教育),對中小型的組織體制來說格外沉重。
6. 遷移的實務步驟
進行方式依循遺留系統遷移的定石,也就是「先觀測、再動手」。
-
盤點依賴。從程式碼中找出所有對
System.Messaging的參照,以及建立MessageQueue的地方。同時也請搜尋 WCF 組態檔中的netMsmqBinding/msmqIntegrationBinding、原生 API(MQSendMessage等MQ*函式)以及透過 COM 的呼叫。即使沒有參照 System.Messaging,也有可能存在依賴 MSMQ 的路徑。要確認的觀點包括:(a) 佇列的路徑(本機或遠端、私人或公用)、(b) 是否為交易佇列、訊息是否設為 Recoverable(若兩者皆非,代表現行系統是以「重新啟動就會遺失訊息」為前提在運作)、(c) 格式器(XmlMessageFormatter / BinaryMessageFormatter / ActiveXMessageFormatter)、(d) 日誌佇列・配送失敗佇列的使用情況、(e) 由誰負責建立與刪除(安裝程式或應用程式本身)。盤點若只是確認過一遍,對遷移判斷並不會產生實際效果。請依照下列格式,每一條佇列填一列,直接作為遷移方針的依據(可以原封不動當作簽核文件的附件)。
觀點 要看哪裡 記錄的內容 對判斷的影響 (a) 佇列的路徑 傳給 MessageQueue的路徑字串、組態檔中的連線目標、FormatName:指定本機/遠端之別、私人/公用之別、對方的機器名稱 若為遠端・跨據點,判斷是否需要 store-and-forward(對應 5.2 表格中「離線韌性」那一列)。若在本機內完結,則歸入 5.2 表格的第一列 (b) 交易與 Recoverable 建立佇列的地方(是否為交易佇列)、傳送時是否指定 Recoverable、是否使用 DTC 是否為交易佇列、是否指定 Recoverable、是否參與 DTC 若有使用 DTC,遷移目標幾乎可以確定為資料表佇列。若兩者皆無,則須明確記載現行系統是以「重新啟動就會遺失訊息」為前提在運作 (c) 格式器 XmlMessageFormatter/BinaryMessageFormatter/ActiveXMessageFormatter的指定位置使用中的格式器,以及訊息本體的型別 若為 BinaryFormatter 系列,必須替換為 JSON(步驟 2)。這是遷移工時的主要因素7 (d) 日誌・配送失敗佇列 佇列的內容值(日誌是否啟用)、系統佇列的內容、運維手冊 是否使用日誌佇列、是否有實際查看配送失敗佇列 「如何處理失敗訊息」會成為遷移目標的設計需求(資料表佇列的話就是退避資料表) (e) 由誰建立・刪除 安裝程式、部署指令碼、應用程式啟動時呼叫的 Create建立的主體,以及由誰設定權限 遷移時「由誰準備新佇列」會直接對應過去。若選擇擱置不動,則轉記到裝機程序中(第 7 章) - 決定訊息格式。遷移後的格式以 JSON 為預設。不能帶進新系統的是像 BinaryMessageFormatter 這樣的BinaryFormatter 系列序列化。BinaryFormatter 已在 .NET 9 中移除,基於安全性考量也不建議使用。7 另一方面,若使用的是像 Protocol Buffers 或 MessagePack 這種規格獨立維護的二進位格式,把該格式原封不動搬到新系統本身並無問題。
- 從接收端開始遷移。先準備好新的佇列(例如資料表佇列),讓接收端的處理支援新佇列之後,再切換傳送端。在遷移期間,若在中間夾入一個把訊息從舊 MSMQ 搬到新佇列的小型橋接程式(這部分維持 .NET Framework 即可),就不必一次性切換所有傳送端。不過,若橋接程式在「從 MSMQ 取出」與「寫入新佇列」之間當掉,就會遺失訊息或造成重複流通。若遷移來源是交易佇列,最低條件是讓 MSMQ 端以交易方式接收,寫入失敗時可以復原,並讓新佇列端能以訊息 ID 排除重複(具備冪等性)的設計。若遷移來源是非交易佇列,則無法採用這個做法,因為佇列的交易屬性在建立時就已決定,之後無法變更。這種情況下,應依「用 Peek 讀取 → 以冪等方式寫入新佇列 → 確認寫入成功後才移除」的順序來組裝(即使在移除之前當掉,重複的部分也會由新佇列端的冪等性吸收)。若這兩種做法對於系統規模來說都不划算,不夾橋接程式、直接朝步驟 5 的「清空後切換」靠攏會更安全。
- 先固定行為,再進行改寫。佇列處理是容易出現時序相依錯誤的領域。若在遷移前先準備好「這個輸入訊息會得到這個結果」的特性化測試,替換之後的驗證就能機械化地進行(參見「以特性化測試固定行為後再進行重構」)。
- 在佇列內容清空的狀態下切換。在還有訊息殘留的狀態下切換,是重複處理與遺漏的溫床。透過計畫性停機把佇列處理到清空之後再切換,終究是最安全也最快的方法。
7. 選擇擱置不動時的最低條件
「沒有把應用程式升級到 .NET 的計畫,從成本效益來看短期內也不遷移」這種判斷,在有條件的前提下是合理的。以下用可以直接轉記到簽核・審查資料中的檢查清單形式,列出這種情況下的最低條件。請如此理解:唯有四項全部打勾,「擱置不動」才會成為一個可行選項。
| 確認 | 最低條件 | 具體要做的事 | 若不滿足會發生什麼 |
|---|---|---|---|
| □ | 在裝機・復原程序中明確記載 MSMQ | MSMQ 是 Windows 的選用功能。請在環境建置手冊中寫下「啟用 Windows 功能」或透過 DISM/PowerShell 啟用的步驟 | 更換 PC・伺服器時忘記啟用,導致遷移當天原因不明地無法運作 |
| □ | 監控佇列長度 | 建立滯留筆數的門檻監控與通知機制,並將配送失敗佇列與日誌佇列也納入監控對象 | 即使接收端停止也不會報錯,只會持續累積,導致沒有人察覺,業務就這樣停擺 |
| □ | 將組態文件化 | 記錄佇列的路徑、權限、是否有交易、格式器、日誌設定(可以直接沿用第 6 章的盤點表) | 未來估算遷移時必須從調查重新開始,精確度與工時都會變差 |
| □ | 每年檢視一次判斷 | 每年確認「是否已列入淘汰清單」「作業系統更新是否改變了行為」 | 等到淘汰公告發布後才慌忙行動 |
第四項特別容易被輕忽,但正因為有這個年度確認,即使公告發布之後也不至於慌張。反過來說,若在無法滿足這四項的環境中選擇擱置不動,就等同於「在看不見的狀態下,靜待某天突然停擺」。
8. 總結
- 截至 2026 年 7 月,MSMQ 並未被官方列為淘汰項目,作為作業系統功能仍然存續。「已經廢除」這種認知並不正確。12
- 另一方面,System.Messaging 仍停留在僅限 .NET Framework 使用的階段,不包含在相容套件中,也沒有通往 .NET(Core 以後)的官方路徑。雖然 CoreWCF 本身有支援政策,但其 MSMQ 傳輸依賴 System.Messaging 的社群移植版本。345
- 判斷的軸心在於「是否要把應用程式升級到 .NET」。若要升級,原則上佇列也應一併遷移(以 P/Invoke 等方式延命,等於是用維護負擔去交換);若選擇擱置不動,則以監控與文件化作為成立的條件。6
- 遷移目標的首選是資料庫的資料表佇列。MSMQ+DTC 的一致性,本質上應以本機交易來取代,訊息代理產品的導入則放在其後再考慮。
- BinaryFormatter 系列的序列化,由於在 .NET 9 中已被移除,無法帶入新系統。遷移時應以 JSON 為預設,但若是像 protobuf 這種獨立維護的格式,直接沿用也無妨。7
- 依「接收端 → 橋接程式 → 傳送端」的順序切換,並先用特性化測試固定行為之後再改寫,是不會出事故的進行方式。
相關文章
- VB6 應用程式能用到什麼時候 ── 執行環境的支援現況與務實的 .NET 遷移做法
- 將 .NET Framework 遷移到 .NET 之前該確認的事 - 在著手前就決定勝負的實戰檢核表
- 為沒有測試的遺留業務應用程式安全地動手修改 ── 特性化測試與重構的實踐
- Windows 的行程間通訊該怎麼選 ── 具名管道 / TCP / gRPC / 共享記憶體 / COM 判斷表
- TCP 中「Send 單位=Receive 單位」的誤解 ── 把 TCP 視為位元組串流來設計接收邏輯
- 接手了沒有原始碼也沒有規格書的系統 ── 不停機維運的實務步驟
- 為業務應用程式的 DB 結構做版本管理 ── 防止「各客戶端 DB 不一致」的遷移實踐
相關諮詢領域
合同會社小村軟體承接包含 MSMQ 在內的遺留架構盤點與遷移計畫、從 .NET Framework 遷移到 .NET,以及包含佇列處理在內的業務系統改造。
參考連結
-
Microsoft Learn, Deprecated features in the Windows client。Windows 用戶端中已停止積極開發(已淘汰)功能的官方清單。關於截至 2026 年 7 月的清單中列有 NTLM、VBScript、WordPad 等項目,但沒有列出 MSMQ(Microsoft Message Queuing);以及淘汰(deprecated)是指「不再積極開發,未來更新中可能會被移除」的階段,與移除(removed)有所區別。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Features Removed or No Longer Developed in Windows Server。Windows Server 中已移除功能與停止開發(已淘汰)功能的官方清單。關於包含 Windows Server 2025 分頁在內、截至 2026 年 7 月的清單中並未列出 MSMQ;以及已淘汰的元件仍會持續隨附於 Windows Server,依產品生命週期為正式環境提供支援,並持續獲得安全性更新與品質更新。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MessageQueue Class (System.Messaging)。關於 System.Messaging.MessageQueue 類別參考文件所涵蓋的版本為 .NET Framework 1.1 到 4.8.1,並不存在 .NET(Core 以後)版本。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Use the Windows Compatibility Pack to port code to .NET。關於 Windows 相容套件(Microsoft.Windows.Compatibility 套件)為了在遷移到 .NET 時補足對 .NET Framework 專用 API 的依賴,提供了約兩萬個 API;以及其技術領域清單(CodeDom、組態、Directory Services、Drawing、ODBC、ACL、WCF、登錄、WMI、效能計數器、Windows 服務、EventLog 等)並未包含訊息傳遞(System.Messaging)。 ↩ ↩2 ↩3 ↩4
-
CoreWCF project, CoreWCF.MSMQ (NuGet) 以及 CoreWCF 1.4.0 Preview release、Microsoft, CoreWCF Support Policy。關於 CoreWCF 是將 WCF 伺服器端移植到 .NET 的社群主導專案,Microsoft 為其提供官方支援政策;作為佇列系傳輸的一環,已發布支援 MSMQ 的套件(CoreWCF.MSMQ);以及該 MSMQ 實作依賴 .NET Framework 版 System.Messaging 函式庫的社群移植版本。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, .NET Framework official support policy。關於 .NET Framework 4.8 被定義為 Windows 作業系統的元件,依所安裝之母產品(作業系統)的生命週期政策提供支援。 ↩ ↩2 ↩3
-
Microsoft Learn, BinaryFormatter migration guide。關於 BinaryFormatter 基於安全性理由而逐步淘汰,自 .NET 9 起已從執行環境中移除實作、預設無法使用;以及官方建議遷移到 JSON(System.Text.Json)等安全的序列化格式。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn(封存版), Express and Recoverable Messaging、System-Generated Queues、Message Queuing (MSMQ)。關於 express 訊息在傳遞中與傳遞後都保留在 RAM 上,一旦訊息所在的電腦或 MSMQ 服務停止就會遺失;recoverable 訊息會在傳送端與中繼的各台電腦上寫入磁碟,並在目的佇列中同樣保留在磁碟上;公用佇列(public queue)會登錄到目錄服務,而私人佇列(private queue)只會登錄在本機電腦上、不會公開到目錄服務;以及保存已取出訊息與已傳送訊息副本的日誌佇列(journal queue),與保存無法送達訊息的配送失敗(deadletter)佇列,都是 MSMQ 自動產生的系統佇列。 ↩
-
Microsoft Learn(封存版), Message Queuing Functions 以及 MQSendMessage、MQReceiveMessage。關於 MSMQ 的 Win32 原生 API(MQCreateQueue、MQSendMessage、MQReceiveMessage 等)已針對 C/C++ 應用程式提供文件,可以不經由受管理 API 就進行佇列的建立、傳送與接收。 ↩
-
Microsoft Learn, 資料表提示 (Transact-SQL)。關於
READPAST是一種跳過而不讀取其他交易鎖定之資料列的提示,可跳過的僅限資料列層級的鎖定,無法跳過分頁層級的鎖定,且只能在READ COMMITTED或REPEATABLE READ隔離等級下指定。特別是以下這段敘述:「當READ_COMMITTED_SNAPSHOT資料庫選項設為ON,且 (a) 工作階段的交易隔離等級為READ COMMITTED,或 (b) 查詢中也指定了READCOMMITTED資料表提示,兩者之一成立時,就無法指定READPAST資料表提示。若要在這些情況下指定READPAST提示,須移除查詢中的READCOMMITTED資料表提示(如果有的話),並在查詢中加入READCOMMITTEDLOCK資料表提示」。另請參閱同一頁面中關於READCOMMITTEDLOCK是不論READ_COMMITTED_SNAPSHOT設定為何,都以鎖定方式強制執行READ COMMITTED的提示,以及對同一資料表無法指定兩個以上粒度提示(PAGLOCK/NOLOCK/READCOMMITTEDLOCK/ROWLOCK/TABLOCK/TABLOCKX)的說明。資料庫端的設定可以透過 sys.databases 的is_read_committed_snapshot_on確認。 ↩ ↩2 -
Microsoft Learn, TOP (Transact-SQL)。關於在 INSERT、UPDATE、MERGE、DELETE 中使用 TOP 時,被參照的資料列並不會依任何順序排列;若要決定順序,應使用同時具有 TOP 與 ORDER BY 的子查詢。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
VB6 應用程式能用到什麼時候 ── 執行環境的支援現況與務實的 .NET 遷移做法
VB6 應用程式究竟能用到什麼時候?本文整理 VB6 執行環境的支援政策(Windows 11 也在支援範圍內)與 IDE 支援早已終止這種不對稱現況,並以實務指南的形式,說明全面重寫、自動轉換、階段性遷移的判斷表、遷移前的資產盤點、VB6 與 .NET 的不相容之處,以及...
多執行緒的實務最佳實踐 .NET篇 ── 在增加執行緒之前先決定的事
針對 .NET/C# 整理能防止「建立執行緒後偶爾當機、卡死」的設計定石。內容涵蓋不自行建立執行緒而改用 Task、減少共享可變狀態、鎖的紀律、以 CancellationToken 設計停止機制,以及 UI 執行緒的處理方式。
在 C#・PowerShell 中使用 WMI/CIM ── 硬體資訊取得・處理程序監控・遠端查詢的實務指南
取得 PC 序號、監控磁碟可用空間、偵測處理程序啟動,這些定番需求的標準答案就是 WMI/CIM。本文解說 Get-CimInstance 等 CIM Cmdlet 的用法、從舊版 Get-WmiObject 的遷移方式、C# 的 System.Management 與 C...
Windows I/O 的深層(第 4 回) ── 快取管理員:你的 WriteFile 究竟何時送達磁碟
本文是透過圖解說明 Windows 快取管理員的系列第 4 回。整理了以檔案映射方式實作的快取、預先讀取與延遲寫入、FlushFileBuffers 與 FILE_FLAG_NO_BUFFERING 的區分使用,直到斷電導致資料消失的條件。
Windows I/O 的深層(第 3 回) ── I/O 完成埠(IOCP)與 .NET 執行緒集區:async/await 的地下室
本文是以圖解說明 I/O 完成埠(IOCP)的系列文章第 3 回。將完成佇列與執行緒數控制合而為一的設計、並行值與 LIFO 釋放、.NET 執行緒集區,直到 async/await 接續實際執行的執行緒為止,逐一整理。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
ActiveX 遷移
整理保留、包裝或替換 COM / ActiveX / OCX 資產的階段性判斷的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
Windows 軟體維護 & 現代化
支援既有 Windows 軟體的階段性升級、功能追加、64 位元就緒以及可維護性的重構。
常見問題
整理諮詢這個主題時常見的問題。
- MSMQ 已經被廢除了嗎?
- 沒有。截至 2026 年 7 月,無論是 Windows 用戶端的已淘汰功能清單,還是 Windows Server 的「已移除功能・停止開發功能」清單,都沒有列出 MSMQ。它仍以 Windows 的選用功能形式,隨附於現行的作業系統中。「已經廢除」這種說法之所以流傳開來,是因為與 .NET 端的狀況混為一談。處理 MSMQ 的標準類別庫 System.Messaging 只存在於 .NET Framework,並未移植到 .NET(Core 以後)。也就是說,正確的狀態是「作為作業系統功能仍然存續,但沒有從現代 .NET 使用它的官方途徑」。
- 有沒有辦法從 .NET 8 或 .NET 10 使用 MSMQ?
- 沒有官方的受管理 API。System.Messaging 是到 .NET Framework 4.8.1 為止的 API,也不包含在 Windows 相容套件(Microsoft.Windows.Compatibility)中。透過 P/Invoke 呼叫 MSMQ 的原生 Win32 API(如 MQSendMessage)在技術上是可行的,但這代表要自行撰寫並維護包含格式器與交易整合在內的包裝層。社群方面,CoreWCF 專案發布了 MSMQ 傳輸用的套件(CoreWCF.MSMQ),但這是用來在現代 .NET 上代管透過佇列呼叫的 WCF 服務(WCF 伺服器端的移植),並不是可以取代 System.Messaging 的通用佇列 API,也不能替代傳送端的用戶端。其實作本身也依賴 .NET Framework 版 System.Messaging 的社群移植版本。可以作為驗證或暫時延命的選項。CoreWCF 本身有 Microsoft 官方的支援政策,但這個 MSMQ 實作所依賴的 System.Messaging 社群移植版本並不享有同樣的保證,若要將其作為業務系統的前提,應先確認所使用的版本是否在支援政策範圍內,以及依賴部分的處理方式,再做判斷。實務上,正確的做法是在把應用程式升級到 .NET 的同時,一併遷移佇列。
- 遷移目標選 RabbitMQ 還是 Azure Service Bus 比較好?
- 在這兩個選項之前,請先考慮把資料庫的資料表當作佇列使用的方案。使用 MSMQ 的中小規模業務系統,多數情況下佇列的對象都是更新自家資料庫的處理。這種情況下,資料表佇列可以在與業務資料相同的本機交易中,同時確定「取出訊息」與「更新業務資料」,用更簡單的機制取代原本由 MSMQ+分散式交易(DTC)所實現的一致性。若資料表佇列無法滿足需求,例如需要在多個系統之間進行鬆散耦合的傳遞,或需要高吞吐量,再依序考慮:若地端(on-premises)需求較強就選 RabbitMQ,若可以放在雲端就選 Azure Service Bus,依這個順序判斷較不容易失敗。
- 當下先維持 .NET Framework、擱置不動,這樣的判斷可以嗎?
- 有條件地可以。.NET Framework 4.8 作為 Windows 的元件,會依照作業系統的生命週期受到支援,而 MSMQ 本身也尚未淘汰,因此可以預期「會持續運作」。不過,若選擇擱置不動,至少要做到以下四點:在裝機程序中明確記載啟用 MSMQ 功能的步驟、建立佇列長度與日誌(journal)的監控機制、將訊息格式與連線組態文件化、為負責人異動預留回復程序。擱置不動真正的風險不在技術本身,而在於「變得沒有人能夠處理」。