Как обработать ошибку сообщения в привязках MSMQ для WCF - PullRequest
17 голосов
/ 17 сентября 2008

Я создал службу 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 ]

14 голосов
/ 17 сентября 2008

Я думаю, что с 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 от Ювала Лоуи

9 голосов
/ 17 сентября 2008

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

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

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

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

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

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

4 голосов
/ 17 сентября 2008

Если вы используете 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 при получении сообщения из очереди и вызвал ваш метод.

1 голос
/ 17 сентября 2008

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

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

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

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

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

...