首頁 > 後端開發 > C#.Net教程 > C#訊息佇列應用程式 -2

C#訊息佇列應用程式 -2

黄舟
發布: 2016-12-17 16:59:10
原創
1296 人瀏覽過

在這個陣列內部,CWorker 類別建立了 CWorkerThread類別的實作版
本。 CWorkerThread 類別(將在下面討論)是一個必須繼承的抽象類別。導出
類別定義了訊息的處理方式:
aThreads = new ArrayList();
for (int idx=0; idx〈sfWorker.NumberThreads; idx++)
{
  WorkerThreadFormatter wfThread = new WorkerThreadFormatter();
  wfThread.PRocessName = sfWorker.ProcessName;
  wfThread.ProcessDesc = sfWorker.ProcessDesc;
  wfThread.ThreadNumber = idx;
  wfThread.InputQueue = sfWorker.InputQueue;
  wfThread.ErrorQueue = sfWorker.ErrorQueue;
  wfThread.OutputName = sfWorker.OutputName;
  // 定義輔助類型,並將其插入輔助執行緒結構
  CWorkerThread wtBase;
  switch (sfWorker.ProcessType)
  {
   case WorkerFormatter.SFProcessType.ProcessRoundRobin:
     wtBase = new CWorkerThreadRoundRobin(this, wfThread);
     break;
   case WorkerFormatter.SFProcessType.ProcessAppSpecific:
     wtBase = new CWorkerThreadAppSpecific(this, wfThread);
     break;
   case WorkerFormatter.SFProcessType.ProcessAssembly:
     wtBase = new CWorkerThreadAssembly(this, wfThread);
     break;
   default:
     throw new Exception("Unknown Processing Type");
  }
  // 新增對陣列的呼叫
  aThreads.Insert(idx, wtBase);
}

  一旦所有的物件都已創建,就可以透過呼叫每個執行緒物件的 Start方
法來啟動它們:
foreach(CWorkerThreadeach cThread in aThreads)
  cThread.Start();

  Stop、Pause 和 Continue 方法在 foreach循環裡執行的操作類似。
Stop方法有如下的垃圾收集操作:
GC.SuppressFinalize(this);

  在類別析構函數中將呼叫 Stop 方法,這樣,在沒有明確呼叫 Stop 方
法的情況下也可以正確地終止物件。如果呼叫了 Stop 方法,將不需要析
構函數。 SuppressFinalize方法能夠防止呼叫物件的 Finalize 方法(析 

構函數的實際實作)。

CWorkerThread 抽象類別

  CWorkerThread 是 CWorkerThreadAppSpecifc、CWorkerThread
RoundRobin 和 CWorkerThreadAssembly繼承的抽象類別。無論如何處理消
息,隊列的大部分處理是相同的,所以 CWorkerThread類別提供了這項功能。
這個類別提供了抽象方法(必須被實際方法取代)以管理資源和處理訊息。

  類別的工作再一次透過 Start、Stop、Pause 和 Continue 方法來實現。
在 Start方法中引用了輸入和錯誤佇列。在 .NET 框架中,訊息由 System.
Messaging 名稱空間處理:
// 嘗試開啟佇列,並設定預設的讀寫屬性
MessageQueue mqInput = new MessageQueue(sInputQueue);
mqInput.MessageReadPropertyFilter.Body = true;
mqInput.MessageReadPropertyFilter.AppSpecific = true;
MessageQueue mqError = new MessageQueue(sErrorQueue);
// 如果使用 MSMQ COM,則將格式化程式設為 ActiveX
mqInput.Formatter = new ActiveXMessageFormatter();
mqError.Formatter = new ActiveXMessageFormatter();

  一旦定義了訊息佇列引用,即會建立一個執行緒用於實際的處理函數
(稱為 ProcessMessages)。在 .NET 框架中,使用 System.Threading
名稱空間很容易實作執行緒處理:
procMessage = new Thread(new ThreadStart(ProcessMessages));
procMessage.Start();

  ProcessMessages 函數是基於 Boolean值的處理循環。當數值設為
