Как мне обработать ошибку сообщения в привязках MSMQ для WCF
Я создал службу WCF и использую привязку netMsmqBinding.
Это простой сервис, который передает Dto моему методу сервиса и не ожидает ответа. Сообщение помещается в MSMQ, и после того, как оно было получено, оно вставляется в базу данных.
Каков наилучший способ убедиться, что данные не будут потеряны.
Я попробовал 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, но, к сожалению, не для меня