DavidJGu's Blog

       尼采说,宁可追求虚无也不能无所追求!  Keeping is harder than winning
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

基于消息传递的Web Applicattion和Windows Service异步通信模型

Posted on 2004-12-05 19:40  Let's DotNet  阅读(1291)  评论(2)    收藏  举报

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