False,處理循環將終止。因此,線程物件的 Stop 方法只設定這一Boolean
值,然後關閉開啟的訊息佇列,並加入帶有主執行緒的執行緒:
// 加入服務執行緒
bRun = false;
procMessage.Join();
// 關閉開啟的訊息佇列
mqInput.Close();
mqError.Close();

Pause 方法只設定一個Boolean 值,讓處理執行緒休眠半秒鐘:

if (bPause)
  Thread.Sleep(500);

『個、Pa和Continue 方法將呼叫抽象的
OnStart 、OnStop、OnPause 和 OnContinue 方法。這些抽象方法為實作
的類別提供了掛鉤,以捕獲和釋放所需的資源。

  ProcessMessages 循環具有以下基本結構:
●接收Message。
●如果Message具有成功的Receive,則呼叫抽象ProcessMessage方法。
●如果Receive或ProcessMessage失敗,將Message傳送至錯誤佇列。

Message mInput;
try
{
  // 從佇列中讀取,並等待 1 秒
  mInput = mqInput.Receive(new TimeSpan(0,0,0,1));
}
catch (MessageQueueException mqe)
{
  // 將訊息設為 null
  mInput = null;
  // 查看錯誤代碼,了解是否逾時
  if (mqe.ErrorCode != (-1072824293) ) //0xC00E001B
  {
   // 若未逾時,發出錯誤並記錄錯誤編號
   LogError("Error: " + mqe.Message);
   throw mqe;
  }
}
if (mInput != null)
{
  // 得到一個要處理的訊息,呼叫處理訊息抽象方法
  try
  {
   ProcessMessage(mInput);
  }
  // 捕捉已知異常狀態的錯誤
  catch (CWorkerThreadException ex)
  {
   ProcessError(mInput, ex.Terminate);
  }
  // 捕捉未知異常,並呼叫 Terminate
  catch
  {
   ProcessError(mInput, true);
  }
}

  ProcessError方法將錯誤的訊息傳送至錯誤佇列。另外,它也可能引
發異常來終止執行緒。如果ProcessMessage方法引發了終止錯誤或 CWorker
ThreadException類型,它將執行此操作。

CworkerThread 導出類別

  任何從 CWorkerThread繼承的類別都必須提供 OnStart、OnStop、On
Pause、OnContinue和 ProcessMessage 方法。 OnStart 和 OnStop方法獲
取並釋放處理資源。 OnPause 和 OnContinue 方法允許暫時釋放和重新獲
取這些資源。 ProcessMessage方法應該處理訊息,並在出現失敗事件時引
發 CWorkerThreadException 異常。

  由於 CWorkerThread建構子定義執行階段參數,因此導出類別必須呼叫基底類別
建構子:
public CWorkerThreadDerived(CWorker v_cParent, WorkerThread
Formatter v_wfThread)
  : base (v_cParent, v_wfThread) {}

  導出類別提供了兩種類型的處理:將訊息傳送至另一個佇列,或呼叫群組
