Сколько ещё можно использовать MSMQ? ── Решение о миграции с legacy-очереди, которую «даже не объявили устаревшей»

· Обновлено: · · Windows, .NET, C#, MSMQ, Очереди сообщений, Унаследованные технологии, Миграция, Информационные системы

«Я слышал, что MSMQ упразднили. Неужели систему, которая работает прямо сейчас, тоже нужно срочно переносить?» — при сопровождении существующей системы начинать нужно именно с этого разграничения.

Существование MSMQ (Microsoft Message Queuing) как функции ОС и наличие API для работы с ним из .NET — это два разных вопроса. На июль 2026 года, то есть на момент, принятый в этой статье за точку отсчёта, MSMQ не входит в официальные списки устаревших функций и остаётся дополнительным компонентом Windows. А вот стандартная библиотека System.Messaging существует только в .NET Framework и в .NET (Core и более поздние версии) перенесена не была.123

Значит, проблема не в том, что «завтра MSMQ остановится», а в том, что при переводе приложения на .NET реализация очереди становится препятствием для миграции.

Эта статья адресована разработчикам, которые сопровождают и переносят системы на MSMQ, и отделам информационных систем, которые определяют политику. Сначала мы разберём факты и условия продолжения эксплуатации, затем перейдём к инвентаризации, выбору цели миграции, реализации табличной очереди и процедуре переключения.

Общая миграция с VB6 или с .NET Framework в целом описана в статьях «Практика миграции с VB6 на .NET» и «Чек-лист перед миграцией с .NET Framework на .NET». Здесь речь пойдёт только о части, связанной с очередью.

1. Сначала вывод: разделяем сохранение ОС и миграцию приложения

Если вы переводите приложение на .NET, очередь переносят вместе с ним — это правило. Если пока работа продолжается на .NET Framework, можно выбрать продолжение использования, проверив срок поддержки ОС и условия эксплуатации. Принимать MSMQ в новом проекте не рекомендуется.4

Читайте с того, что нужно решить сейчас

Что нужно решить сейчас Отправная точка решения Где это изложено
Правда ли, что технологию «упразднили» Разделить состояние функции ОС и состояние управляемого API Глава 1: факты
Можно ли пока продолжать использовать как есть Проверить срок поддержки ОС и то, можно ли наладить мониторинг, восстановление и передачу ответственности Глава 2: условия продолжения использования
Хотим заменить вместе с миграцией на .NET Провести инвентаризацию зависимостей, форматов, DTC и устойчивости к работе офлайн Глава 3: инвентаризация, Глава 4: цели миграции
Можно ли сохранить через P/Invoke или CoreWCF Разделить возможность вызвать API и готовность нести расходы на сопровождение Раздел 1.3: управляемый API и P/Invoke, Раздел 1.4: CoreWCF
RabbitMQ или Azure Service Bus Прежде чем делать этот выбор, проверить, достаточно ли табличной очереди в той же рабочей БД Глава 4: выбор по требованиям, Глава 5: табличная очередь
Хотим перенести SQL извлечения в свою БД Проверить уровень изоляции, настройку снимка и подсказки блокировок Раздел 5.3: SQL и настройки БД
Нужно сохранить порядок обработки очереди Разделить порядок извлечения и порядок применения к бизнес-данным Раздел 5.4: параллельная обработка и порядок
Хотим вывести воркер в эксплуатацию Проверить границу транзакции, а также возврат в работу, повторные попытки и очистку Раздел 5.5: воркер, Раздел 5.6: эксплуатация
Как переключить работающую систему Определить формат сообщений, принимающую сторону, обработку дублей и судьбу оставшихся сообщений Глава 6: процедура переключения

Если читать подряд, порядок такой: подтверждаем, что сохранилось → условия продолжения использования → инвентаризация → цель миграции → реализация → переключение. Когда понадобится вернуться к решению, смотрите итоги в главе 7.

1.1. Уточните, о каком уровне идёт речь

Ответ на вопрос «можно ли ещё использовать MSMQ» зависит от уровня, как показано ниже. Точка отсчёта для этих состояний — тот же июль 2026 года, что и в начале статьи.

Уровень Текущее состояние Что это значит на практике
Функция ОС (дополнительный компонент Windows) Сохраняется. Входит в состав актуальных выпусков клиента Windows и Windows Server, объявления об устаревании нет12 Если включить, он работает и сейчас. Пока ОС поддерживается, использовать его можно
Нативный Win32 API (MQSendMessage и подобные) Документирован и доступен5 Путь вызова из .NET через P/Invoke остаётся. Но форматтеры и интеграцию с транзакциями придётся написать и сопровождать самостоятельно
System.Messaging в .NET Framework Доступен, но только для .NET Framework 1.1–4.8.13 Именно здесь работают существующие системы. Дальше пути нет
Официальный управляемый API в .NET (начиная с Core) Не существует. В пакет совместимости Windows он тоже не входит6 В тот момент, когда приложение переходит на .NET, часть, связанную с очередью, приходится переписывать
Привязка MSMQ в WCF → CoreWCF.MSMQ Есть перенос силами сообщества. У самого CoreWCF есть политика поддержки, но реализация MSMQ зависит от community-порта System.Messaging7 Годится для продления жизни сервиса WCF, вызываемого через очередь (принимающая сторона), но это не замена отправляющей стороне и не универсальный API очередей
Новое внедрение Не рекомендуется Потому что официального пути в будущее нет

