ASP.NETでのメッセージ処理(MSMQ)3


本明細書の例のソースコードまたは素材のダウンロード
本文の前の2つの文章の中でMSMQの関連知識点について紹介して、多くの前の2つの文章を読んだことがある友达はすべてこのようないくつかの問題を聞いたことがあります:
1、どのようにMSMQを実際のプロジェクトに応用しますか?
2、実際の応用例を紹介していただけませんか.
  3、......
前の2つの文章では、MSMQでよく使われる技術点について基本的に紹介しましたが、本稿では主にMSオープンソースプロジェクトPetShopにおけるMSMQアプリケーションを例にMSMQの実際のプロジェクトでの応用を紹介します.PetShopでは,システムがマルチスレッドの専用アプリケーションを用いてメッセージキューを監視するため,PetShopアプリケーション解析に入る前に,マルチスレッドとMSMQに関する知識点を理解する.
一、マルチスレッドとMSMQ
指定されたメッセージキューにメッセージデータがあるかどうかにかかわらず、マルチスレッドでこのキューを監視する必要がありますが、キュー内のデータが変化すると、メッセージを読み出すなどの処理が行われます.この要件に基づいて、キューをマルチスレッドで監視し、キューにメッセージデータがある場合はメッセージを読み出し、ない場合はキューを監視し、キューデータが変更された場合(新しいメッセージが追加された場合)はソース(メッセージの読み出し)を行う例を示します.
まず、スレッド数を格納するためのスレッド配列を定義します.1static private int ThreadNumber = 5; //5
2static private Thread[] ThreadArray = new Thread[ThreadNumber];

起動する必要があるスレッドをThreadArray配列にロードし,そのためのスレッドを1つの遍歴配列で起動するが,実際にはここには5つのスレッドしかない.1private void button1_Click(object sender, EventArgs e)
2{
3  StartThreads();
4}
5
6private void StartThreads()
7{
8  int counter; //
9  for (counter = 0; counter < ThreadNumber; counter++)
10  {
11    ThreadArray[counter] = new Thread(new ThreadStart(MSMQListen));
12    ThreadArray[counter].Start();
13    this.richTextBox2.Text += (counter + 1).ToString() + " !";
14  }
15}
16
17private void MSMQListen()
18{
19  while (true)
20  {
21    //
22    MessageBox.Show(MsgQueue.ReceiveMessage());
23  }
24}

 
上記のコードを読むと問題がある場合は、まずマルチスレッドに関する知識点を理解することをお勧めします.StartThreadsメソッドで配列に格納されているスレッドを起動し、MSMQListenメソッドに処理を依頼します.MSMQListenメソッドが完了したのはキュー内のメッセージを読み取ることです.ここでは2番目の記事で使用するMsgQueueクラスとBookクラスを使用します.詳細は2番目の記事ASP.NETではメッセージ処理(MSMQ)2を行う.
指定したメッセージキューを監視するために5つのスレッドが起動しました.では、スレッドが応答するかどうかを確認するために、キューにメッセージを送信することでテストします.上からスレッドを起動するコードから,キューにメッセージがマルチスレッドの監視下にある限り,スレッドはキュー内のメッセージを読み出すことが明らかになった.1private void button3_Click(object sender, EventArgs e)
2{
3  Book book = new Book();
4  book.BookId = 1;
5  book.BookName = "asp.net";
6  book.BookAuthor = "abcd";
7  book.BookPrice = 50.80;
8
9  MsgQueue.SendMessage(book);
10}

では、ここのテストでは、Bookオブジェクトメッセージをキューに送信します.上の分析によると、マルチスレッドは同時にこのメッセージを読み出します.
ほほほ、テストの結果が出て、このテストは私たちが前に提出した需要に達したようです.!OK、MSMQとマルチスレッドについて簡単に紹介します.
 
