Microsoft Message Queuing (MSMQ) – промежуточная среда обмена сообщениями
Конструктор, получающий имена очередей для посылки и приема сообщений.
public MsmqClient(String queueSendName, String queueReceiveName, QueueFormatter formatterType): base(formatterType) { // список отправленных сообщений без ответов messages = new Dictionary<String,RequestType>(); // создание очереди для посылки запросов, если она не существует queueSend = MsmqTools.CreateQueue(queueSendName, QueueType.Transactional); // создание очереди для приема ответов, если она нужна if (queueReceiveName != null) { queueReceive = MsmqTools.CreateQueue(queueReceiveName); queueReceive.Formatter = answerFormatter; // считывать из очереди свойство CorrelationId queueReceive.MessageReadPropertyFilter.CorrelationId = true; } else { queueReceive = null; } }
В методе Dispose происходит закрытие используемых очередей.
public void Dispose() { queueSend.Close(); queueSend.Dispose(); if (queueReceive != null) { queueReceive.Close(); queueReceive.Dispose(); } }
Функции BeginReceive и EndReceive начинают и прекращают прием ответов сервера, изменяя обработчик события PeekComplete очереди ответов.
public void BeginReceive() { // установить обработчик на событие, возникающее при появлении // сообщения в очереди queueReceive.PeekCompleted += OnPeek; // начать отслеживание поступления сообщения в очередь queueReceive.BeginPeek(); } // прекратить прием ответов сервера public void EndReceive() { // отключить обработчик queueReceive.PeekCompleted -= OnPeek; }
Функция Send посылает в исходящую очередь запрос общего типа для его обработки сервером. Для ответа на сообщение серверу следует использовать очередь, указанную в поле ResponseQueue посылаемого сообщения.
public void Send(RequestType request) { // создание нового сообщения Message message = new Message(request, requestFormatter); message.ResponseQueue = queueReceive; // использование восстаналиваемых сообщений message.Recoverable = Recoverable; // послать сообщение; поскольку транзакция состоит из // единственной операции, вместо объекта-транзакции используется // значение MessageQueueTransactionType.Single queueSend.Send(message, MessageQueueTransactionType.Single); // поле message.Id устанавливается после посылки сообщения; // идентификатор сообщения связывается c отосланным запросом // в списке необслуженных запросов messages.Add(message.Id, request); }
Обработчик события очереди PeekComplete использует внутренние транзакции MSMQ. В одну транзакцию входит операция чтения ответа из очереди и последующий вызов события ProcessAnswer. Если в ходе обработки события возникло исключение, ответ сервера останется в очереди ответов. Иначе сообщение удаляется из поддерживаемого клиентом списка невыполненных запросов.
public void OnPeek(Object source, PeekCompletedEventArgs asyncResult) { // создание внутренней транзакции MSMQ MessageQueueTransaction transaction = new MessageQueueTransaction(); // начало транзакции transaction.Begin(); try { // прекратить ожидание сообщений в очереди queueReceive.EndPeek(asyncResult.AsyncResult); // получить сообщение из очереди в рамках транзакции Message message = queueReceive.Receive(transaction); // в поле CorrelationId должен быть идентификатор сообщения // с исходным запросом String messageId = message.CorrelationId; // есть ли такое сообщение в списке невыполненных запросов? if (messages.ContainsKey(messageId)) { if (message.Body is AnswerType) { // преобразовать тело сообщения к типу ответа // и вызвать событие по его обработке AnswerType answer = (AnswerType) message.Body; ProcessAnswer(this, messages[messageId], answer); }; messages.Remove(messageId); } // продолжить ожидать сообщения BeginReceive(); // успешное завершение транзакции transaction.Commit(); } catch (Exception e) { // отмена транзакции transaction.Abort(); throw e; } } }Листинг 5.2.