1.2. Устаревшей не объявлена именно функция ОС

В списке «Deprecated features» клиента Windows есть NTLM, VBScript, WordPad и другие пункты, но пункта про MSMQ там нет. То же самое относится к «Features Removed or No Longer Developed» для Windows Server, включая вкладку Windows Server 2025.12

Устаревание (deprecated) — это официальный статус, означающий, что активная разработка прекращена и компонент может быть удалён в будущем обновлении. Он отличается от удаления (removed). Что касается MSMQ, по итогам проверки в этой статье даже такого объявления об устаревании не выпущено.

1.3. Миграции на .NET мешает управляемый API

Справочник по System.Messaging.MessageQueue охватывает .NET Framework 1.1–4.8.1, версии для .NET (Core и более поздних) не существует. Пакет совместимости Windows (Microsoft.Windows.Compatibility) предоставляет около 20 000 API — реестр, WMI, службы Windows, EventLog и другие области, — но System.Messaging в него не входит.36

Если оставлять через P/Invoke, придётся взять на себя и сопровождение обёртки

Нативный API тоже никуда не исчез. Вызывать Win32-функции MQSendMessage и MQReceiveMessage из .NET через P/Invoke само по себе возможно. Но форматтеры и интеграцию с транзакциями, которые обеспечивала System.Messaging, придётся написать в виде собственной обёртки и сопровождать её. Это не основной путь миграции, а средство продления жизни MSMQ, когда сохранить его нужно во что бы то ни стало.5

1.4. CoreWCF оценивают как путь для принимающей стороны WCF

В WCF из .NET Framework была привязка, использующая MSMQ. Как путь для её размещения на .NET существует транспорт MSMQ в CoreWCF (CoreWCF.MSMQ).7

Его назначение, однако, — принимающая сторона сервиса WCF, вызываемого через очередь. Это не универсальный API очередей вместо System.Messaging и не замена клиенту отправляющей стороны.

У самого CoreWCF есть политика поддержки Microsoft. Реализация же транспорта MSMQ зависит от community-порта System.Messaging из .NET Framework, и считать, что на эту зависимую часть распространяется та же гарантия, нельзя. Принимайте решение, проверив, попадает ли используемая версия под поддержку и кто будет сопровождать зависимую часть.7

Итак, «MSMQ уже упразднили» и «его нельзя перенести на .NET как есть» — не одно и то же. Выбрав P/Invoke или CoreWCF, вы откладываете замену очереди ценой принятия на себя затрат на сопровождение этого пути.

На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 28, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle

2. Продолжать ли использование: решают план миграции на .NET и организация эксплуатации

2.1. Почему «оно работает» — ещё не основание для решения

.NET Framework 4.8 поддерживается как компонент Windows, в соответствии с жизненным циклом операционной системы, в которой он установлен. Поэтому можно планировать работу в пределах этого срока поддержки.4

Проверить нужно не только то, работает ли система. Если зависимость от MSMQ удерживает всё приложение на .NET Framework, остаются и следующие затраты на сопровождение и миграцию.

Затрата Влияние на решение о миграции
Обеспечение сопровождающих и передача системы Инженеров, способных объяснить System.Messaging и DTC, становится всё меньше, и расходы на повторное изучение конфигурации растут
Ограничения среды выполнения и библиотек Часть нового синтаксиса C# доступна после одного обновления компилятора, но улучшения производительности среды выполнения и новые стандартные библиотеки недоступны. Всё больше пакетов перестают поддерживать .NET Framework как целевую платформу
Зависимость от DTC Если оставить часть, которая фиксирует «извлечение из очереди» и «обновление БД» вместе, модернизируется только периферия, а самое трудное остаётся напоследок
Старые форматы сериализации Реализация BinaryFormatter удалена из среды выполнения в .NET 9, поэтому миграция должна охватить и формат8

Проблема потери сопровождающего рассматривается и в статье «Как сопровождать систему, для которой нет ни исходного кода, ни документации». Обращения по MSMQ чаще начинаются не с разбора сбоя, а с оценки миграции — именно потому, что сложность создают такие зависимости и передача ответственности, а не само поведение системы.

«Работает, пока ОС поддерживается» и «чем дольше ждёшь, тем легче миграция» — это два разных утверждения. Даже если миграция пока не планируется, трудные места стоит оценить заранее.

2.2. Если пока не переносим, выполняем четыре минимальных условия

Отсутствие плана перевода приложения на .NET и сохранение системы без изменений по соображениям затрат и выгоды — так называемая консервация — при определённых условиях разумна. Но предполагается, что выполнены все четыре пункта ниже.

Проверка Минимальное условие Что конкретно сделать Что будет, если не сделать
Явно указать MSMQ в процедурах подготовки окружения и восстановления MSMQ — дополнительный компонент Windows. Опишите в инструкции по подготовке среды шаги включения через «Включение или отключение компонентов Windows» либо через DISM или PowerShell При замене компьютера или сервера про включение забывают, и в день переключения ничего не работает без видимой причины
Следить за длиной очереди Настроить мониторинг порогового значения числа накопившихся сообщений и уведомления. Включить в наблюдение также очередь недоставленных сообщений и очередь журнала Когда принимающая сторона останавливается, ошибка не возникает и сообщения просто копятся, поэтому работа бизнеса встаёт незаметно для всех
Задокументировать конфигурацию Зафиксировать пути очередей, права, наличие транзакций, форматтер и настройки журнала (для этого подходит таблица инвентаризации из раздела 3.2) Будущая оценка миграции начинается заново с обследования, и страдают и её точность, и трудозатраты
Раз в год пересматривать решение Ежегодно проверять, не появился ли компонент в списке устаревших, и не изменилось ли его поведение после обновления ОС Действовать придётся в спешке, уже после публикации уведомления об устаревании