二、MSMQの高原開発プロジェクトPetShopにおける応用分析.
PetShop 4.0では、頻繁にデータベースにアクセスするための操作を回避するために、メッセージキューを使用して挿入するデータを一時的に格納します.キュー内のメッセージは、システムの専用アプリケーションが処理するのを待って、最後にデータベースにデータを挿入します.
PetShop 4.0のメッセージ処理は、主に以下の大部分に分けられる:受注ポリシーインタフェースIOrderStategy、メッセージインタフェースIMessageing、メッセージファクトリMessageFactory、MSMQ実装MSMQMessaging、バックグラウンド処理アプリケーションOrderProessor.次の図を示します.
1、オーダーポリシーインタフェースIOrderStategy
PetShop 4.0のアーキテクチャは非常に膨大であり、受注処理には2つの処理ポリシーがあり、ここでもポリシーモデルの応用であり、IOrderStrategyインタフェースは受注ポリシーの上層抽象として、異なる受注処理を実現する具体的なポリシーとして実現され、UMLは以下の通りである.
  
概略コード:1namespace PetShop.IBLLStrategy
2{
3  public interface IOrderStrategy
4  {
5    void Insert(OrderInfo order);
6  }
7}
8
9namespace PetShop.BLL
10{
11  public class OrderSynchronous:IOrderStrategy
12  {
13    private static readonly IOrder asynchOrder = QueueAccess.CreateOrder();
14
15    public void Insert(OrderInfo order)
16    {
17      asynchOrder.Send(order);
18    }
19  }
20}
21
22//

 
上のUMLとコードから分かるように、受注ポリシーインタフェースには、抽象ファクトリモードを使用して対応する受注ポリシーオブジェクトの作成を完了する2つの実装があります.この点については後述の情報工場部分で紹介しますが、ここでは説明しません.
2、メッセージインタフェースIMessageing
PetShop 4.0では、受注処理に非同期処理が使用されているため、メッセージインタフェースにはIOrderインタフェースが1つしか定義されていない.IOrderインタフェースの定義はMSMQの実装と一致しており、送信および受信動作を提供する必要がある.Sendメソッドでは,パラメータはデータアクセス層のデータエンティティオブジェクト(OrderInfo)であり,具体的な実装はMSMQの実装クラス(PetShop.MSMQMessaging.Order)で行う.
        
