[C#.NET][WCF] 接收 MSMQ DeadLetter

[C#.NET][WCF] 接收 MSMQ DeadLetter

當Client 發送 Queue 失敗或是 Server 接收太慢都可以透過以下屬性定義,來處理"死訊息",架構圖如下:
image
 
以下列出比較重要的屬性做為記錄:
CustomDeadLetterQueue:指定特定位置。

DeadLetterQueue:列舉狀態。

None :不使用 DeadLetterQueue

System :使用系統定義的Queue

Custom :使用特定位置

TimeToLive:Queue 存放時間。
設定畫面如下圖:
SNAGHTML11886924
 
接收 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 確實有被觸發,至於後續要怎麼處理就自行發揮了

SNAGHTML11b55a93

 

3.把 Client 專案裡的 ServiceHost 註解掉,觀察 Dead Letter

image

 

Queue 的訊息跟一般的不一樣,這是由 WCF 幫我們處理的訊息

SNAGHTML11b902ab

 

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

Image result for microsoft+mvp+logo