Особенно важен ежегодный пересмотр: это процедура, которая не даёт пропустить уведомление об устаревании или последствия обновления ОС. На случай перехода ответственного сотрудника на другую должность оставляйте не только конфигурацию, но и процедуру восстановления. Сохранение текущего состояния без выполнения этих условий несёт риск того, что никто не узнает ни об остановке, ни о её причине.

3. Инвентаризация: зафиксируйте, что вы поручили MSMQ

3.1. Разберитесь с возможностями и терминами

В MSMQ отправляющая сторона записывает сообщение в очередь, а приложение на той же или на другой машине извлекает его в удобный момент. К цели миграции нужно перенести в основном три свойства.

Сообщения накапливаются, даже когда получатель недоступен: store-and-forward

Даже при недоступном получателе сообщения накапливаются локально и доставляются после восстановления. Это помогает на ненадёжных линиях связи между площадками, но безусловной долговечности с сохранением через перезапуск не гарантирует.

Фиксация очереди и обновления БД вместе: транзакции

Операции с очередью можно сделать транзакционными, а с помощью MS DTC (Distributed Transaction Coordinator) извлечение из очереди и обновление БД можно зафиксировать в одной распределённой транзакции.

Работал без установки дополнительного ПО: в составе ОС

Дополнительное ПО устанавливать не требовалось, поэтому в 2000-е годы MSMQ широко использовали для интеграции заказов и поставок, асинхронной обработки документов и связи между этапами производства на площадке. Именно сочетание этих трёх свойств и объясняет, почему он жив до сих пор.

Различайте накопление, долговечность и транзакции по терминам

Термины, используемые в таблицах решений, определим и здесь.9

Термин Что означает и почему это проверяют
Частная очередь / публичная очередь Частная регистрируется только на локальном компьютере и не публикуется в Active Directory. Указывается в виде .\\private$\\имя_очереди. Публичная регистрируется в службе каталогов и доступна для поиска в пределах домена. В небольших и средних бизнес-системах чаще встречаются частные
Сообщение express Режим отправки по умолчанию. Хранится в памяти и при передаче, и после доставки, поэтому быстр, но теряется при остановке этого компьютера или службы очереди сообщений
Сообщение recoverable (восстанавливаемое) Хранится на диске на отправляющей стороне, на каждом промежуточном компьютере и в очереди назначения, поэтому переживает перезапуск. Указывается отправляющей стороной явно
Транзакционная очередь Обрабатывает только транзакционные сообщения. Свойство задаётся при создании очереди и позже изменено быть не может. Транзакционные сообщения сохраняются на диск
DTC Служба Windows, координирующая транзакцию, охватывающую несколько ресурсов, например MSMQ и базу данных. Как заменить согласованность очереди и БД — главный предмет спора при миграции
Очередь журнала / очередь недоставленных сообщений Системные очереди, создаваемые MSMQ. Первая хранит копии отправленных и извлечённых сообщений, вторая — сообщения, которые не удалось доставить. Следите за ростом объёма, если их не обслуживать

«Может накапливать, пока получатель остановлен» и «переживает перезапуск MSMQ или машины» — это разные свойства. Проверьте отдельно, используется ли только первое или необходимо и второе, за счёт сообщений recoverable или транзакционных.

3.2. Выявите зависимости из кода, конфигурации и эксплуатации

Не заканчивайте инвентаризацию ссылками на библиотеки

Сначала ищут ссылки на System.Messaging и места создания MessageQueue. Однако отсутствие такой ссылки само по себе не даёт основания заключить, что MSMQ не используется. Проверяйте пути вызова по отдельности.

Путь вызова Что искать в коде и конфигурации
Библиотека .NET Framework Ссылки на System.Messaging, места создания MessageQueue
Конфигурация WCF netMsmqBinding / msmqIntegrationBinding
Нативный API и COM Нативные функции MQ*, такие как MQSendMessage, и вызовы через COM

Сохраните по каждой очереди основание для решения о миграции

Записывайте результаты проверки по каждой очереди в таблицу ниже. Цель — не просто проверить и забыть, а оставить результаты как основание для политики миграции и внутренних обоснований.

