Как мне обработать сбой сообщения в привязках MSMQ для WCF

StackOverflow https://stackoverflow.com/questions/82099

  •  09-06-2019
  •  | 
  •  

Вопрос

Я создал службу WCF и использую привязку netMsmqBinding.

Это простой сервис, который передает Dto моему сервисному методу и не ожидает ответа.Сообщение помещается в MSMQ и после получения вставляется в базу данных.

Каков наилучший способ убедиться, что данные не теряются?

Я попробовал 2 следующих метода:

  1. Создать исключение

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

  2. установите значение ReceiveRetryCount="3" для привязки

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

В идеале я хотел бы сделать следующее:

Попробуйте обработать сообщение

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

Могу ли я достичь этого?Если да, то каким образом?Можете ли вы указать мне на какие-нибудь хорошие статьи о том, как лучше всего использовать WCF и MSMQ для моей данной sceneria.

Мы были бы очень признательны за любую помощь.Спасибо!

Некоторая дополнительная информация

Я использую MSMQ 3.0 в Windows XP и Windows Server 2003.К сожалению, я не могу использовать встроенную поддержку ядовитых сообщений, предназначенную для MSMQ 4.0 и Vista / 2008.

Это было полезно?

Решение

В SDK есть пример, который может быть полезен в вашем случае.По сути, что он делает, так это присоединяет реализацию IErrorHandler к вашей службе, которая будет перехватывать ошибку, когда WCF объявляет сообщение "ядовитым" (т.Е.когда все настроенные повторные попытки будут исчерпаны).Что делает пример, так это перемещает сообщение в другую очередь, а затем перезапускает ServiceHost, связанный с сообщением (поскольку при обнаружении вредоносного сообщения произойдет сбой).

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

1 - Если у вас есть несколько конечных точек, связанных с вашим сервисом (т.е.передается через несколько очередей), нет способа узнать, в какую очередь поступило вредоносное сообщение.Если у вас только одна очередь, это не будет проблемой.Я не видел никакого официального решения для этого, но я поэкспериментировал с одной возможной альтернативой, которую я задокументировал здесь: http://winterdom.com/weblog/2008/05/27/NetMSMQAndPoisonMessages.aspx

2- Как только сообщение о проблеме перемещается в другую очередь, это становится вашей ответственностью, поэтому вы должны переместить его обратно в очередь обработки по истечении тайм-аута (или подключить к этой очереди новую службу для его обработки).

Честно говоря, в любом случае, вы смотрите на некоторую "ручную" работу, которую WCF просто не покрывает сама по себе.

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

Другие советы

Я думаю, что с MSMQ (доступным только в Vista) вы могли бы сделать вот так:

<bindings>
    <netMsmqBinding>
        <binding name="PosionMessageHandling"
             receiveRetryCount="3"
             retryCycleDelay="00:05:00"
             maxRetryCycles="3"
             receiveErrorHandling="Move" />
    </netMsmqBinding>
</bindings>

WCF немедленно повторит попытку ReceiveRetryCount несколько раз после первого сбоя вызова.После сбоя пакета сообщение перемещается в очередь повторных попыток.После задержки в минуту RetryCycleDelay сообщение перемещается из очереди повторных попыток в очередь конечной точки, и пакет повторяется.Это будет повторено Максимальное время трех циклов.Если все это не удается, сообщение обрабатывается в соответствии с обработкой receiveErrorHandling, которая может быть перемещена (в очередь отравления), отклонена, удалена или ошибка

Кстати, хорошим текстом о WCF и MSMQ является глава 9 книги Programmig WCF от Ювала Лоуи

Если вы используете SQL-Server, то вам следует использовать распределенную транзакцию, поскольку и MSMQ, и SQL-Server поддерживают ее.Что происходит, так это то, что вы помещаете запись в базу данных в блок TransactionScope и вызываете scope.Complete() только в случае успеха.Если это не удастся, то, когда ваш метод WCF вернет сообщение, оно будет помещено обратно в очередь для повторной попытки.Вот урезанная версия кода, который я использую:

    [OperationBehavior(TransactionScopeRequired=true, TransactionAutoComplete=true)]
    public void InsertRecord(RecordType record)
    {
        try
        {
            using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
            {
                SqlConnection InsertConnection = new SqlConnection(ConnectionString);
                InsertConnection.Open();

                // Insert statements go here

                InsertConnection.Close();

                // Vote to commit the transaction if there were no failures
                scope.Complete();
            }
        }
        catch (Exception ex)
        {
            logger.WarnException(string.Format("Distributed transaction failure for {0}", 
                Transaction.Current.TransactionInformation.DistributedIdentifier.ToString()),
                ex);
        }
     }

Я тестирую это, ставя в очередь большое, но известное количество записей, позволяя WCF запускать множество потоков для обработки многих из них одновременно (достигает 16 потоков - 16 сообщений из очереди одновременно), затем завершаю процесс в середине операций.При перезапуске программы сообщения считываются из очереди и обрабатываются снова, как будто ничего не произошло, и по завершении теста база данных непротиворечива и не содержит пропущенных записей.

Распределенный диспетчер транзакций присутствует во внешней среде, и когда вы создаете новый экземпляр TransactionScope, он автоматически выполняет поиск текущей транзакции в области вызова метода, которая уже должна была быть создана WCF, когда она извлекла сообщение из очереди и вызвала ваш метод.

К сожалению, я застрял на Windows XP и Windows Server 2003, так что это не вариант для меня.- (Я еще раз уточню это в своем вопросе, поскольку я нашел это решение после публикации и понял, что не могу его использовать)

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

ИТАК, что я в итоге сделал, так это допустил сбой Линии и оставил сообщения в очереди.Я также отправляю в свою службу системного ведения журнала фатальное сообщение о том, что это произошло.Как только наша проблема будет решена, я перезапущу службу, и все сообщения снова начнут обрабатываться.

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

aogan, у вас был идеальный ответ для MSMQ 4.0, но, к сожалению, не для меня

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top