利用MSMQ傳送訊息(物件)到NServiceBus終結點(不採用Send-Only方式)
待解決的問題:
某些應用程式需要非同步傳送訊息到遠端機器的NServiceBus終結點,但是又不希望知道接收並處理非同步訊息的是NServiceBus這種ESB終結點,也許非同步訊息處理者將來會換成其他的ESB終結點,所以不能採用Send-Only的方式,否則應用程式(訊息傳送方)還是需要引入NserviceBus.dll等類庫。
所以我的思路是:
應用程式呼叫我的介面傳送訊息到指定的私有訊息佇列,然後NServiceBus終結點有一個執行緒,接收這樣一個訊息,這個訊息沒有實現IComman/Imessage介面,所以還要再將這個訊息轉換為NServiceBus能夠處理的訊息型別(實現ICommand,IMessage等介面,可以出發MessageHandler呼叫),然後將這個訊息通過:
Bus.Send("EndPointName@localhost", message);
傳送給本機的NServiceBus終結點。
後面會展示詳細的程式碼。。。。
其實我還想過提供給應用程式的介面是一個WCF服務,服務的實現中採用Send-Only的方式將接收到的訊息轉換為NServiceBus能夠識別的訊息,然後採用Send方式傳送到本機的NServiceBus終結點,但是我試了一下,WCF服務的實現中配置Send-Only方式那段程式碼會丟擲一個很奇怪的異常,到現在我還沒有分析出來為什麼。
所以綜上所述,提供給應用程式一個介面,將訊息傳送到我的ESB終結點,同時又沒有任何耦合,我們採用的方式是直接操作訊息佇列,程式碼如下(關鍵片段),如要下載我的完整程式碼,地址為百度雲盤:
提供給應用程式的介面,包括訊息定義,和訊息傳送介面:
namespace MSMQMessageSender
{
[Serializable]
public class AsynMessage
{
public AsynMessage() { }
public int id { set; get; }
public string body { set; get; }
}
}
public static bool SendMessage(AsynMessage msg)
{
try
{
MessageQueue msmq = new MessageQueue(@"FormatName:Direct=TCP:192.1.11.186\Private$\NServiceBus.EndPoint"); //這裡的訊息佇列地址是能夠將訊息傳送到遠端機器的訊息佇列的關鍵。
msmq.Formatter = new XmlMessageFormatter(new Type[] { typeof(AsynMessage) });
System.Messaging.Message message = new System.Messaging.Message();
message.Label = "訊息標題";
message.Body = msg;
msmq.Send(message);
}
catch (Exception ee)
{
Console.WriteLine("傳送訊息出現異常,原因是:" + ee.Message);
return false;
}
return true;
}
下面是NserviceBus中,首先是啟動的時候需要建立訊息佇列:
namespace CBIP.Server
{
class EndConfigPoint : IConfigureThisEndpoint, AsA_Publisher { }
class ConfiguringTheDistributorWithTheFluentApi : INeedInitialization
{
public void Init()
{
if (!MessageQueue.Exists(@".\Private$\NServiceBus.EndPoint"))
{
System.Messaging.MessageQueue.Create(@".\Private$\NServiceBus.EndPoint");
}
}
}
}
然後定義NserviceBus中能夠識別的訊息:
namespace NServiceBus.Messages
{
[Serializable]
public class CBIPAsynMessage : ICommand
{
public int id { get; set; }
public string body { get; set; }
}
}
然後是在NServiceBus的Start()方法中開啟一個輪訓執行緒,輪訓訊息佇列中的訊息:
namespace CBIP.Server
{
public class ServerEndPoint : IWantToRunWhenBusStartsAndStops
{
public IBus Bus { get; set; }
public void Start()
{
Thread thread = new Thread(MyThread);
thread.Start();
}
public void MyThread()
{
while (true)
{
MessageQueue MQ = new MessageQueue(@".\Private$\NServiceBus.EndPoint");
//呼叫MessageQueue的Receive方法接收訊息
System.Messaging.Message message = null;
try
{
message = MQ.Receive(TimeSpan.FromSeconds(5));
}
catch (MessageQueueException ee)
{
message = null;
Console.WriteLine("超時:" + ee.Message);
}
if (message != null)
{
message.Formatter = new XmlMessageFormatter(new Type[] { typeof(MSMQMessageSender.AsynMessage) });
AsynMessage msg = (AsynMessage)message.Body;
Console.WriteLine(msg.id + ", " + msg.body);
CBIPAsynMessage cbipMsg = new CBIPAsynMessage() //轉換成NServiceBus能夠識別的訊息
{
id = msg.id,
body = msg.body
};
Bus.Send("CBIP.Server@localhost", cbipMsg); //傳送到本機NServiceBus節點
Console.WriteLine("傳送到ESB完成");
}
else
{
Console.WriteLine("沒有找到訊息!");
}
}
}
public void Stop()
{
}
}
}
就這樣在NServiceBus的訊息處理者中就能正確收到訊息了:
namespace CBIP.Server
{
public class AsynMessageHandler : IHandleMessages<CBIPAsynMessage>
{
public void Handle(CBIPAsynMessage message)
{
Console.WriteLine("AsynMessageHandler收到一個訊息");
}
}
}
直接以程式的方式啟動NServiceBus終結點,上面的程式碼是可以正確工作的,但是當我將我的NServiceBus終結點安裝位Windows NT服務之後,問題來了,輪詢執行緒無論如何也收不到訊息,然後我去檢視訊息佇列NServiceBus.EndPoint,發現裡面是有訊息的,那就是輪詢執行緒沒辦法取出來。
後來除錯才發現時沒有訪問許可權,錯誤是“Access to Message Queuing System id denied”
網上查了一下是需要在建立訊息佇列的時候,給指定使用者配置訪問許可權的,NServiceBus中建立佇列的程式碼改為下面這樣就可以了:
if (!MessageQueue.Exists(@".\Private$\NServiceBus.EndPoint"))
{
MessageQueue mq = System.Messaging.MessageQueue.Create(@".\Private$\NServiceBus.EndPoint");
mq.SetPermissions("Administrator", MessageQueueAccessRights.FullControl);
}
======================================= 華麗的分割線 ==============================
另外,正常來說,NserviceBus的Master終結點安裝了NServiceBus的軟體,有實現了IConfigureThisEndpoint, AsA_Publisher { }這兩個介面的類,有訊息處理者,寄宿到NServiceBus.Host.exe,填寫好命令列引數NServiceBus.Integration和NServiceBus.Master,並且電腦安裝了訊息佇列之後,是可以沒有問題地啟動起來,並自動建立訊息佇列的,我的專案中開始也是這樣,但是不知道為什麼一段時間之後啟動報錯:“無法建立訊息佇列,或者沒有對應許可權”。
這個問題還沒找到原因,目前採用的辦法只能是顯示判斷是否存在訊息佇列,不存在就顯示建立一個。
相關文章
- 以事務方式傳送 Kafka 訊息Kafka
- RocketMQ(6)---傳送普通訊息(三種方式)MQ
- laravel中使用利用訊息佇列傳送郵件Laravel佇列
- Rocket MQ傳送訊息的三種方式初析MQ
- 訊息中介軟體—RocketMQ訊息傳送MQ
- 【RocketMQ】MQ訊息傳送MQ
- RocketMQ(八):訊息傳送MQ
- iOS 傳送位置訊息iOS
- 鴻蒙傳送訊息通知鴻蒙
- 多種訊息傳送機制,處理合適不??
- 用程式碼理解 ObjC 中的傳送訊息和訊息轉發OBJ
- 用程式碼理解ObjC中的傳送訊息和訊息轉發OBJ
- RocketMQ(九):訊息傳送(續)MQ
- TNW-傳送模板訊息TNW
- 6-RocketMQ傳送訊息MQ
- php ActiveMQ的傳送訊息,與處理訊息PHPMQ
- 理解TON合約中的訊息傳送結構
- 使用C#在應用程式間傳送訊息C#
- RocketMQ中Producer訊息的傳送MQ
- 傳送不同型別的訊息型別
- RocketMQ -- 訊息傳送儲存流程MQ
- Kafka -- 訊息傳送儲存流程Kafka
- 快速向 Google Chat 傳送訊息Go
- iOS 給父類傳送訊息iOS
- 小程式傳送訂閱訊息
- 使用 laravel-wechat-notification 傳送微信模板訊息、企業微信應用訊息Laravel
- 從 runtime 原始碼解析物件傳送訊息的動態性原始碼物件
- Runtime備忘-訊息傳送流程
- 分散式事務:訊息可靠傳送分散式
- django+小程式傳送模板訊息Django
- Python呼叫飛書傳送訊息Python
- WIN32傳送自定義訊息Win32
- 傳送kafka訊息的shell指令碼Kafka指令碼
- 一張圖進階 RocketMQ - 訊息傳送MQ
- RocketMQ - 生產者訊息傳送流程MQ
- Laravel 的訊息通知 怎麼採用佇列的方式推送Laravel佇列
- Laravel 佇列訊息與傳送郵件Laravel佇列
- Android Handler 訊息傳送效能優化Android優化