Аспект Где смотреть Что записать Как влияет на решение
(a) Путь очереди Строка пути, передаваемая в MessageQueue, адрес подключения в файлах конфигурации, указание FormatName: Локальная или удалённая, частная или публичная, имя машины-партнёра Если удалённая или между площадками, определите необходимость store-and-forward (строка «устойчивость к офлайну» в разделе 4.2). Если всё локально, это попадает в первую строку раздела 4.2
(b) Транзакции и Recoverable Место создания очереди (транзакционная ли она), указание Recoverable при отправке, использование DTC Транзакционная очередь или нет, есть ли указание Recoverable, участвует ли DTC Если DTC используется, табличная очередь как цель миграции почти определена. Если нет ни того, ни другого, явно укажите, что текущая система эксплуатируется исходя из того, что при перезапуске сообщения теряются
(c) Форматтер Места указания XmlMessageFormatter / BinaryMessageFormatter / ActiveXMessageFormatter Используемый форматтер и тип тела сообщения Если это семейство BinaryFormatter, замена на JSON обязательна (раздел 6.1). Это становится главным фактором трудозатрат миграции8
(d) Журнал и очередь недоставленных сообщений Свойства очереди (включён ли журнал), содержимое системных очередей, инструкции по эксплуатации Используется ли журнал, смотрит ли кто-нибудь на очередь недоставленных сообщений То, как обрабатываются неудачные сообщения, становится требованием к проектированию цели миграции (в табличной очереди — таблица отбраковки)
(e) Кто создаёт и удаляет Установщик, скрипты развёртывания, вызов Create при запуске приложения Кто создаёт очереди и кто настраивает права При миграции это прямо превращается в вопрос «кто подготовит новые очереди». Если выбран вариант консервации, перенесите это в процедуру подготовки окружения (глава 2)

4. Цель миграции: выбирают по нужным свойствам, а не по названию продукта-преемника

4.1. Разберите требования четырьмя вопросами

По результатам инвентаризации уточните следующие четыре пункта.

  1. Принимающая сторона — это обработка, которая обновляет собственную рабочую базу данных?
  2. Транзакционная очередь и обновление БД фиксируются вместе через DTC?
  3. Отправляющая и принимающая стороны находятся на разных машинах или площадках? Действительно ли используется store-and-forward?
  4. Каков объём сообщений? При масштабе в несколько тысяч — несколько десятков тысяч сообщений в день, характерном для многих бизнес-систем, пропускная способность кандидатов вряд ли станет решающим доводом.

4.2. Первый кандидат для каждого свойства

Если это асинхронная обработка, обновляющая рабочую БД той же системы, сначала рассматривают табличную очередь. Брокер выбирают тогда, когда есть требование, которое табличная очередь не закрывает: доставка в несколько систем, маршрутизация и подобное.

Используемое свойство Первый кандидат Обоснование и предостережения
Асинхронная обработка внутри одной системы (принимающая сторона обновляет БД) Табличная очередь в БД «Извлечение сообщения» и «обновление бизнес-данных» можно зафиксировать в одной локальной транзакции, поэтому DTC становится не нужен. Существующее резервное копирование, мониторинг и эксплуатация применяются без изменений. На отправляющей стороне «обновление бизнес-данных и вставка строки сообщения» тоже можно поместить в одну транзакцию, а это и есть так называемый паттерн Outbox
DTC делает очередь и обновление БД атомарными Табличная очередь в БД Суть этой миграции — заменить распределённую транзакцию локальной. Облачные сервисы очередей не могут участвовать в DTC, поэтому пока есть это требование, брокер — обходной путь
Слабосвязанная интеграция нескольких систем и языков RabbitMQ (на своих серверах) / Azure Service Bus (можно в облаке) Веерная доставка (fan-out) и маршрутизация — сильная сторона брокеров. Для RabbitMQ заложите в оценку нагрузку на самостоятельную эксплуатацию (резервирование и обновления)
Устойчивость к офлайну между площадками (store-and-forward) Azure Service Bus и подобное плюс схема повторной отправки, либо табличная очередь на каждой площадке плюс синхронизация Механизмов, прозрачно заменяющих поведение MSMQ «накопить на отправляющей стороне и доставить позже», немного. Измените проект так, чтобы ответственность за накопление на отправляющей стороне была явно возложена на приложение
Производитель и потребитель внутри одного процесса Очередь в памяти, например Channels в .NET Случай, когда межпроцессная очередь изначально была не нужна. Оставьте всё внутри процесса, а если нужна долговечность — переходите на табличную очередь
Используется только как межпроцессное взаимодействие между службами Windows IPC, например именованные каналы Но такая замена возможна только тогда, когда обе стороны работают одновременно. MSMQ принимает отправку и при остановленной принимающей стороне (даже для сообщений express) и доставляет позже, а канал сразу завершается ошибкой. Если вы полагаетесь на накопление во время простоя, реализуйте повторную отправку и буферизацию в приложении либо оставьте очередь. Как выбирать, описано в статье «Таблица решений по IPC в Windows»

Почему сначала рассматривают табличную очередь

«Сначала рассмотреть табличную очередь» здесь означает не желание рекомендовать базу данных для любой задачи. Причина в том, что для асинхронной обработки, обновляющей ту же БД, которая часто встречается в бизнес-системах эпохи MSMQ, согласованность остаётся простой, а существующее резервное копирование, мониторинг и эксплуатацию можно задействовать.

Требования, для которых подходит брокер, и что нужно спроектировать дополнительно

RabbitMQ и Azure Service Bus подходят для слабосвязанной доставки между несколькими системами и на разных языках, для веерной доставки и маршрутизации. Их стоит рассматривать и тогда, когда нужна высокая пропускная способность. Однако очередь и рабочая БД становятся разными ресурсами, поэтому не переносите атомарность MSMQ и DTC как есть: проектируйте идемпотентность и повторную отправку на стороне приложения. В оценку включите также мониторинг, резервирование, обновления и обучение сотрудников для нового ПО.

Даже после миграции накопление при простое не всегда можно отбросить