MSの開発者は本当に何でも考えることができ、メッセージインタフェースの実現に全面的に考慮されており、将来の拡張を避けるために他のデータオブジェクトもMSMQに使用される.したがって、PetShop 4.0におけるメッセージインタフェース実装では、キューのベースクラス(PetShopQueue)が定義され、メッセージの送信(Send)および受信(Receive)メソッドの基本動作が実現される.コードは次のとおりです.1namespace PetShop.MSMQMessaging
2{
3  public class PetShopQueue:IDisposable
4  {
5    //
6    protected MessageQueueTransactionType transactionType = MessageQueueTransactionType.Automatic;
7    protected MessageQueue queue; //
8    protected TimeSpan timeout;  //
9
10    public PetShopQueue(string queuePath, int timeoutSeconds)
11    {
12      queue = new MessageQueue(queuePath); // quueuPath
13      timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeoutSeconds));
14
15      queue.DefaultPropertiesToSend.AttachSenderId = false;
16      queue.DefaultPropertiesToSend.UseAuthentication = false;
17      queue.DefaultPropertiesToSend.UseEncryption = false;
18      queue.DefaultPropertiesToSend.AcknowledgeType = AcknowledgeTypes.None;
19      queue.DefaultPropertiesToSend.UseJournalQueue = false;
20    }
21
22    /**//// <summary>
23    ///
24    /// </summary>
25    public virtual object Receive()
26    {
27      try
28      {
29        using (Message message = queue.Receive(timeout, transactionType))
30          return message;
31      }
32      catch (MessageQueueException mqex)
33      {
34        if (mqex.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout)
35          throw new TimeoutException();
36        throw;
37      }
38    }
39
40    /**//// <summary>
41    ///
42    /// </summary>
43    public virtual void Send(object msg)
44    {
45      queue.Send(msg, transactionType);
46    }
47
48    IDisposable #region IDisposable
49    public void Dispose()
50    {
51      queue.Dispose(); //
52    }
53    #endregion
54  }
55}

 
MSMQキューは永続的なキューであり、ユーザーの注文によってデータが失われることはありません.Queueは、データを格納するキューとしてメッセージキュー(MessageQueue)タイプ、PetShopQueueにtimeout値を設定し、バックグラウンド処理アプリケーション(OrderProessor)は、timeoutの値に基づいてキュー内の受注数を定期的にスキャンします.
3、メッセージ工場MessageFactory
IOrderの実装によって異なるポリシーが変更されることを考慮したのか、PetShopでは抽象ファクトリモードを利用し、IOrderオブジェクトの作成を専門のファクトリモジュール(MessageFactory)でカプセル化し、以下のように定義している.1namespace PetShop.MessagingFactory
2{
3  /**//// <summary>
4  /// This class is implemented following the Abstract Factory pattern to create the Order
5  /// Messaging implementation specified from the configuration file
6  /// </summary>
7  public sealed class QueueAccess
8  {
9    //<add key="OrderMessaging" value="PetShop.MSMQMessaging"/>
10    private static readonly string path = "PetShop.MSMQMessaging";
11
12    /**//// <summary>
13    /// , new
14    /// </summary>
15    private QueueAccess()
16    { }
17
18    public static IOrder CreateOrder()
19    {
20      string className = path + ".Order";
21      return (IOrder)Assembly.Load(path).CreateInstance(className);
22    }
23  }
24}

 
QueueAccessクラスでは,CreateOrder法により反射技術を用いて正しいIOrder型オブジェクトを作成する(実際にはインタフェースの具体的な実装クラスを作成したオブジェクトであり,マルチステートの原理と反射技術を適用した).UML図の下:
  
PetShop 4.0では、メッセージインタフェースの具体的な実装は、プロファイルによってwebに定義される.configでは:
  
ここでは、より直感的なプレゼンテーションと紹介のためにpathを硬化定義し、以下のようにします.
  private static readonly string path = "PetShop.MSMQMessaging";
ここでは、PetShopのようなビジネスロジック層の注文処理ポリシーの呼び出しを容易にするために、ファクトリモードを用いてオブジェクトの作成を担当する.BLLモジュールのOrderSynchronousクラス:1namespace PetShop.BLL
2{
3  public class OrderSynchronous:IOrderStrategy
4  {
5    private static readonly IOrder asynchOrder = QueueAccess.CreateOrder();
6
7    public void Insert(OrderInfo order)
8    {
9      asynchOrder.Send(order);
10    }
11  }
12}

このようにIOrderインタフェースの実装が変化すると,プロファイルを修正するだけでOKとなり,システム全体が柔軟で安定している.
4、MSMQはMSMQMessagingを実現する
PetShopでMSMQMessagingモジュールでは,受注オブジェクトがメッセージインタフェース(IMessaging)モジュールのIOrderを実現するとともに,ベースクラスPetShopQueueを継承する.次のように定義します.1namespace PetShop.MSMQMessaging
2{
3  public class Order:PetShopQueue,IOrder
4  {
5    private static readonly string queuePath = ConfigurationManager.AppSettings["OrderQueuePath"];
6    private static int queueTimeout = 20; //20
7
8    public Order()
9      : base(queuePath, queueTimeout)
10    {
11      queue.Formatter = new BinaryMessageFormatter();
12      Console.WriteLine(queuePath);
13    }
14
15    public new OrderInfo Receive()
16    {
17      // , Automatic 。
18      base.transactionType = MessageQueueTransactionType.Automatic;
19      return (OrderInfo)((Message)base.Receive()).Body;
20    }
21
22    public OrderInfo Receive(int timeout)
23    {
24      base.timeout = TimeSpan.FromSeconds(Convert.ToDouble(timeout));
25      return Receive();
26    }
27
28    /**//// <summary>
29    ///
30    /// </summary>
31    /// <param name="orderMessage"></param>
32    public void Send(OrderInfo orderMessage)
33    {
34      // , Single 。
35      base.transactionType = MessageQueueTransactionType.Single;
36      base.Send(orderMessage);
37    }
38  }
39}

 
UMLスケッチ:
ここで、OrderクラスはベースクラスPetShopQueueを継承するとともにインタフェースIOrderも実現するが、メッセージインタフェースとベースクラスで定義された受信メッセージ(Receive)メソッドはメソッドの署名において同じである.したがって、OrderのReceiveメソッド実装では、overrideキーワードではなくnewを使用して親PetShopQueueのReceive虚メソッドを書き換える必要があります.この場合、OrderクラスのReceiveメソッドは、メッセージインタフェースIOrderにおけるReceiveメソッドを実現する2つの意味を表す.二つ目は,newキーワードを用いて親クラスPetShopQueueを書き換えたReceive虚メソッドである.
PetShop 4.0では,オブジェクト向けの知識点を非常に精巧に適用しているが,このように分析した場合,我々はどのようにOrderを呼び出すことができるだろうか.そうですか.1//1、 PetShopQueue
2PetShopQueue order = new Order();
3order.Receive();
4
5//2、 IOrder
6IOrder order = new Order();
7order.Receive();
多態原理によれば、上記の2つの実現は正しいが、私たちは誰を捨てているのだろうか.PetShop 4.0の正しい呼び出しは2つ目の方法で、この呼び出し方法も「インタフェース向け設計」の原則に合っています.----詳細については、MessageFactoryセクションの説明を参照してください.
5、バックグラウンド処理アプリケーションOrderProessor
前の一連の操作は、最終的にはここに進み、最終的にデータベースを挿入する操作を実現します.PetShop 4.0ではOrderProessorはコンソールアプリケーションであり、必要に応じてWindows Serviceとして設計することもできます.彼が完了した操作は、メッセージキューの受注データを受信し、データベースに挿入することです.OrderProessorではマルチスレッド技術を用い,キュー内の注文情報を監視し,定期的に処理する.メインメソッドMainメソッドでは制御線プログラムに用いられ,コアの実行はProcessOrdersメソッドに委任されて実現される.
  1private static void ProcessOrders()
2{
3  TimeSpan tsTimeout = TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout * batchSize));
4  Order order = new Order(); // PetShop.BLL.Order
5  while (true)
6  {
7    TimeSpan datatimeStarting = new TimeSpan(DateTime.Now.Ticks);
8    double elapsedTime = 0;
9
10    int processedItems = 0;
11    ArrayList queueOrders = new ArrayList();
12
13    //
14    using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tsTimeout))
15    {
16      //
17      for (int i = 0; i < batchSize; i++)
18      {
19        try
20        {
21          //
22          if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds)
23          {
24            queueOrders.Add(order.ReceiveFromQueue(queueTimeout));
25          }
26          else
27          {
28            i = batchSize; //
29          }
30          elapsedTime = new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datatimeStarting.TotalSeconds;
31        }
32        catch (TimeoutException)
33        {
34          //
35          i = batchSize;
36        }
37      }
38
39      //
40      for (int k = 0; k < queueOrders.Count; k++)
41      {
42        order.Insert((OrderInfo)queueOrders[k]);
43        processedItems++;
44        totalOrdersProcessed++;
45      }
46      //
47      ts.Complete();
48    }
49     Console.WriteLine("(Thread Id " + Thread.CurrentThread.ManagedThreadId + ") batch finished, " + processedItems + " items, in " + elapsedTime.ToString() + " seconds.");
50  }
51} 

 
ProcessOrders法はまずビジネスロジック層PetShopを通過する.BLL.OrderクラスのメソッドReceiveFromQueueは、メッセージ・キュー内の受注データを取得し、ArrayListオブジェクトに挿入してデータベースに挿入します.OrderProcessorの完全なコード定義は次のとおりです.
  Code  1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Configuration;
 5using System.Threading;
 6using PetShop.BLL;
 7using System.Collections;
 8using System.Transactions;
 9using PetShop.Model;
