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

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

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

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

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

  1. Бросить исключение

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

  2. установить receiveRetryCount="3" в привязке

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

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

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

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

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

Любая помощь приветствуется. Спасибо!

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

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

4 ответа

Решение

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

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

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

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

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

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

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

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

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

Кстати, хороший текст о WCF и MSMQ - глава 9 книги Progammig WCF от Ювала Лоуи.

Если вы используете SQL-сервер, вам следует использовать распределенную транзакцию, поскольку 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 Server не работает, как часто служба будет перезапущена.

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

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

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

Другие вопросы по тегам