Проверьте, можно ли вообще отбросить требования устойчивости к офлайну и накопления при остановке принимающей стороны. Выбор облачной очереди не добавляет локальное накопление на отправляющей стороне автоматически, а переход на именованные каналы не сохраняет ожидание при остановленном партнёре в прежнем виде. Если это нужно, добавьте в приложение повторную отправку и буферизацию либо оставьте очередь.

5. Табличная очередь: фиксация операций с очередью и обновлений бизнес-данных в одной БД

В этой главе сначала разберём, что даёт сведение в одну БД. Затем по порядку рассмотрим предпосылки SQL извлечения, что именно гарантирует порядок, транзакцию воркера и обработку, добавляемую для эксплуатации.

5.1. Почему DTC становится ненужным

В MSMQ очередь и БД — разные ресурсы. Чтобы зафиксировать вместе «извлекли из очереди» и «обновили бизнес-данные», требовалась распределённая транзакция через DTC.

Если сделать очередь таблицей в той же базе данных, что и бизнес-данные, и то и другое становится обновлением одной БД. Извлечение, обновление бизнес-данных и запись о завершении можно зафиксировать в одной локальной транзакции. Замена распределённой транзакции локальной и есть суть этой миграции.

Фиксировать может не только принимающая, но и отправляющая сторона

На отправляющей стороне обновление бизнес-данных и INSERT строки сообщения тоже можно поместить в одну транзакцию. Это паттерн Outbox, и он предотвращает несогласованность, когда «бизнес-данные обновлены, а сообщение не отправлено».

Ниже — минимальная форма для SQL Server. Часть на C# — это псевдокод, показывающий границы транзакции, а не готовую реализацию для развёртывания как есть.

5.2. Таблица для хранения заданий

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);

В Payload хранится тело в формате JSON, а Status различает не обработанные, обрабатываемые и завершённые задания. StartedAt и RetryCount используются при возврате в работу и повторных попытках.

5.3. Извлечение одной строки и проверка настроек БД

Обновление извлекаемой строки и получение тела одним оператором SQL

При извлечении одна строка переводится в состояние «в обработке», а тело принимается через OUTPUT. Благодаря READPAST кандидаты, заблокированные другим воркером, пропускаются без ожидания, что позволяет обрабатывать очередь несколькими процессами.10

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;

Сначала проверьте настройку снимка и уровень изоляции

Читая этот SQL, проверьте READ_COMMITTED_SNAPSHOT и уровень изоляции сеанса.

В официальной документации указано: если параметр базы данных READ_COMMITTED_SNAPSHOT имеет значение ON, а сеанс находится на уровне READ COMMITTED или в запросе дополнительно указана подсказка READCOMMITTED, то READPAST в таком виде указать нельзя. Решение — убрать подсказку READCOMMITTED, если она есть, и включить READCOMMITTEDLOCK.10

Уровень изоляции по умолчанию — READ COMMITTED, поэтому если перенести этот SQL в базу с включённым снимком без мер предосторожности, дело не ограничится блокировкой — оператор SQL завершится ошибкой. В Azure SQL Database параметр включён по умолчанию. В локальной среде его тоже могли включить для борьбы с блокировками при чтении, поэтому проверьте заранее.

SELECT is_read_committed_snapshot_on FROM sys.databases WHERE name = DB_NAME();

В приведённом выше SQL извлечения указан READCOMMITTEDLOCK, поэтому чтение идёт с блокировками независимо от настройки снимка. В базе, где параметр OFF, это совпадает с поведением по умолчанию.10

Почему ROWLOCK не указан рядом и в чём ограничение READPAST

В отличие от часто встречающегося WITH (READPAST, UPDLOCK, ROWLOCK), здесь ROWLOCK рядом не указан. Причина в том, что ROWLOCK и READCOMMITTEDLOCK относятся к одной группе подсказок гранулярности, и для одной таблицы нельзя указать обе.10

READPAST позволяет пропускать только блокировки строк, блокировки страниц он пропустить не может. В таком примере, где одна строка берётся поиском по индексу с TOP (1), эскалация блокировок практически не проблема, но если вы хотите явно указать ROWLOCK, убедитесь, что READ_COMMITTED_SNAPSHOT имеет значение OFF, и уберите вместо него READCOMMITTEDLOCK.

5.4. Порядок извлечения и порядок применения к бизнес-данным — разные вещи

Для извлекаемых кандидатов задайте порядок

Если опустить ORDER BY и написать UPDATE TOP (1), то, какая строка будет выбрана, не определено. TOP в UPDATE не упорядочивает затрагиваемые строки, и наличие индекса по (Status, Id) само по себе тоже не гарантирует порядок. Чтобы старые задания не откладывались бесконечно, в примере указан ORDER BY Id.11

При параллельной обработке первым извлечённый не значит первым применённый

При этом порядок завершения при параллельной обработке не выравнивается. Пока воркер A обрабатывает задание 1, воркер B пропускает эту строку через READPAST и может зафиксировать задание 2 раньше.

Что выравнивается Что не выравнивается
Какая строка извлекается первой (ORDER BY Id) Какая строка применяется к бизнес-данным первой

Когда порядок применения имеет значение — например, обновления по одному и тому же номеру заказа, — выбирают один из следующих вариантов.

Как сохранить порядок Что получаете и чем платите
Один потребитель Отказываетесь от параллелизма ради порядка. Самый простой вариант, если он выдерживает нужную пропускную способность
Разделение по ключу Закрепляете воркер за ключом вроде номера заказа либо добавляете условие, при котором строка не извлекается, пока обрабатывается тот же ключ. Гарантируется порядок внутри ключа, а порядок между ключами — нет

