[C#.NET][WCF] 接收 MSMQ DeadLetter
當Client 發送 Queue 失敗或是 Server 接收太慢都可以透過以下屬性定義,來處理"死訊息",架構圖如下:
以下列出比較重要的屬性做為記錄:
CustomDeadLetterQueue:指定特定位置。
DeadLetterQueue:列舉狀態。
None :不使用 DeadLetterQueue
System :使用系統定義的Queue
Custom :使用特定位置
TimeToLive:Queue 存放時間。
設定畫面如下圖:
接收 Dead Letter,這是本篇的重點
1.定義 AddressFilterMode 特性,用來處理 Dead Letter
2.利用 DeliveryFailure and DeliveryStatus 來判斷 Dead Letter 狀態
public class dead : IService1 { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void SendUser(User request) { MsmqMessageProperty property = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty; if (property.DeliveryFailure == DeliveryFailure.ReachQueueTimeout || property.DeliveryFailure == DeliveryFailure.ReceiveTimeout) { Console.WriteLine("發送逾時,重送 : {0}", request); //TODO:做想做的事 } } }
片段程式碼
@Service,用來接收 @".\private$\service 的 queue
{ internal class servic { private static void Main(string[] args) { string queueName = @".\private$\service"; if (!MessageQueue.Exists(queueName)) MessageQueue.Create(queueName, true); ServiceHost serviceHost = new ServiceHost(typeof(Service1)); try { serviceHost.Open(); //start listening Console.WriteLine("The service is ready."); Console.WriteLine("Press <ENTER> to terminate service."); Console.ReadLine(); } catch (Exception) { serviceHost.Abort(); } finally { serviceHost.Close(); } } } public class Service1 : IService1 { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void SendUser(User request) { Console.WriteLine("Receive: {0} ", request); } } }Service Xml<configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="serviceBehavior0"> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <netMsmqBinding> <binding name="deal" deadLetterQueue="System" timeToLive="00:00:05"> <security mode="None" /> </binding> </netMsmqBinding> </bindings> <services> <service behaviorConfiguration="serviceBehavior0" name="Service.Service1"> <endpoint address="net.msmq://localhost/private/service" binding="netMsmqBinding" bindingConfiguration="deal" contract="Contract.IService1" /> <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://YAO-WIN8:8888/service" /> </baseAddresses> </host> </service> </services> </system.serviceModel> </configuration>
@Client :
1.接收 .\private$\service\dead 的 queue
2.發送訊息至 .\private$\service\
{ internal class client { private static void Main(string[] args) { string dealName = @".\private$\service/dead"; if (!MessageQueue.Exists(dealName)) MessageQueue.Create(dealName, true); var client = new Service1Client(); var user = new User() { Name = "余小章", Age = 28, ID = Guid.NewGuid() }; using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { client.SendUser(user); scope.Complete(); } Console.WriteLine("Send : {0}", user); ServiceHost host = new ServiceHost(typeof(dead)); try { host.Open(); Console.WriteLine("The DealLetter service is ready."); Console.ReadLine(); } catch (Exception ex) { host.Abort(); } finally { host.Close(); } } } [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, AddressFilterMode = AddressFilterMode.Any)] public class dead : Contract.IService1 { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public void SendUser(User request) { MsmqMessageProperty property = OperationContext.Current.IncomingMessageProperties[MsmqMessageProperty.Name] as MsmqMessageProperty; if (property.DeliveryFailure == DeliveryFailure.ReachQueueTimeout || property.DeliveryFailure == DeliveryFailure.ReceiveTimeout) { Console.WriteLine("發送逾時,重送 : {0}", request); //TODO:做想做的事 } } public Task SendUserAsync(User request) { throw new NotImplementedException(); } } }
Client Xml
<configuration> <system.serviceModel> <services> <service name="Client.dead"> <endpoint address="net.msmq://localhost/private/service/dead" binding="netMsmqBinding" bindingConfiguration="Service.NoSecurity" contract="Contract.IService1" /> </service> </services> <bindings> <netMsmqBinding> <binding name="Client.NoSecurity" customDeadLetterQueue="net.msmq://localhost/private/service/dead" deadLetterQueue="Custom" timeToLive="00:00:05"> <security mode="None" /> </binding> <binding name="Service.NoSecurity"> <security mode="None" /> </binding> </netMsmqBinding> </bindings> <client> <endpoint address="net.msmq://localhost/private/service" binding="netMsmqBinding" bindingConfiguration="Client.NoSecurity" contract="Service.Proxy.IService1" name="Client.NetMsmqBinding" /> </client> </system.serviceModel> </configuration>
演練步驟
1.執行 Service.exe,然後關掉
2.執行 Client.exe,接收 Dead Letter 確實有被觸發,至於後續要怎麼處理就自行發揮了
3.把 Client 專案裡的 ServiceHost 註解掉,觀察 Dead Letter
Queue 的訊息跟一般的不一樣,這是由 WCF 幫我們處理的訊息
Note.專案或 Service.exe 必須要使用管理員身份執行
文章出自:http://www.dotblogs.com.tw/yc421206/archive/2013/10/28/125942.aspx
範例下載:https://dotblogsfile.blob.core.windows.net/user/yc421206/1310/201310281997244.zip
參考文章:http://msdn.microsoft.com/zh-tw/library/ms752268%28v=vs.110%29.aspx
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET