一、MSMQ——消息的响应(响应队列)
如果需要从接收程序中获得比确认消息更多的信息(消息确认参考二),就可以使用响应队列。响应队列类似于一般队列,但原始发生程序吧该队列用作接收程序,原始接收
程序把响应队列作用于发生程序。
发送程序必须用 Message 类的 ResponseQueue 属性指定响应队列。在响应消息中,把 CorrelationId 属性设置为原始的消息ID,这样,客户端就知道该条应答属于哪条消息。
这类似于确认队列。响应消息用 MessageQueue 对象的 Send() 方法发送,MessageQueue 对象从 ResponseQueue 属性中返回。
当消息队列生成确认或报告消息时,它使用相关标识符属性来指定原始消息的消息标识符。 这样,相关标识符就可将报告或确认消息与原始消息关联起来。
发送应用程序然后可以进行匹配的确认或与原始消息的报表通过使用CorrelationId属性来标识原始消息的Id属性。
连接器应用程序还必须设置 CorrelationId 属性发送到原始消息的消息标识符的确认消息和报告的消息。
当你的应用程序将响应消息发送到发送应用程序时,可以设置 CorrelationId 响应消息的原始消息的消息标识符的属性。 发送应用程序然后可以将响应消息与已发送的消息进行匹配。
/// <summary> /// 消息队列 /// </summary> private MessageQueue messageQueue { get { string path = ".\\private$\\temp"; if (MessageQueue.Exists(path)) { //如果存在指定路径的消息队列,则获取 return new MessageQueue(path); } else { //不存在,则创建新的 return MessageQueue.Create(path); } } } /// <summary> /// 获取返回消息 /// </summary> public void ReciveMessage() { while (true) { //Message.Receive()是同步进行的,如果队列中没有消息,会阻塞当前线程 Message message = messageQueue.Receive(); message.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); string msg = message.Body.ToString(); var responeMessage = new Message("responeMessage") { //原始消息id CorrelationId = message.Id }; message.ResponseQueue = GetResponseQueue(); //获取或设置一个值,该值指示发送方 ID 是否应附在消息中。 //message.AttachSenderId = true; //发生响应消息 message.ResponseQueue.Send(responeMessage); Console.WriteLine(msg); } }
获取响应消息,获得原消息id进行匹配
1 /// <summary> 2 /// 获取响应队列 3 /// </summary> 4 /// <returns></returns> 5 public MessageQueue GetResponseQueue() 6 { 7 //路径 8 string path = ".\\private$\\ResponseQueue"; 9 if (MessageQueue.Exists(path)) 10 { 11 //如果存在指定路径的消息队列,则获取 12 return new MessageQueue(path); 13 } 14 else 15 { 16 //不存在,则创建新的 17 return MessageQueue.Create(path); 18 } 19 } 20 21 /// <summary> 22 /// 获取返回队列消息 23 /// </summary> 24 public void ReciveResponseQueue() 25 { 26 while (true) 27 { 28 var myQueue = GetResponseQueue(); 29 //设置将获取 CorrelationId 的MessageReadPropertyFilter设为TRUE 30 myQueue.MessageReadPropertyFilter.CorrelationId = true; 31 //Message.Receive()是同步进行的,如果队列中没有消息,会阻塞当前线程 32 Message message = myQueue.Receive(); 33 34 message.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); 35 string msg = message.Body.ToString(); 36 //获取原始消息的Id 37 var _correlationId = message.CorrelationId; 38 message.ResponseQueue = GetResponseQueue(); 39 40 Console.WriteLine(msg); 41 } 42 }
二、MSMQ——消息的确认
可以将MessageQueue对象设置为生成确认消息,能通知消息发送者消息是否已经成功传递。
有两种主要的确认类型:
·消息到达目标队列的确认
·目标应用程序从队列中检索到消息的确认
确认是通过向队列发送新消息来处理的。这种情况下,确认消息从目标队列发送到一个特殊类型的队列中:管理队列。确认消息不同于标准消息,因为它们不包含正文;在确认中,消息头中的信息最重要。
这里以一个示例来演示一下:
(一)新建管理队列
private MessageQueue CreateAdminQueue()
{
string trPath = @".\Private$\selfAdminQueue";
MessageQueue _queue;
if (!MessageQueue.Exists(trPath))
return MessageQueue.Create(trPath);
_queue = new MessageQueue(trPath);
return _queue;
}
(二)传递消息
public void SendConfirmMessage(string strMsg)
{
MessageQueue _queue = CreateQueue();
Message _message = new Message(strMsg);
_message.AdministrationQueue = CreateAdminQueue();
_message.AcknowledgeType =
AcknowledgeTypes.PositiveReceive
| AcknowledgeTypes.PositiveArrival;
_queue.Send(_message);
}
·设置AdminstrationQueue的属性。这个属性是用来设置和获取接收消息队列的确认消息的队列。
·设置确认消息属性AcknowledgeType。这个属性是用来设置和获取返回给发送方消息确认的类型,它是个枚举类型。枚举值:
PositiveArrival |
一个掩码,用于在原始消息到达队列时请求肯定确认。 |
PositiveReceive |
一个掩码,用于在成功从队列检索到原始消息时请求肯定确认。 |
NegativeReceive |
一个掩码,用于当未能从队列接收原始消息时请求否定确认。 |
None |
一个掩码,用于请求不发送任何确认消息(无论是肯定的还是否定的)。 |
NotAcknowledgeReachQueue |
一个掩码,用于在原始消息不能到达队列时请求否定确认。当到达队列时间计时器过期时或不能对消息进行身份验证时,可能请求否定确认。 |
NotAcknowledgeReceive |
一个掩码,用于当发生错误时请求否定确认,防止在其接收时间计时器过期前从队列接收原始消息。 |
FullReachQueue |
一个掩码,用于在原始消息到达队列时请求肯定确认,或者用于到达队列时间计时器过期后请求否定确认,或者用于不能对原始消息进行身份验证时请求否定确认。 |
FullReceive |
一个掩码,用于在接收时间计时器过期前从队列收到原始消息时请求肯定确认,否则请求否定确认。 |
(三)接收方
public string GetNormalMessage()
{
MessageQueue _queue = CreateQueue();
_queue.Formatter =
new XmlMessageFormatter(new Type[] { typeof(string) });
Message _message = _queue.Receive();
return _message.Id;
}
接收消息正常接收。然后得到消息的Id,通过Id来获取在管理队列中的管理消息的信息
(四)管理队列消息
public void GetAcknowledgmentMessage(string strMsgId)
{
MessageQueue _queue = CreateAdminQueue();
_queue.MessageReadPropertyFilter.CorrelationId = true;
_queue.MessageReadPropertyFilter.Acknowledgment = true;
try
{
while (_queue.PeekByCorrelationId(strMsgId) != null)
{
Message myAcknowledgmentMessage =
_queue.ReceiveByCorrelationId(strMsgId);
Console.WriteLine("Correlation Id: "
+ myAcknowledgmentMessage.CorrelationId.ToString());
Console.WriteLine("Id: "
+ myAcknowledgmentMessage.Id.ToString());
Console.WriteLine("Acknowledgment Type: "
+ myAcknowledgmentMessage.Acknowledgment.ToString());
}
}
catch
{ }
}
通过消息标识来检索管理队列,检索成功打印信息。确认类型Acknowledgment用于指定尝试的消息传递的结果。它是个枚举:
None |
该消息不是确认消息。 |
AccessDenied |
一个否定到达确认,它指示发送应用程序不具有将消息发送到目标队列所需的权限。 |
BadDestinationQueue |
一个否定到达确认,它指示目标队列不可用于发送应用程序。 |
BadEncryption |
一个否定到达确认,它指示目标队列管理器未能解密私有消息。 |
BadSignature |
一个否定到达确认,它指示原始消息的数字签名无效并且未能由消息队列进行身份验证。 |
CouldNotEncrypt |
一个否定到达确认,它指示源队列管理器未能加密私有消息。 |
HopCountExceeded |
一个否定到达确认,它指示已超出了原始消息的跳数(跳数指示中间服务器的数目)。 |
NotTransactionalQueue |
一个否定到达确认,它指示已将事务性消息发送到非事务性队列。 |
NotTransactionalMessage |
一个否定到达确认,它指示非事务性消息被发送到了事务性队列。 |
Purged |
一个否定到达确认,它指示消息在到达其目标队列前已被清除。 |
QueueDeleted |
一个否定读取确认,它指示在可以读取消息前队列已被删除。 |
QueueExceedMaximumSize |
一个否定到达确认,它指示原始消息因其目标队列已满而未被传送。 |
QueuePurged |
一个否定读取确认,它指示在可以读取消息前队列已被清除。 |
ReachQueue |
一个肯定到达确认,它指示原始消息已到达其目标队列。 |
ReachQueueTimeout |
一个否定到达确认,它指示在原始消息可到达目标队列前到达队列时间计时器或接收时间计时器已过期。 |
ReceiveTimeout |
一个否定读取确认,它指示在其接收时间计时器过期前没有从队列接收原始消息。 |
Receive |
一个肯定读取确认,它指示原始消息已由接收应用程序接收 |
(五)流程
·当向消息队列传递消息时,同时向管理队列传递了消息(当消息队列收到消息后,管理队列中也会收到消息)。此时的消息是:消息已经到达消息队列。(在管理队列中的消息是不带包体正文的,所以说头最重要。)
·当从消息队列中接收到消息后(此时消息的标识已经读出),消息队列消息就会清空(Revceive的)。然后在管理队列中就会再多一条管理消息,来标示:消息已经被接收。
·当处理管理队列消息时,会按消息标识来处理管理队列消息。