快速搭建多线程Windows服务解决方案

一、引言

在软件开发过程中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
    }

四、快速搭建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());
            }
        }
    }

接下来我们创建一个简单的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         }

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服务解决方案,主要好处就接口开发容易搭建多任务服务。服务框架也业务逻辑分离。把核心代码抽取到底层类库或者底层框架中,为团队其他成员创建服务带来便捷,他们不需要关心服务的如何具体实现的,只专注业务逻辑开发。

本人很少写博客,也很少写总结相关文档,这次算是第一次写博客,语言和条理上自己感觉也比较混乱,自认为自己还是在学习阶段,需要想园区很多同仁学习,如果存在问题或者不理解地方欢迎大家文明讨论。再次谢谢大家。

时间: 2024-08-02 15:13:50

快速搭建多线程Windows服务解决方案的相关文章

快速搭建 SpringCloud 微服务开发环境的脚手架

本文适合有 SpringBoot 和 SpringCloud 基础知识的人群,跟着本文可使用和快速搭建 SpringCloud 项目. 本文作者:HelloGitHub-秦人 HelloGitHub 推出的<讲解开源项目>系列,今天给大家带来一款基于 SpringCloud2.1 的微服务开发脚手开源项目——SpringCloud 项目源码地址:https://github.com/zhoutaoo/SpringCloud 一.微服务的简介 微服务是可以独立部署.水平扩展.独立访问的服务单元.

快速搭建简易DHCP服务(小白专属)

Windows server Windows server是微软在2003年4月24日推出的Windows 的服务器操作系统,其核心是Microsoft Windows Server System(WSS),每个Windows Server都与其家用(工作站)版对应(2003 R2除外). DHCP服务 DHCP服务即为动态主机配置协议是一个局域网的网络协议.指的是由服务器控制一段IP地址范围,客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码.首先, DHCP服务器必须是一台安装有

使用FileZilla快速搭建FTP文件服务

为了便于平时对文件的存储访问,特意搭建FTP服务 FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务. FTP是文件传输协议,就是专门用来传输文件的协议 基础环境 服务器:Windows Server 2012 R2 工具:   FileZilla Server 0.9.41中文版 搭建过程 1.下载FileZilla服务端 https://www.filezilla.cn/download/serve

记录一下:使用 python -m SimpleHTTPServer 快速搭建http服务

在 Linux 服务器上或安装了 Python 的机器上,Python自带了一个WEB服务器 SimpleHTTPServer. 我们可以很简单的使用  python -m SimpleHTTPServer 快速搭建一个http服务,提供一个文件浏览的web服务. 命令如下: python -m SimpleHTTPServer 8000 使用上面的命令可以把当前目录发布到8000端口. 但是这条命令是当前运行的,不是后台运行的,也就是说如果Ctrl + C,则该端口就会关闭. python -

如何快速搭建ftp服务器(详细教程)

FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务. FTP是文件传输协议,就是专门用来传输文件的协议.这篇文章主要介绍了使用云帮手工具快速搭建FTP文件服务 ,需要的朋友可以参考下. 首先进入云帮手(官网),下载安装好服务器管理运维工具. 搭建ftp只需要安装完主机探针,探针是为了更安全管理主机服务器,让服务器更稳定,实时查看主机运行状态. 安装完探针后,就可以进入管理界面,环境安装只需要安装一个ft

“云中论道”之——使用开源技术和Azure公有云服务快速搭建云端IoT解决方案(上)

"云中论道"技术课堂第一课开讲啦!微软各路技术咖们齐聚一堂,为大家带来干货不断!作为"云中论道"课堂的开课之作,我们首先邀请到了微软Azure专家级的架构师:槐长清,他为我们带来了关于"使用开源技术和Azure公有云服务快速搭建云端IoT解决方案"的精心讲解. 本文作者介绍: 微软Azure专家级架构师,江湖人称"槐长清",曾连续5年被评为微软最有价值专家,多年云计算从业经验,对微软公有云解决方案有深入研究. 涉及产品及技术:

Windows服务的快速搭建与调试(C#图解) 利用BAT批处理安装服务程序

一.什么是Windows 服务? 答:Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面.这使服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用.还可以在不同于登录用户的特定用户帐户或默认计算机帐户的安全上下文中运行服务. 二.创建Windows 服务. 打开:Visual

使用Topshelf快速搭建Windows服务

创建一个windows服务用于同步SqlServer数据到Elasticsearch 新建elasticsearch索引 PUT:http://localhost:9200/index_singer/ { "settings": { "number_of_shards": 5, "number_of_replicas": 1 }, "mappings": { "singer_index": { "

RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用

RDIFramework.NET框架SOA解决方案(集Windows服务.WinForm形式与IIS形式发布)-分布式应用 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用户和开发者最佳的.Net框架部署方案.该框架以SOA范式作为指导思想,作为异质系统整合与互操作性.分布式应用提供了可行的解决方案. 1.SOA平台简介 1.1.概述 SOA(service-oriented architecture,也叫面向服务的体系结构或面向服务架构)是指为了解决在Inte