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