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