這里應該將私有隊列稱做“專用隊列”好像更貼切一些了,O(∩_∩)O
可以訪問遠程主機的MSMQ的私有隊列的,這個是毋庸置疑的,但需要說明的是不能通過代碼創建私有隊列,關於這一點,我也不知道為什么?
下面說說我的經驗
1、首先要保證遠端的主機和本地機器同時加入到了同一個域中
2、要通過管理工具在遠端主機中創建私有的隊列,例如 192.168.117.47\Private$\MyPath,可以在創建時指定是否啟用事務
3、在本地無法得到遠端是否存在指定的私有隊列,也無法得到指定的私有隊列是否已經啟用了事務,因此在編碼時,最好能明確的知道遠端已經創建了這個私有隊列,並且這個私有隊列是否已經啟用了事務
4、下面給出遠端私有隊列的格式
如果是 IP 地址的形式,請使用 "FormatName:DIRECT=TCP:" + 遠端IP + @"\private$" + @"\" + 私有隊列的路徑名稱
如果是機器名的方式,請使用 "FormatName:DIRECT=OS:" +遠端的主機名 +
@"\private$" + @"\" + 私有隊列的路徑名稱
5、要保證本機和遠端的主機使用同樣的域賬戶登錄,最好這個賬戶也是本機及遠端機的系統管理員組成員
發送消息
public bool SendMessage(string path, object source, bool transactional = false){
// 注意路徑的格式見本日志(一)的部分
if (!string.IsNullOrWhiteSpace(path) && source != null)
{
try
{
using (MessageQueue mqSender = new
MessageQueue (path))
{
mqSender.MessageReadPropertyFilter.Body = true;
mqSender.MessageReadPropertyFilter.AppSpecific = true;
mqSender.MessageReadPropertyFilter.Priority = true;
mqSender.MessageReadPropertyFilter.Recoverable = true; // 防止重啟主機時丟失消息
mqSender.Formatter = new XmlMessageFormatter(new Type[] { source.GetType() });
if (
transactional == true)
{
using (MessageQueueTransaction tran = new MessageQueueTransaction())
{
tran.Begin();
mqSender.Send(source, tran);
tran.Commit();
}
}
else
mqSender.Send(source);
mqSender.Close();
}
return true;
}
catch (MessageQueueException ex)
{
Console.Write(ex);
}
catch (Exception ex)
{
Console.Write(ex);
}
}
return false;
}
接收消息
public static T GetMessage<T>(string path, bool isDeleteMessage = true, bool t
ransactional = false)
{
T result = default(T);
try
{
using (MessageQueue mqReceiver = new MessageQueue(path))
{
mqReceiver.MessageReadPropertyFilter.Body = true;
mqReceiver.MessageReadPropertyFilter.AppSpecific = true;
mqReceiver.MessageReadPropertyFilter.Priority = true;
mqReceiver.MessageReadPropertyFilter.Recoverable = true; // 防止重啟主機時丟失消息
mqReceiver.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });
Message message = null;
if (
transactional == true)
{
if (isDeleteMessage == true)
{
using (MessageQueueTransaction tran = new MessageQueueTransaction())
{
tran.Begin();
Console.WriteLine("等待接收......");
message = mqReceiver.Receive(tran);
Console.WriteLine("接到了");
tran.Commit();
}
}
else
message = mqReceiver.Peek();
}
else
{
if (isDeleteMessage)
{
Console.WriteLine("等待接收......");
message = mqReceiver.Receive();
Console.WriteLine("接到了");
}
else
message = mqReceiver.Peek();
}
if (message != null)
result = (T)message.Body;
mqReceiver.Close();
}
}
catch (MessageQueueException ex)
{
Console.Write(ex);
}
catch (Exception ex)
{
Console.Write(ex);
}
return result;
}
申:
以下代碼在調試遠端的主機時會出現異常
bool b = MessageQueue.Exists(path); // 按說應該不會,但是我的機器調試時總出錯,不知道為什么?
MessageQuene m = MessageQuene.Create(path);// 不知道,反正沒有通過
bool b = m.Transactional; // 好像在遠端時不支持這個屬性了
以上信息在本地時沒有任何問題,O(∩_∩)O~
一點補充:
以下只在本地有效,不知道遠端是否有效,沒試過
如何得到本地的私有隊列中的消息的數量(主要代碼如下:)
using System.Diagnostics;
return (long)(new PerformanceCounter("MSMQ Queue", "Messages in Queue", path).NextValue());
如果在執行以上代碼時出現注冊表缺少什么等等的 InvalidOperationException 時,請以管理員的方式在 DOS 中執行
命令 "lodctr /R",也可以通過命令 "perfmon" 查看性能計數器的情況
另外說明在 path 中本地機器名要給全例如 @"MyPC\Private$\MyPath",不能用省略符號 @".\Private$\MyPath"替代
在進行MSMQ的編程時,請添加引用 System.Message.dll 並添加對應的命名空間的引用
其實除了 MSMQ ,我們還是有很多其他的選擇的,例如 ActiveMQ 等等,有興趣大家可以看看了......
關於在集群中使用隊列
1、要在集群中使用隊列,請在集群中的每個主機的私有隊列中創建自己的隊列(最好創建事務性隊列),例如我的集群中包含兩台主機 192.168.117.47、192.168.117.48,共同的漂移地址是192.168.117.50,那我就在每台主機的 MSMQ 的私有隊列中分別創建 \private$\MyPath (創建時指定帶有事務)
2、在創建隊列之后,請在隊列的屬性中指定用戶及該用戶對隊列的訪問權限,否則訪問隊列的客戶端程序將不能正確的發送和接收隊列。
3、發送消息時,要使用漂移地址 192.168.117.50 發送,
同時指定發送消息時要
啟用事務
4、接收消息時,請使用單機的 IP 192.168.117.47 或 192.168.117.48 接收隊列,同時,請
不要指定接收消息的隊列啟用事務,嘿嘿,這里是不是和不在集群時的情況有些不同,同時也和發送消息有些不一樣呢?!
5、之前給出的代碼是采用的格式化是 XmlMessageFormatter,這就要求在接收消息時必須要知道消息中對象的類型,如果我們在發送和接收消息時指定 MessageQueue.Formatter = new BinaryMessageFormatter(),則可以在發送和接收消息時不用知道消息中包含的對象的類型了
之前的接收都是同步接收消息,有沒有辦法來異步獲取消息呢?當然可以,代碼如下:
主調方代碼:
MessageQueue mq = GetMessageQueue(path); // GetMessageQueue 函數如何實現就不寫了吧?!
mq.ReceiveCompleted += new ReceiveCompletedEventHandler(mq_ReceiveCompleted);
mq.BeginReceive();
回調函數
private void
mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
{
MessageQueue mq = sender as MessageQueue;
if (mq != null && e != null)
{
Message message = mq.EndReceive(e.AsyncResult);
if (message != null)
Console.WriteLine(message.Body);
mq.BeginReceive();
}
}
這是接收消息之后就刪除的代碼,當然也可以做接收消息但不刪除的,這里就不再熬訴了