10
11namespace PetShop.OrderProcessor
12{
13  class Program
14  {
15    private static int transactionTimeout = int.Parse(ConfigurationManager.AppSettings["TransactionTimeout"]);
16    private static int queueTimeout = int.Parse(ConfigurationManager.AppSettings["QueueTimeout"]);
17    private static int batchSize = int.Parse(ConfigurationManager.AppSettings["BatchSize"]);
18    private static int threadCount = int.Parse(ConfigurationManager.AppSettings["ThreadCount"]);
19    private static int totalOrdersProcessed = 0;
20
21    static void Main(string[] args)
22    {
23      Thread workTicketThread;
24      Thread[] workerThreads = new Thread[threadCount];
25
26      for (int i = 0; i < threadCount; i++)
27      {
28        workTicketThread = new Thread(new ThreadStart(ProcessOrders));
29        //
30        workTicketThread.IsBackground = true;
31        workTicketThread.SetApartmentState(ApartmentState.STA);
32
33        workTicketThread.Start();
34        workerThreads[i] = workTicketThread;
35      }
36
37      Console.WriteLine(" , .");
38      Console.ReadLine();
39      Console.WriteLine(" , ");
40
41      //
42      for (int i = 0; i < workerThreads.Length; i++)
43      {
44        workerThreads[i].Abort();
45      }
46
47      Console.WriteLine();
48      Console.WriteLine(totalOrdersProcessed + " .");
49      Console.WriteLine(" . ");
50      Console.ReadLine();
51    }
52
53    private static void ProcessOrders()
54    {
55      TimeSpan tsTimeout = TimeSpan.FromSeconds(Convert.ToDouble(transactionTimeout * batchSize));
56      Order order = new Order(); // PetShop.BLL.Order
57      while (true)
58      {
59        TimeSpan datatimeStarting = new TimeSpan(DateTime.Now.Ticks);
60        double elapsedTime = 0;
61
62        int processedItems = 0;
63        ArrayList queueOrders = new ArrayList();
64
65        //
66        using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required, tsTimeout))
67        {
68          //
69          for (int i = 0; i < batchSize; i++)
70          {
71            try
72            {
73              //
74              if ((elapsedTime + queueTimeout + transactionTimeout) < tsTimeout.TotalSeconds)
75              {
76                queueOrders.Add(order.ReceiveFromQueue(queueTimeout));
77              }
78              else
79              {
80                i = batchSize; //
81              }
82              elapsedTime = new TimeSpan(DateTime.Now.Ticks).TotalSeconds - datatimeStarting.TotalSeconds;
83            }
84            catch (TimeoutException)
85            {
86              //
87              i = batchSize;
88            }
89          }
90
91          //
92          for (int k = 0; k < queueOrders.Count; k++)
93          {
94            order.Insert((OrderInfo)queueOrders[k]);
95            processedItems++;
96            totalOrdersProcessed++;
97          }
98          //
99          ts.Complete();
100        }
101         Console.WriteLine("(Thread Id " + Thread.CurrentThread.ManagedThreadId + ") batch finished, " + processedItems + " items, in " + elapsedTime.ToString() + " seconds.");
102      }
103    }
104  }
105}
106

MSMQ技術は非同期処理に加えて、分散処理技術としても応用できる.MSMQによる分散処理に関する内容については、本人の能力に限りがありますので、関連資料をご覧ください.
本文はMSMQとマルチスレッドとPetShop 4.0の中でMSMQの応用に対して分析を行って、前に私が書いた2編のMSMQに関する知識点の紹介文章を結びつけて、MSMQに対して1つの全面的な認識を創立して、この3編の文章がみんなにMSMQを学ぶことに役立つことを望みます.