一、引言
在软件开发过程中windows服务有的时候非常有用,用于同步数据,发送邮件,宿主WF引擎服务等,但是快速搭建一个好用多线程进行多任务处理的程序往往是一个项目必须考虑的问题。自己在项目中也经常碰到类似的问题,但是一直没有做过这方面总结,每次需要相关windows服务,也重头写一次。自己几乎没有写博客的习惯,前不久看到博客园中有篇关于windows服务的框架写的非常好(抱歉,当时看完以后忘记收藏),感觉自己这些年对它也有一定的认识,但是很少拿出来和大家分享。其实大家都知道通过分享才能找到问题并能提高代码的质量。经过国庆期间的初步整理一个windows服务快速实现的解决方案终于调试通过,当然里面也有一定的问题,希望大伙能指出,谢谢。
二、通用Windows服务接口
定义ICommand作为windows service服务接口,所有自定义的服务都应实现ICommand
三、定义WindowsServiceItem对象与WindowsServiceItemCollection集合
服务的每个任务对应一个WindowsServiceItem,一个服务对应多个任务这样定义WindowsServiceItemCollection集合进行维护每个任务。其实WindowsServiceItem中实现了WindowsTimer周期属性,Type为自定义服务的类型.这里只是简单的对象定义大伙应该一看就明白。
以下是WindowsTimer的定义.
<span>public</span>
<span>class</span>
<span> WindowsTimer : IDisposable
{
</span>
<span>///</span>
<span><summary></span>
<span>///</span>
<span> 周期任务描述
</span>
<span>///</span>
<span></summary></span>
<span>public</span>
<span>string</span> Description = <span>string</span><span>.Empty;
</span><span>///</span><span><summary></span><span>///</span><span> 时钟周期控件
</span><span>///</span><span></summary></span><span>public</span> Timer Timer = <span>new</span><span> Timer();
</span><span>///</span><span><summary></span><span>///</span><span> 是否准备好了可以接受下一个周期的任务
</span><span>///</span><span></summary></span><span>public</span><span>bool</span> Prepared = <span>true</span><span>;
</span><span>///</span><span><summary></span><span>///</span><span> Windows服务的时间控件异常回调委托
</span><span>///</span><span></summary></span><span>public</span><span> WindowsServiceTimerExceptionCallBack CallBack;
</span><span>///</span><span><summary></span><span>///</span><span> 构造函数
</span><span>///</span><span></summary></span><span>///</span><span><param name="description"></span><span>周期任务描述</span><span></param></span><span>///</span><span><param name="interval"></span><span>执行周期间隔毫秒数</span><span></param></span><span>public</span> WindowsTimer(<span>string</span> description, <span>double</span><span> interval)
{
Description </span>=<span> description;
Timer.Interval </span>=<span> interval;
Timer.Enabled </span>= <span>false</span><span>;
}
</span><span>#region</span> IDisposable Members
<span>///</span><span><summary></span><span>///</span><span> 析构函数
</span><span>///</span><span></summary></span>
~<span>WindowsTimer()
{
Dispose(</span><span>false</span><span>);
}
</span><span>///</span><span><summary></span><span>///</span><span>///</span><span></summary></span><span>public</span><span>void</span><span> Dispose()
{
Dispose(</span><span>true</span><span>);
GC.SuppressFinalize(</span><span>this</span><span>);
}
</span><span>///</span><span><summary></span><span>///</span><span>///</span><span></summary></span><span>///</span><span><param name="disposing"></param></span><span>protected</span><span>virtual</span><span>void</span> Dispose(<span>bool</span><span> disposing)
{
</span><span>if</span><span> (disposing)
{
</span><span>if</span> (Timer != <span>null</span><span>)
{
Timer.Dispose();
}
}
}
</span><span>#endregion</span><span>
}</span>
四、快速搭建Windows服务核心Windows服务类
该类继承 System.ServiceProcess.ServiceBase重写相关的基类方法。在此之前定义两个默认Command:WindowsServiceDefaultCommand和WindowsServiceEmptyCommand
核心WindowsService代码如下:
<span>[Serializable]
</span>
<span>public</span>
<span>class</span>
<span> WindowsService : System.ServiceProcess.ServiceBase
{
</span>
<span>private</span>
<span>bool</span>
<span> _fHasInitServerRemotingObject;
</span>
<span>private</span>
<span>readonly</span>
<span>object</span> _fHasInitServerRemotingObjectLock = <span>new</span><span>object</span><span>();
</span><span>private</span><span>readonly</span> WindowsServiceItemCollection _fTimerServices = <span>new</span><span> WindowsServiceItemCollection();
</span><span>private</span><span>readonly</span><span>object</span> _fTimerServiceObjectsLock = <span>new</span><span>object</span><span>();
</span><span>private</span><span>readonly</span> Dictionary<Type, ICommand> _fTimerServiceObjects = <span>new</span> Dictionary<Type, ICommand><span>();
</span><span>///</span><span><summary></span><span>///</span><span> 构造函数
</span><span>///</span><span></summary></span><span>///</span><span><param name="timerServices"></param></span><span>public</span><span> WindowsService(WindowsServiceItemCollection timerServices)
{
</span><span>if</span> (timerServices != <span>null</span><span>)
{
</span><span>foreach</span> (WindowsServiceItem item <span>in</span><span> timerServices)
{
_fTimerServices.Add(item);
}
}
}
</span><span>///</span><span><summary></span><span>///</span><span> 服务启动执行的操作
</span><span>///</span><span></summary></span><span>///</span><span><param name="args"></param></span><span>protected</span><span>override</span><span>void</span> OnStart(<span>string</span><span>[] args)
{
</span><span>if</span> (_fTimerServices.Count == <span>0</span><span>)
{
</span><span>var</span> wsiEmpty = <span>new</span><span> WindowsServiceItem
{
WindowsTimer </span>= <span>new</span> WindowsTimer(<span>"</span><span>默认的一个Command的轮询周期,设置为5分钟。</span><span>"</span>, <span>300000</span><span>),
CommandType </span>= <span>typeof</span><span> (WindowsServiceEmptyCommand)
};
_fTimerServices.Add(wsiEmpty);
}
</span><span>var</span> wsi = <span>new</span><span> WindowsServiceItem
{
WindowsTimer </span>= <span>new</span> WindowsTimer(<span>"</span><span>默认的一个Command的轮询周期,设置为5秒钟。</span><span>"</span>, <span>5000</span><span>),
CommandType </span>= <span>typeof</span><span> (WindowsServiceDefaultCommand)
};
_fTimerServices.Add(wsi);
</span><span>foreach</span> (WindowsServiceItem kvp <span>in</span><span> _fTimerServices)
{
kvp.WindowsTimer.Timer.Elapsed </span>-=<span> Timer_Elapsed;
kvp.WindowsTimer.Timer.Elapsed </span>+=<span> Timer_Elapsed;
kvp.WindowsTimer.Timer.Enabled </span>= <span>true</span><span>;
}
}
</span><span>private</span><span>void</span> Timer_Elapsed(<span>object</span><span> sender, ElapsedEventArgs e)
{
</span><span>try</span><span>
{
</span><span>#region</span> 获取Command对象<span>
WindowsServiceItem wsItem </span>=<span> _fTimerServices.GetItemByTimer((Timer)sender);
Type commandType </span>=<span> wsItem.CommandType;
ICommand command </span>= <span>null</span><span>;
</span><span>if</span> (!<span>_fTimerServiceObjects.ContainsKey(commandType))
{
</span><span>lock</span><span> (_fTimerServiceObjectsLock)
{
</span><span>if</span> (!<span>_fTimerServiceObjects.ContainsKey(commandType))
{
</span><span>var</span> cmd = Activator.CreateInstance(commandType) <span>as</span><span> ICommand;
_fTimerServiceObjects.Add(commandType, cmd);
command </span>=<span> cmd;
}
}
}
</span><span>else</span><span>
{
command </span>=<span> _fTimerServiceObjects[commandType];
}
</span><span>#endregion</span><span>if</span> (!<span>wsItem.WindowsTimer.Prepared)
{
</span><span>return</span><span>;
}
</span><span>if</span> (command != <span>null</span><span>)
{
</span><span>lock</span><span> (wsItem.WindowsTimer)
{
</span><span>try</span><span>
{
wsItem.WindowsTimer.Prepared </span>= !<span>wsItem.WindowsTimer.Prepared;
command.Execute();
}
</span><span>catch</span><span> (Exception ex)
{
</span><span>//</span><span>这里不应该导致整个服务终止,而只是把这个错误信号传递到外面去。</span><span>if</span> (wsItem.WindowsTimer.CallBack != <span>null</span><span>)
{
</span><span>var</span> args = <span>new</span><span> WindowsServiceTimerExceptionArgs(ex, e);
</span><span>try</span><span>
{
wsItem.WindowsTimer.CallBack(wsItem.WindowsTimer, args);
}
</span><span>catch</span><span> (Exception ex1)
{
LogHelper.WriteErrorLog(ex1.ToString());
}
}
}
</span><span>finally</span><span>
{
wsItem.WindowsTimer.Prepared </span>= !<span>wsItem.WindowsTimer.Prepared;
}
}
}
}
</span><span>catch</span><span> (Exception ex)
{
LogHelper.WriteErrorLog(ex.ToString());
}
}
</span><span>///</span><span><summary></span><span>///</span><span> 服务停止执行的操作
</span><span>///</span><span></summary></span><span>protected</span><span>override</span><span>void</span><span> OnStop()
{
</span><span>try</span><span>
{
</span><span>foreach</span> (WindowsServiceItem kvp <span>in</span><span> _fTimerServices)
{
kvp.WindowsTimer.Timer.Enabled </span>= <span>false</span><span>;
</span><span>try</span><span>
{
kvp.WindowsTimer.Timer.Dispose();
}
</span><span>catch</span><span> (Exception ex)
{
LogHelper.WriteErrorLog(ex.ToString());
}
}
}
</span><span>catch</span><span> (Exception ex)
{
LogHelper.WriteErrorLog(ex.ToString());
}
}
</span><span>///</span><span><summary></span><span>///</span><span> 释放Timer资源
</span><span>///</span><span></summary></span><span>///</span><span><param name="disposing"></param></span><span>protected</span><span>override</span><span>void</span> Dispose(<span>bool</span><span> disposing)
{
</span><span>if</span><span> (disposing)
{
</span><span>if</span> (_fTimerServices != <span>null</span><span>)
{
</span><span>var</span> timers = <span>new</span> List<WindowsTimer><span>();
</span><span>foreach</span> (WindowsServiceItem wt <span>in</span><span> _fTimerServices)
{
timers.Add(wt.WindowsTimer);
}
_fTimerServices.Clear();
</span><span>foreach</span> (WindowsTimer wt <span>in</span><span> timers)
{
wt.Dispose();
}
}
}
</span><span>base</span><span>.Dispose(disposing);
}
}</span>
五、服务demo
开始服务demo的时候,这里我们这里稍微介绍服务帮助类WindowsServiceHelper.cs主要用于执行服务和异常的管理
<span>///</span>
<span><summary></span>
<span>///</span>
<span> Windows服务辅助类
</span>
<span>///</span>
<span></summary></span>
<span> [Serializable]
</span>
<span>public</span>
<span>class</span>
<span> WindowsServiceHelper
{
</span>
<span>///</span>
<span><summary></span>
<span>///</span>
<span>///</span>
<span></summary></span>
<span>static</span>
<span> WindowsServiceHelper()
{
}
</span>
<span>///</span>
<span><summary></span>
<span>///</span>
<span> 执行Windows服务
</span>
<span>///</span>
<span></summary></span>
<span>public</span>
<span>static</span>
<span>void</span>
<span> RunServices(WindowsServiceItemCollection timerServices, WindowsServiceTimerExceptionCallBack callBack)
{
WindowsServiceItemCollection.IsWindowsServiceEnviroments </span>= <span>true</span><span>;
</span><span>try</span><span>
{
</span><span>if</span> (timerServices == <span>null</span><span>)
{
</span><span>throw</span><span>new</span> ArgumentNullException(<span>"</span><span>timerServices</span><span>"</span><span>);
}
</span><span>if</span> (callBack != <span>null</span><span>)
{
</span><span>foreach</span> (WindowsServiceItem kvp <span>in</span><span> timerServices)
{
kvp.WindowsTimer.CallBack </span>+=<span> callBack;
}
}
</span><span>var</span> servicesToRun = <span>new</span><span> ServiceBase[]
{
</span><span>new</span><span> WindowsService(timerServices)
};
</span><span>foreach</span> (ServiceBase service <span>in</span><span> servicesToRun)
{
service.AutoLog </span>= <span>true</span><span>;
}
AppDomain.CurrentDomain.UnhandledException </span>+=<span> CurrentDomain_UnhandledException;
ServiceBase.Run(servicesToRun);
}
</span><span>catch</span><span> (Exception ex)
{
LogHelper.WriteErrorLog(ex.ToString());
</span><span>throw</span><span>;
}
}
</span><span>///</span><span><summary></span><span>///</span><span> 未处理异常的处理
</span><span>///</span><span></summary></span><span>///</span><span><param name="sender"></param></span><span>///</span><span><param name="e"></param></span><span>private</span><span>static</span><span>void</span> CurrentDomain_UnhandledException(<span>object</span><span> sender, UnhandledExceptionEventArgs e)
{
</span><span>try</span><span>
{
</span><span>if</span> (e != <span>null</span><span>)
{
</span><span>if</span> (e.ExceptionObject != <span>null</span><span>)
{
</span><span>if</span> (e.ExceptionObject <span>is</span><span> Exception)
{
LogHelper.WriteErrorLog(e.ExceptionObject.ToString());
}
}
}
}
</span><span>catch</span><span> (Exception ex)
{
LogHelper.WriteErrorLog(ex.ToString());
}
}
}</span>
接下来我们创建一个简单的Demo
重新创建一个控制台程序,创建一个SendSmsCommand.cs 用于发信息,再创建一个SysDataToDataBase.cs用于同步数据到数据库。
<span>public</span>
<span>class</span>
<span> SendSmsCommand:ICommand
{
</span>
<span>public</span>
<span>void</span>
<span> Execute()
{
</span>
<span>//</span>
<span> 服务的业务逻辑核心代码</span>
<span> DoSomething();
}
}</span>
<span>public</span>
<span>class</span>
<span> SysDataToDataBase:ICommand
{
</span>
<span>public</span>
<span>void</span>
<span> Execute()
{
</span>
<span>//</span>
<span> 服务的业务逻辑核心代码</span>
<span> DoSomething();
}
} </span>
接下来就是将业务相关的服务进行注册,打开Program.cs修改main函数的代码如下:
<span> 1</span>
<span>static</span>
<span>void</span> Main(<span>string</span><span>[] args)
</span><span> 2</span><span> {
</span><span> 3</span><span>try</span><span> 4</span><span> {
</span><span> 5</span> AppDomain.CurrentDomain.UnhandledException += <span>new</span><span> UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
</span><span> 6</span><span>var</span> timerServices = <span>new</span><span> WindowsServiceItemCollection();
</span><span> 7</span><span> 8</span><span> RegisterSendSmsService(timerServices);
</span><span> 9</span><span> RegisterSynDateService(timerServices);
</span><span>10</span><span> WindowsServiceHelper.RunServices(timerServices, WindowsServiceTimerExceptionCallBack);
</span><span>11</span><span>12</span><span> }
</span><span>13</span><span>catch</span><span>(Exception ex)
</span><span>14</span><span> {
</span><span>15</span> WriterLog(ex.ToString(), <span>"</span><span>error</span><span>"</span><span>);
</span><span>16</span><span> }
</span><span>17</span><span>18</span> }
RegisterSendSmsService方法就是用来注册发送信息服务,RegisterSynDateService用来注册同步数据服务。相关实现如下:
<span>private</span>
<span>static</span>
<span>void</span>
<span> RegisterSynDateService(WindowsServiceItemCollection timerServices)
{
</span>
<span>try</span>
<span>
{
</span>
<span>const</span>
<span>double</span> interval = <span>1000</span><span>;
</span><span>var</span> sendTimer = <span>new</span> WindowsTimer(<span>"</span><span>SendSynDataInterval</span><span>"</span><span>, interval);
timerServices.Add(sendTimer, </span><span>typeof</span><span>(SysDataToDataBase));
}
</span><span>catch</span><span> (Exception ex)
{
Console.Write(ex);
WriterLog(ex.ToString(), </span><span>"</span><span>error</span><span>"</span><span>);
}
}
</span><span>static</span><span>void</span><span> RegisterSendSmsService(WindowsServiceItemCollection timerServices)
{
</span><span>#region</span> 发送短信服务
<span>try</span><span>
{
</span><span>const</span><span>double</span> interval = <span>1000</span><span>;
</span><span>var</span> sendTimer = <span>new</span> WindowsTimer(<span>"</span><span>SendSMSInterval</span><span>"</span><span>, interval);
timerServices.Add(sendTimer, </span><span>typeof</span><span>(SendSmsCommand));
}
</span><span>catch</span><span> (Exception ex)
{
Console.Write(ex);
WriterLog(ex.ToString(), </span><span>"</span><span>error</span><span>"</span><span>);
}
</span><span>#endregion</span><span>
}</span>
六、写在最后
本快速搭建windows服务解决方案,主要好处就接口开发容易搭建多任务服务。服务框架也业务逻辑分离。把核心代码抽取到底层类库或者底层框架中,为团队其他成员创建服务带来便捷,他们不需要关心服务的如何具体实现的,只专注业务逻辑开发。
本人很少写博客,也很少写总结相关文档,这次算是第一次写博客,语言和条理上自己感觉也比较混乱,自认为自己还是在学习阶段,需要想园区很多同仁学习,如果存在问题或者不理解地方欢迎大家文明讨论。再次谢谢大家。
原文:http://www.cnblogs.com/DomoYao/p/4016494.html
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!