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.