1. 准备工作
Web Server+MSMQ+Windows Service;
一般在win2k下我们会装IIS,但MSMQ(Microsoft Message Queue)不一定会装。可以通过(控制面板-计算机管理-服务和应用程序-消息队列)查看。若没有装可以通过添加window组件安装。
2. 原理
在web客户端发出指令或消息Message(可以是对象Message,但必须是可序列化的,且对象所在程序集在windows service中要引用),然后Message被指定消息队列所接受,至此客户端(或者说web server)的事情已经做完。
windows service在指定时间间隔下会读取消息队列(当然是同名消息队列), 经解析消息内容完成相应动作(可以是数据库操作,或大数据量运算等)。
下面是我草画了一幅原理图,
3. 实现
客户端:
private void SendPrintRequestToMessageQueue()

{
try

{
//pick up the queuename from the web.config file
string queueName = ConfigurationSettings.AppSettings["PrintChecksMessageQueue"];
MessageQueue destMsgQ = new MessageQueue(queueName);

/**//*instantiate the message request object with correct parameter
in this case we can assume that the parameter
comes from a drop downlist selection*/
PrintCheckRequestMessage objMessage = new
PrintCheckRequestMessage(this.TextBox1.Text.Trim());
//Create a new message object
Message destMsg = new Message();

/**//*In case of unsuccessful attempts to post messages
, send them to dead letter queue for audit traceability*/
destMsg.UseDeadLetterQueue = true;

/**//*Use binary formatter for posting the request message
object into the queue*/
destMsg.Formatter = new System.Messaging.BinaryMessageFormatter();
//Assign the request message object to the message body
destMsg.Body = objMessage;
//Post the message
destMsgQ.Send(destMsg);
}
catch(System.Exception ex)

{
//display the error message somewhere (on a label may be)
this.lblError.Text = "Communication Error!"+ex.Message;
}
}
memo:
using System.Messaging;
using System.ServiceProcess;
using System.Configuration;
消息对象:
using System;
namespace MSMQTest


{

/**//// <summary>
/// PrintCheckRequestMessage 的摘要说明。
/// </summary>
///
[Serializable]
public class PrintCheckRequestMessage

{
public string BodyContent;
public PrintCheckRequestMessage(string strSend)

{
BodyContent = strSend;
}
}
}
memo:要单独生成程序集。
windows服务:
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Messaging;
using MSMQTest;
namespace windowsMQPollService