件方法。接收和發送訊息的兩種實現使用了循環技術或應用程式偏移(保
留在訊息 AppSpecific屬性中),作為使用哪一個佇列的決定因素。此方案
中的設定檔應該包括佇列路徑的清單。實現的 OnStart和 OnStop 方法
應該開啟和關閉這些佇列的參考:
iQueues = wfThread.OutputName.Length;
mqOutput = new MessageQueue[iQueues];
for (int idx=0; idx〈iQueues; idx++)
{
  mqOutput[idx] = new MessageQueue(wfThread.OutputName[idx]);
  mqOutput[idx].Formatter = new ActiveXMessageFormatter();

在這些方案中,訊息的處理很簡單:將訊息傳送必要的輸出佇列。在
循環情況下,這個過程為:
try
{
  mqOutput[iNextQueue].Send(v_mInput);
}
catch (Exception ex)
{
  // 如果錯誤強制終止異常
  throw new CWorkerThreadException(ex.Message, true);
}
// 計算下一個佇列號碼
iNextQueue++;
iNextQueue %= iQueues;

  後一種呼叫帶有訊息參數的元件的實作方法比較有趣。 ProcessMessage
方法使用 IWebMessage介面調入一個 .NET 元件。 OnStart 和 OnStop 方
法取得並釋放此組件的引用。

  此方案中的設定檔應該包含兩個項目:完整的類別名稱和類別所在檔案的
位置。依照 IWebMessage介面中的定義,在元件上調用 Process方法。

  要取得物件引用,需要使用 Activator.CreateInstance 方法。此函
數需要一個程序集類型。在這裡,它是從程序集檔案路徑和類別名稱中導出的。
一旦取得物件引用,它將被放入適當的介面:
private IWebMessage iwmSample;
private string sFilePath, sTypeName;
// 儲存組件路徑與型別名稱
sFilePath = wfThread.OutputName[0];
sTypeName = wfThread.OutputName[1];
// 取得必要物件的參考
Assembly asmSample = Assembly.LoadFrom(sFilePath);
Type typSample = asmSample.GetType(sTypeName);
object objSample = Activator.CreateInstance(typSample);
// 定義給物件的必要介面
iwmSample = (IWebMessage)objSample;

  取得物件參考後,ProcessMessage方法會在 IWebMessage介面上呼叫
Process 方法:
WebMessageReturn wbrSample;
try
{
  // 定義方法呼叫的參數
  string sLabel = v_mInput.Label;
  string sBody = (string)v_mInput.Body;
  int iAppSpecific = v_mInput.AppSpecific;
  // 呼叫方法並捕捉回傳程式碼
  wbrSample = iwmSample.Process(sLabel, sBody, iAppSpecific);
}
catch (InvalidCastException ex)
{
  // 如果在訊息內容中發生錯誤,則強制發出一個非終止異常
  throw new CWorkerThreadException(ex.Message, false);
}
catch (Exception ex)
{
  // 如果錯誤呼叫組件,則強制發出終止異常
  throw new CWorkerThreadException(ex.Message, true);
}
// 如果沒有錯誤,則檢查物件呼叫的回傳狀態
switch (wbrSample)
{
  case WebMessageReturn.ReturnBad:
   throw new CWorkerThreadException
     ("Unable to process message: Message marked bad", false);
  case WebMessageReturn.ReturnAbort:
   throw new CWorkerThreadException
     ("Unable to process message: Process terminating", true);
  default:
   break;
}

  提供的範例元件將訊息正文寫入資料庫表。如果擷取到嚴重資料庫錯位
誤,您可能想要終止處理過程,但是在這裡,僅將訊息標記為錯誤的消
息。

  由於此範例中建立的類別實例可能會取得並保留昂貴的資料庫資源,所
以用 OnPause和 OnContinue 方法釋放和重新取得物件參考。

檢測設備

  就像在所有優秀的應用程式中一樣,檢測設備用於監測應用程式的狀
態。 。 NET 框架大大簡化了將事件日誌、效能計數器和 Windows管理偵測
裝置(WMI )納入應用程式的過程。訊息應用程式使用時間日誌和效能計
數器,二者都是來自 System.Diagnostics 組件。

  在 ServiceBase類別中,您可以自動啟用事件日誌。另外,ServiceBase
EventLog成員支援寫入應用程式事件日誌:
EventLog.WriteEntry(sMyMessage, EventLogEntryType.Information);

  對於寫入事件日誌而不是應用程式日誌的應用程序,它能夠很容易地
創建和獲取 EventLog 資源的引用(正如在 CWorker類別中所做的一樣),
並且能夠使用 WriteEntry 方法記錄日誌項目:
private EventLog cLog;
string sSource = ServiceControl.ServiceControlName;
string sLog = "application";
// 查看來源是否存在,如果不存在,則建立來源
if (!EventLog.SourceExists(sSource))
  EventLog.CreateEventSource(sSource, sLog);
// 建立日誌對象,並引用現在定義的來源
cLog = new EventLog();
cLog.Source = sSource;
// 在日誌中寫入條目,表示建立成功
cLog.WriteEntry("已成功建立", EventLogEntryType.Information);

  .NET 框架大幅簡化了效能計數器。對於每一個處理線程、線程導出
的用戶和整個應用程序,這一消息應用程式都能提供計數器,用於跟踪消
息數量和每秒鐘處理訊息的數量。若要提供此功能,您需要定義效能計數器
的類別,然後增加對應的計數器實例。

  效能計數器的類別在服務 OnStart方法中定義。這些類別代表兩種計
數器-訊息總數和每秒鐘處理的訊息數:
CounterCreationData[] cdMessage = new CounterCreationData[2];
cdMessage[0] = new CounterCreationData("Messages/Total", "Total
Messages Processed",
PerformanceCounterType.NumberOfItems64);
cdMessage[1] = new CounterCreationData("Messages/Second",
"Messages Processed a Second",
PerformanceCounterType.RateOfChangePerSecond32);
PerformanceCounterCategory.Create("MSDN Message Service", "MSDN
Message Service Counters", cdMessage);

  一旦定義了效能計數器類別,將建立 PerformanceCounter 物件以訪
問計數器實例功能。 PerformanceCounter物件需要類別、計數器名稱和一
個可選的實例名稱。對於輔助進程,將使用來自 xml檔案的進程名稱,代
碼如下:
pcMsgTotWorker = new PerformanceCounter("MSDN Message Service",
"Messages/Total", sProcessName);
pcMsgSecWorker = new PerformanceCounter("MSDN Message Service",
"Messages/Second", sProcessName);
pcMsgTotWorker.RawValue = 0;
pcMsgSecWorker.RawValue = 0;

要增加計數器的值,只需要呼叫適當的方法:

pcMsgTotWorker.IncrementBy(1);
pcMsgSecWorker.IncrementBy(1);

最後說明一點,服務終止時,安裝的效能計數器類別應該從系統中刪除:

PerformanceCounterCatef.

  由於效能計數器在.NET 框架中工作,因此需要運行一項特殊的服務。
此服務(PerfCounterService)提供了共享記憶體。計數器資訊將寫入共享
內存,並被性能計數器系統讀取。

安裝

  在結束以前,我們來簡要介紹一下安裝以及稱為 installutil.exe的
安裝工具。由於此應用程式是 Windows服務,它必須使用installutil.exe
來安裝。因此,需要使用一個從 System.Configuration.Install 組件
中繼承的 Installer類別:
public class ServiceRegister: Installer
{
  private ServiceInstaller serviceInstaller;
  private ServiceProcessInstaller processInstaller;
  public ServiceRegister()
  {
   // 創建服務安裝程序
   serviceInstaller = new ServiceInstaller();
   serviceInstaller.StartType = ServiceStart.Manual;
   serviceInstaller.ServiceName = ServiceControl.ServiceControl
   Name;
   serviceInstaller.DisplayName = ServiceControl.ServiceControl
   Desc;
   Installers.Add(serviceInstaller);
   // 建立進程安裝程序
   processInstaller = new ServiceProcessInstaller();
   processInstaller.RunUnderSystemAccount = true;
   Installers.Add(processInstaller);
  }
}

  如此示例類別所示,對於一個 Windows服務,服務和服務進程各需要一
個安裝程序,以定義運行服務的帳戶。其他安裝程式允許註冊事件日誌和
性能計數器等資源。

總結

  從這個 .NET 框架應用程式範例中可以看出,以前只有 Visual C++
程式設計師能夠編寫的應用程序,現在使用簡單的物件導向程式即可實現。盡
管我們的重點是 C# ,但本文所述的內容也同樣適用於 Visual Basic 和
Managed C++.新的 .NET 框架使開發人員能夠使用任何程式語言來創建功
能強大、可伸縮的 Windows應用程式和服務。

  新的 .NET 框架不僅簡化和擴展了編程的種種可能,還能夠輕鬆地將
人們經常遺忘的應用程式檢測設備(例如性能監測計數器和事件日誌通知)
合併到應用程式中。儘管這裡的應用程式沒有使用 Windows管理檢測設備
(WMI ),但 .NET 框架同樣也可以應用它。 

 以上就是C#訊息佇列應用程式 -2的內容,更多相關文章請關注PHP中文網(www.php.cn)!


相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板