Если не выбирать ни то, ни другое, явно укажите в проекте, что при параллельной обработке общий порядок FIFO не гарантируется. В MSMQ при параллельном приёме возникает та же проблема. Важно не переносить систему, сохраняя представление «это очередь, значит, обрабатывается по порядку».

5.5. Границы транзакции воркера

На принимающей стороне нужно выровнять транзакцию, охватывающую извлечение, обновление бизнес-данных и запись о завершении. В псевдокоде ниже три операции получают одно и то же подключение и транзакцию, а в конце всё фиксируется вместе.

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();   // «Извлечение» и «обновление бизнес-данных» фиксируются этой одной строкой вместе
}

Ключевой момент в том, что DequeueOne, ApplyBusinessData и MarkDone используют одно подключение и одну транзакцию, а последний Commit фиксирует извлечение и обновление бизнес-данных вместе. Если заменить это брокером, свойство «одна и та же БД» теряется, и нужна другая схема обеспечения согласованности.

5.6. Возврат в работу, повторные попытки и очистка для эксплуатации

Помимо минимального примера, спроектируйте следующие операции.

Операция Что делает
Возврат строк, оставшихся в обработке Восстановление, которое переводит строки в Status = 0, если Status = 1 и StartedAt старше заданного интервала
Ограничение повторных попыток и отбраковка Строки, у которых RetryCount превысил предел, переносятся в отдельную таблицу или, например, в Status = 9. Это место соответствует очереди недоставленных сообщений в MSMQ
Очистка завершённых строк Строки со Status = 2 периодически удаляются или архивируются, чтобы таблица и индексы не разрастались

В небольших и средних бизнес-системах таблицы и опроса или варианта с уведомлениями часто достаточно. Прежде чем добавлять ещё один продукт для очередей, проверьте, покрывает ли эта реализация нужные свойства и эксплуатационные задачи.

6. Процедура переключения: сначала выравниваем формат, поведение и принимающую сторону

При переключении сначала выравнивают формат сообщений и тесты входных данных и результатов. Затем выбирают между связкой старого и нового через мост и переключением после опустошения старой очереди.

6.1. Определите формат сообщений, в котором старое и новое могут сосуществовать

Форматтом по умолчанию после миграции делают JSON. Не переносят сериализацию семейства BinaryFormatter, например BinaryMessageFormatter. Реализация BinaryFormatter удалена из среды выполнения в .NET 9, и с точки зрения безопасности он тоже не рекомендуется.8

С другой стороны, это не значит, что любой двоичный формат никуда не годится. Форматы, спецификация которых поддерживается независимо, например Protocol Buffers или MessagePack, можно без проблем перенести в новую систему как есть. Форматтер и тип тела уточните заранее при инвентаризации из раздела 3.2.

6.2. До переписывания зафиксируйте входы и результаты тестами

Обработка очереди — это часть, где легко возникают дефекты, зависящие от времени. Заранее подготовьте характеризующие тесты вида «такое входное сообщение даёт такой результат», чтобы после замены можно было механически проверить, что результат тот же. Смотрите также «Зафиксировать поведение характеризующими тестами и только потом рефакторить».

6.3. Начинайте с принимающей стороны, а мост проектируйте с учётом потерь и дублей

Сначала переведите принимающую сторону

Сначала подготовьте новую очередь и переведите на неё принимающую сторону, и только затем переключайте отправляющую.

Если поставить небольшой мост, переносящий сообщения из старого MSMQ в новую очередь, не придётся переключать всех отправителей сразу. Сам мост может оставаться на .NET Framework.

Разделите процедуру передачи по наличию транзакций

Однако если остановка произойдёт между извлечением из MSMQ и записью в новую очередь, возникнут либо потеря, либо повторная доставка. Проектируйте в зависимости от типа исходной очереди, как показано ниже.

Источник Минимально необходимая передача
Транзакционная очередь Принимать на стороне MSMQ транзакционно, чтобы при неудачной записи можно было откатиться. На стороне новой очереди отбрасывать дубли по идентификатору сообщения и обрабатывать идемпотентно
Нетранзакционная очередь Прочитать через Peek → идемпотентно записать в новую очередь → убедиться, что запись прошла, и только после этого удалить из старой очереди. Дубли, возникающие при остановке до удаления, поглощаются на стороне новой очереди

Свойство транзакционности очереди задаётся при создании и позже изменено быть не может. Обратите внимание, что процедуру транзакционного приёма нельзя применить к нетранзакционной очереди как есть.

6.4. Если мост не оправдан, опустошите очередь и переключайтесь

Если меры против потерь и дублей в мосте несоразмерны масштабу системы, не заставляйте старое и новое сосуществовать, а выбирайте переключение после полного опустошения старой очереди во время плановой остановки.

Переключение с оставшимися в очереди сообщениями ведёт к двойной обработке и потерям. Опустошить очередь и затем переключиться в итоге часто оказывается и самым безопасным, и самым быстрым — таково практическое суждение этой статьи.

7. Итоги

На июль 2026 года, то есть на момент, принятый в этой статье за точку отсчёта, MSMQ официально не объявлен устаревшим. Сохранение как функции ОС и лёгкость миграции на .NET — это разные вещи. System.Messaging замкнут на .NET Framework, поэтому правило такое: если приложение переходит на .NET, очередь пересматривают вместе с ним.123