{
public class windowsMQPollService : System.ServiceProcess.ServiceBase

{

/**//// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
//定义计时器
private System.Timers.Timer timerPolling;
//定义开始计时标志
private bool isTimerStarted;
//存储配置数据
private System.Collections.Hashtable htConfigData;
//消息队列地址
private string m_StrQueuePath=string.Empty;
//创建消息对象
public PrintCheckRequestMessage objCheckMsgRequest = new PrintCheckRequestMessage(string.Empty);
//创建数据连接对象
private SqlDataProvider checkProvider = new SqlDataProvider();
//创建windows事件日志交互
private System.Diagnostics.EventLog exceptionEventLog;
public windowsMQPollService()

{
// 该调用是 Windows.Forms 组件设计器所必需的。
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists(this.ServiceName))

{
System.Diagnostics.EventLog.CreateEventSource(this.ServiceName,
"Application");
}
exceptionEventLog.Source = this.ServiceName;
exceptionEventLog.Log = "Application";
}
// 进程的主入口点
static void Main()

{
System.ServiceProcess.ServiceBase[] ServicesToRun;
// 同一进程中可以运行多个用户服务。若要将
//另一个服务添加到此进程,请更改下行
// 以创建另一个服务对象。例如,
//
// ServicesToRun = New System.ServiceProcess.ServiceBase[] {new windowsMQPollService(), new MySecondUserService()};
//

ServicesToRun = new System.ServiceProcess.ServiceBase[]
{ new windowsMQPollService() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}

/**//// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器
/// 修改此方法的内容。
/// </summary>
private void InitializeComponent()

{
components = new System.ComponentModel.Container();
this.exceptionEventLog = new System.Diagnostics.EventLog();
((System.ComponentModel.ISupportInitialize)(this.exceptionEventLog)).BeginInit();
this.ServiceName = "windowsMQPollService";
((System.ComponentModel.ISupportInitialize)(this.exceptionEventLog)).EndInit();
}

/**//// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )

{
if( disposing )

{
if (components != null)

{
components.Dispose();
}
}
base.Dispose( disposing );
}

/**//// <summary>
/// 设置具体的操作,以便服务可以执行它的工作。
/// </summary>
protected override void OnStart(string[] args)

{
//Read config file entries for the Windows service
//into a hashtable for faster retrieval
GetConfigurationData();

/**//*check whether the config file had
correct entries or there were some
errors while populating the hash*/
if (htConfigData["Service.MSMQPath"] == null)

{
exceptionEventLog.WriteEntry("Failed to read" +
" the config file information.",
System.Diagnostics.EventLogEntryType.Error);
}
int iPollingInterval = 60;
if (htConfigData["Service.PollingInterval"] == null)

{
exceptionEventLog.WriteEntry("Polling Interval not specified." +
" The service is starting assuming " +
"polling interval to be 60 minutes.",
System.Diagnostics.EventLogEntryType.Warning);
}
else

{
try

{
//get the configured polling interval
iPollingInterval =
int.Parse((string)htConfigData["Service.PollingInterval"]);
}
catch

{
exceptionEventLog.WriteEntry("Not a valid value" +
" specified for Polling Interval. The service is starting" +
" assuming polling interval to be 60 minutes.",
System.Diagnostics.EventLogEntryType.Warning);
}
}
//Create timer of interval iPollingInterval.
timerPolling = new System.Timers.Timer();
timerPolling.Elapsed += new System.Timers.ElapsedEventHandler(OnTimer);
timerPolling.Interval = (double)(iPollingInterval * 60 * 10);
timerPolling.Enabled = true;
isTimerStarted=true;
//start the polling activity of the Windows service
timerPolling.Start();
}
private void OnTimer(object source, System.Timers.ElapsedEventArgs e)

{
if(isTimerStarted)

{
timerPolling.Stop();
//At appropriate intervals get messages from the queue
PickupFromMSMQ();
timerPolling.Start();
}
}
private bool PickupFromMSMQ()

{
//read the queuepath from the hashtable
m_StrQueuePath = htConfigData["Service.MSMQPath"].ToString();
string formattedMessage = string.Empty;
try

{
System.Messaging.MessageQueue mqRecvQueue =
new System.Messaging.MessageQueue(m_StrQueuePath);
//use binary formater for receiving messages from the queue
mqRecvQueue.Formatter = new System.Messaging.BinaryMessageFormatter();
//DataSet allChecksDS;
//receive the message from the queue
System.Messaging.Message msgSrcMessage = mqRecvQueue.Receive();
//recreate the requestmessage object
objCheckMsgRequest = (PrintCheckRequestMessage)msgSrcMessage.Body;
string strTest = objCheckMsgRequest.BodyContent;
checkProvider.SaveData2File(strTest);
//string strBatchNumber = objCheckMsgRequest.AuditId;
//use the dataprovider to call functions to get DataSet
//allChecksDS = checkProvider.GetDataSetByBatchNumber(strBatchNumber);

/**//*do your own stuff with the DataSet.
For us it was calling another assembly
to print uncountable crytsal reports*/
return true;
}
catch(Exception ex)

{
//write exception message into the eventlog
exceptionEventLog.WriteEntry("Error while reading from Queue :" +
ex.Message +"--"+
ex.StackTrace,System.Diagnostics.EventLogEntryType.Error );
return false;
}
}

/**//// <summary>
/// 读配置文件到Hashtable,
/// (Service.MSMQPath|Service.PollingInterval)
/// </summary>
private void GetConfigurationData()

{
try

{
htConfigData = new System.Collections.Hashtable();
System.Collections.Specialized.NameValueCollection colNameVal;
colNameVal = System.Configuration.ConfigurationSettings.AppSettings;
foreach(string sKey in colNameVal.AllKeys)

{
htConfigData.Add(sKey,colNameVal[sKey]);
}
}
catch(System.Configuration.ConfigurationException e)

{
exceptionEventLog.WriteEntry("The configuration file is missing." + " Could not start the service", System.Diagnostics.EventLogEntryType.Error);
throw(new Exception("Service Startup Error : " +e.Message));
}
}

/**//// <summary>
/// 停止此服务。
/// </summary>
protected override void OnStop()

{
// TODO: 在此处添加代码以执行停止服务所需的关闭操作。
}
}
}
using System;
using System.IO;
namespace windowsMQPollService


{

/**//// <summary>
/// SqlDataProvider 的摘要说明。
/// </summary>
public class SqlDataProvider

{
public SqlDataProvider()

{
//
// TODO: 在此处添加构造函数逻辑
//
}
public void SaveData(string strTest)

{
}
public void SaveData2File(string strTest)

{
string strPath =@"E:\test\MSMQService\RunResult\RunResult.txt" ;
string strDir =@"E:\test\MSMQService\RunResult" ;
System.IO.StreamWriter sw;
if(!File.Exists(strPath))

{
Directory.CreateDirectory(strDir);
sw = File.CreateText(strPath);
}
else

{
sw = new StreamWriter(strPath);
}
sw.WriteLine(strTest);
sw.Close();
}
}
}
memo:using MSMQTest;(消息对象程序集)
4.参考文献
How to do asynchronous programming using ASP.NET, MSMQ and Windows Service, for long running processes,
By anupamkundu
from http://www.codeproject.com/dotnet/Using_MSMQ_ASPnet_Window.asp