Если пока продолжать использование, проверьте срок поддержки ОС и подготовьте процедуры сборки и восстановления, мониторинг очереди, запись конфигурации и ежегодный пересмотр решения. Большой риск консервации не только технический: не остаётся человека, который может к системе прикоснуться.

Если выполнять миграцию, сначала проведите инвентаризацию зависимостей и форматов сообщений. Для асинхронной обработки, обновляющей ту же рабочую БД, первый кандидат — табличная очередь. Согласованность, которую обеспечивали MSMQ и DTC, можно заменить локальной транзакцией в той же БД. RabbitMQ или Azure Service Bus рассматривают тогда, когда есть требование, которого она не закрывает: доставка в несколько систем, маршрутизация и подобное.

Кроме того, проверьте уровень изоляции и подсказки SQL, порядок применения при параллельной обработке и меры против потерь и дублей в мосте. Важно не спешить из-за «говорят, технологию упразднили», а понять, от чего зависит именно ваша система, и на этом основании решить, сохранять её или переносить.

Похожие статьи

Смежные области консультаций

Komura Software LLC занимается инвентаризацией устаревших конфигураций, включая MSMQ, и планированием их миграции, переводом с .NET Framework на .NET, а также доработкой бизнес-систем, включающих обработку очередей.

Справочные материалы

  1. Microsoft Learn, Deprecated features in the Windows client. Официальный список функций клиента Windows, активная разработка которых прекращена, то есть устаревших. О том, что по состоянию на июль 2026 года в списке есть NTLM, VBScript, WordPad и другие пункты, а MSMQ (Microsoft Message Queuing) в нём не указан, и о том, что устаревание (deprecated) — это статус «активная разработка не ведётся, компонент может быть удалён в будущем обновлении», который отличается от удаления (removed).  2 3 4

  2. Microsoft Learn, Features Removed or No Longer Developed in Windows Server. Официальный список удалённых из Windows Server функций и функций, разработка которых прекращена (устаревших). О том, что в списке по состоянию на июль 2026 года, включая вкладку Windows Server 2025, MSMQ не указан, и о том, что устаревшие компоненты продолжают входить в состав Windows Server, поддерживаются для эксплуатации в соответствии с жизненным циклом продукта и продолжают получать обновления безопасности и качества.  2 3 4

  3. Microsoft Learn, MessageQueue Class (System.Messaging). О том, что справочник по классу System.Messaging.MessageQueue охватывает версии с .NET Framework 1.1 по 4.8.1 и что версии для .NET (Core и более поздних) не существует.  2 3 4

  4. Microsoft Learn, .NET Framework official support policy. О том, что .NET Framework 4.8 определён как компонент операционной системы Windows и поддерживается в соответствии с политикой жизненного цикла родительского продукта (операционной системы), в котором он установлен.  2

  5. Microsoft Learn (архив), Message Queuing Functions, MQSendMessage и MQReceiveMessage. О том, что нативный Win32 API MSMQ (MQCreateQueue, MQSendMessage, MQReceiveMessage и другие) документирован для приложений на C/C++ и позволяет создавать очереди, отправлять и принимать сообщения, не обращаясь к управляемому API.  2

  6. Microsoft Learn, Use the Windows Compatibility Pack to port code to .NET. О том, что пакет совместимости Windows (пакет Microsoft.Windows.Compatibility) предоставляет около 20 000 API, чтобы закрыть зависимости от API, существующих только в .NET Framework, при миграции на .NET, и что в его перечне технологических областей (CodeDom, конфигурация, Directory Services, Drawing, ODBC, ACL, WCF, реестр, WMI, счётчики производительности, службы Windows, EventLog и другие) обмен сообщениями (System.Messaging) отсутствует.  2

  7. CoreWCF project, CoreWCF.MSMQ (NuGet) и CoreWCF 1.4.0 Preview release, Microsoft, CoreWCF Support Policy. О том, что CoreWCF — это проект под руководством сообщества, переносящий серверную часть WCF на .NET, что Microsoft предоставляет для него официальную политику поддержки, что в рамках транспортов для очередей опубликована поддержка MSMQ (пакет CoreWCF.MSMQ) и что эта реализация MSMQ зависит от community-порта библиотеки System.Messaging из .NET Framework.  2 3

  8. Microsoft Learn, BinaryFormatter migration guide. О том, что BinaryFormatter поэтапно выводится из использования по соображениям безопасности, что начиная с .NET 9 его реализация удалена из среды выполнения и по умолчанию он недоступен, и о том, что в качестве цели миграции предлагаются безопасные форматы сериализации, такие как JSON (System.Text.Json).  2 3

  9. Microsoft Learn (архив), Express and Recoverable Messaging, System-Generated Queues, Message Queuing (MSMQ). О том, что сообщение express хранится в оперативной памяти и при передаче, и после доставки и теряется при остановке компьютера, на котором оно находится, или при остановке службы очереди сообщений; что сообщение recoverable записывается на диск на отправляющем компьютере и на каждом промежуточном компьютере и хранится на диске также в очереди назначения; что публичная очередь регистрируется в службе каталогов, тогда как частная регистрируется только на локальном компьютере и в службе каталогов не публикуется; и о том, что очередь журнала, хранящая копии извлечённых из очереди и уже отправленных сообщений, и очередь недоставленных сообщений (dead-letter), хранящая сообщения, которые не удалось доставить, — это системные очереди, создаваемые MSMQ. 

  10. Microsoft Learn, Table Hints (Transact-SQL). О том, что READPAST — это подсказка, пропускающая строки, заблокированные другими транзакциями, не читая их; что пропускать можно только блокировки уровня строк, но не уровня страниц; и что указывать её можно только при уровне изоляции READ COMMITTED или REPEATABLE READ. В частности, о формулировке: «Если параметр базы данных READ_COMMITTED_SNAPSHOT имеет значение ON и истинно хотя бы одно из условий: (a) уровень изоляции транзакции сеанса — READ COMMITTED; (b) в запросе указана также табличная подсказка READCOMMITTED, — табличную подсказку READPAST указать нельзя. Чтобы указать подсказку READPAST в этих случаях, удалите табличную подсказку READCOMMITTED, если она есть, и включите в запрос табличную подсказку READCOMMITTEDLOCK На той же странице смотрите также, что READCOMMITTEDLOCK заставляет чтение READ COMMITTED использовать блокировки независимо от настройки READ_COMMITTED_SNAPSHOT и что для одной таблицы нельзя указать более одной подсказки гранулярности (PAGLOCK / NOLOCK / READCOMMITTEDLOCK / ROWLOCK / TABLOCK / TABLOCKX). Настройку на стороне базы данных можно проверить через is_read_committed_snapshot_on в sys.databases 2 3 4

  11. Microsoft Learn, TOP (Transact-SQL). О том, что при использовании TOP с INSERT, UPDATE, MERGE и DELETE затрагиваемые строки не упорядочиваются каким-либо образом и что, когда порядок важен, используют подзапрос с TOP и ORDER BY. 

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Правда ли, что MSMQ упразднили?
Нет. По состоянию на июль 2026 года MSMQ нет ни в списке устаревших функций клиента Windows, ни в списке «Удалённые функции и функции, разработка которых прекращена» для Windows Server. Как дополнительный компонент Windows он по-прежнему входит в состав актуальных версий ОС. Формулировка «технологию упразднили» распространилась из-за путаницы с ситуацией на стороне .NET. Стандартная библиотека System.Messaging, через которую работают с MSMQ, существует только в .NET Framework и в .NET (начиная с Core) перенесена не была. Точнее говоря, состояние такое: как функция ОС MSMQ сохраняется, но официального способа использовать его из современного .NET нет.
Есть ли способ использовать MSMQ из .NET 8 или .NET 10?
Официального управляемого API нет. System.Messaging — это API для .NET Framework вплоть до 4.8.1, и в пакет совместимости Windows (Microsoft.Windows.Compatibility) он не входит. Вызывать нативный Win32 API MSMQ (например, MQSendMessage) через P/Invoke технически возможно, но это означает, что придётся самостоятельно написать и сопровождать обёртку, включая форматтеры и интеграцию с транзакциями. В сообществе проект CoreWCF публикует пакет для транспорта MSMQ (CoreWCF.MSMQ), однако он предназначен для размещения сервисов WCF, вызываемых через очередь, на современном .NET, то есть это перенос серверной части WCF. Он не является ни универсальным API очередей вместо System.Messaging, ни заменой клиенту отправляющей стороны. Его реализация тоже зависит от community-порта версии System.Messaging для .NET Framework. Как вариант для проверки или временного продления срока службы он годится. У самого CoreWCF есть официальная политика поддержки Microsoft, но на community-порт System.Messaging, от которого зависит эта реализация MSMQ, такая же гарантия не распространяется, поэтому если вы собираетесь положить это в основу бизнес-системы, решение принимайте только после проверки того, попадает ли используемая версия под политику поддержки и как будет обслуживаться зависимая часть. Практически правильный путь — переносить очередь одновременно с переводом приложения на .NET.
Что предпочесть в качестве цели миграции — RabbitMQ или Azure Service Bus?
Прежде чем делать этот выбор, рассмотрите вариант с очередью на таблице базы данных. В большинстве небольших и средних бизнес-систем, использующих MSMQ, на другой стороне очереди находится обработка, которая обновляет собственную базу данных компании. В этом случае табличная очередь позволяет зафиксировать «извлечение сообщения» и «обновление бизнес-данных» в одной локальной транзакции с бизнес-данными и заменить согласованность, которую обеспечивали MSMQ и распределённая транзакция (DTC), более простым механизмом. А к брокеру переходить тогда, когда табличной очереди не хватает для требования, например когда нужна слабосвязанная доставка между несколькими системами или высокая пропускная способность: если требования к локальному развёртыванию сильны, смотрите на RabbitMQ, если можно разместить в облаке — на Azure Service Bus. Такой порядок меньше всего ведёт к неудаче.
Допустимо ли пока оставить всё на .NET Framework?
Условно да. .NET Framework 4.8 поддерживается как компонент Windows, в соответствии с жизненным циклом операционной системы, и сам MSMQ не объявлен устаревшим, так что рассчитывать на его работу можно. Но если вы выбираете оставить всё как есть, обязательно сделайте четыре вещи: явно укажите включение компонента очереди сообщений в процедуре подготовки окружения, настройте мониторинг длины очереди и журнала, задокументируйте формат сообщений и конфигурацию подключений и оставьте процедуру восстановления на случай ухода ответственного сотрудника. Настоящий риск консервации системы не технический — он в том, что не остаётся человека, который может к ней прикоснуться.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог