使用 Topshelf 创建 Windows 服务

Ø  前言

C# 创建 Windows 服务的方式有很多种,Topshelf 就是其中一种方式,而且使用起来比较简单。下面使用 Visual Studio Ultimate 2013 演示一下具体的使用步骤:

 

1.   首先,新建一个控制台应用程序,Framework 版本选择4.5,用于测试和启动 Windows 服务。

 

2.   打开程序包管理器控制台,安装 Topshelft 所需的 dll 文件。

1)   安装 Topshelft.dll

1.   注意:因为需要跟当前项目的 .NET Framework 版本兼容,所以需要指定安装版本为 v3.3.1

2.   控制台输入:Install-Package Topshelf -Version 3.3.1,如图:

clip_image002[14]

 

2)   安装 Topshelf.Log4Net.dll

1.   同样,安装与 Topshelf 相同的版本,控制台输入:Install-Package Topshelf.Log4Net -Version 3.3.1

 

3)   安装完成后,包含如下图的引用:

clip_image003[14]

 

3.   因为我们准备使用 log4net 来监控 Windows 服务的运行,所以配置一下 log4net

1)   新建一个 log4net.config 文件。

2)   编辑 log4net.config 的内容:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <configSections>

    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>

  </configSections>

  <log4net>

    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">

      <param name="Encoding" value="utf-8" />

      <param name="RollingStyle" value="date"/>

      <param name="File" value="Logs\"/>

      <param name="DatePattern" value="yyyy-MM-dd&quot;.log&quot;"/>

      <param name="StaticLogFileName" value="false"/>

      <param name="MaxSizeRollBackups" value="10"/>

      <param name="AppendToFile" value="true"/>

      <layout type="log4net.Layout.PatternLayout">

        <param name="ConversionPattern" value="%n%-6p%d{yyyy-MM-dd HH:mm:ss}%n消息:%m"/>

      </layout>

    </appender>

    <root name="logerror">

      <level value="all" />

      <appender-ref ref="RollingLogFileAppender"/>

    </root>

  </log4net>

</configuration>

Ø  关于 log4net 可参考:C# 使用 log4net 记录日志

 

4.   Topshelf 的两种运行方式

Ø  Topshelf 有两种运行方式,下面把两种方式都一起贴出来,代码如下:

using log4net;

using Topshelf;

 

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]

namespace TopshelfWindowsService

{

    public class Program

    {

        private static ILog Logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

 

        /// <summary>

        /// 获取当前进程Id

        /// </summary>

        /// <returns></returns>

        public static int GetProcessId()

        {

            return System.Diagnostics.Process.GetCurrentProcess().Id;

        }

 

        /// <summary>

        /// 获取当前线程Id

        /// </summary>

        /// <returns></returns>

        public static int GetThreadId()

        {

            return System.Threading.Thread.CurrentThread.ManagedThreadId;

        }

 

        /// <summary>

        /// 自定义类加定时器。

        /// </summary>

        public class LogTimer

        {

            private System.Timers.Timer _timer;

 

            public LogTimer()

            {

                _timer = new System.Timers.Timer(10000);

                _timer.Elapsed += (sender, eventArgs) =>

                {

                    Logger.InfoFormat("服务正在运行,PID{0}TID{1}", GetProcessId(), GetThreadId());

                };

            }

 

            public void Start()

            {

                _timer.Start();

                Logger.InfoFormat("服务已开启,PID{0}TID{1},调用 Start() 方法", GetProcessId(), GetThreadId());

            }

 

            public void Stop()

            {

                _timer.Stop();

                Logger.InfoFormat("服务已停止,PID{0}TID{1},调用 Stop() 方法", GetProcessId(), GetThreadId());

            }

 

            public void Pause()

            {

                //...逻辑代码

                Logger.InfoFormat("服务已暂停,PID{0}TID{1},调用 Pause() 方法", GetProcessId(), GetThreadId()); ;

            }

 

            public void Continue()

            {

                //...逻辑代码

                Logger.InfoFormat("服务继续运行,PID{0}TID{1},调用 Continue() 方法", GetProcessId(), GetThreadId());

            }

 

            public void Shutdown()

            {

                //...逻辑代码

                Logger.InfoFormat("服务已关闭,PID{0}TID{1},调用 Shutdown() 方法", GetProcessId(), GetThreadId());

            }

        }

 

        /// <summary>

        /// 自定义类加定时器,并实现 ServiceControl,ServiceSuspend,ServiceShutdown 接口。

        /// </summary>

        public class LogControl : ServiceControl, ServiceSuspend, ServiceShutdown

        {

            private System.Timers.Timer _timer = new System.Timers.Timer(10000);

 

            public LogControl()

            {

                _timer.Elapsed += (sender, eventArgs) =>

                {

                    Logger.InfoFormat("服务正在运行,PID{0}TID{1}", GetProcessId(), GetThreadId());

                };

            }

 

            bool ServiceControl.Start(HostControl hostControl)

            {

                _timer.Start();

                Logger.InfoFormat("服务已开启,PID{0}TID{1},调用 ServiceControl.Start(HostControl hostControl) 方法", GetProcessId(), GetThreadId());

                return true;

            }

 

            bool ServiceControl.Stop(HostControl hostControl)

            {

                _timer.Stop();

                Logger.InfoFormat("服务已停止,PID{0}TID{1},调用 ServiceControl.Stop(HostControl hostControl) 方法", GetProcessId(), GetThreadId());

                return true;

            }

 

            bool ServiceSuspend.Pause(HostControl hostControl)

            {

                //...逻辑代码

                Logger.InfoFormat("服务已暂停,PID{0}TID{1},调用 ServiceControl.Pause(HostControl hostControl) 方法", GetProcessId(), GetThreadId());

                return true;

            }

 

            bool ServiceSuspend.Continue(HostControl hostControl)

            {

                //...逻辑代码

                Logger.InfoFormat("服务继续运行,PID{0}TID{1},调用 ServiceControl.Continue(HostControl hostControl) 方法", GetProcessId(), GetThreadId());

                return true;

            }

 

            void ServiceShutdown.Shutdown(HostControl hostControl)

            {

                //...逻辑代码

                Logger.InfoFormat("服务已关闭,PID{0}TID{1},调用 ServiceShutdown.Shutdown(HostControl hostControl) 方法", GetProcessId(), GetThreadId());

            }

        }

 

        public static void Main()

        {

            //Topshelf 服务的两种运行方式

            //1. 自定义类加定时器

            //Logger.InfoFormat("正在准备安装日志服务1PID{0}TID{1}", GetProcessId(), GetThreadId());

            //HostFactory.Run(o =>

            //{

            //    o.Service<LogTimer>(b =>

            //    {

            //        b.ConstructUsing(x => new LogTimer());

            //        b.WhenStarted(x => x.Start());

            //        b.WhenStopped(x => x.Stop());

            //        b.WhenPaused(x => x.Pause());

            //        b.WhenContinued(x => x.Continue());

            //        b.WhenShutdown(x => x.Shutdown());

            //    });

            //    o.RunAsLocalSystem();

            //    o.SetServiceName("LogTimerService");

            //    o.SetDisplayName("日志服务1");

            //    o.SetDescription("自定义类加定时器,实现日志服务");

            //});

 

            //2. 自定义类加定时器,并实现 ServiceControl,ServiceSuspend,ServiceShutdown 接口

            Logger.InfoFormat("正在准备安装日志服务2PID{0}TID{1}", GetProcessId(), GetThreadId());

            HostFactory.Run(o =>

            {

                o.Service<LogControl>();

                o.RunAsLocalSystem();

                o.SetServiceName("LogTimerControlService");

                o.SetDisplayName("日志服务2");

                o.SetDescription("自定义类加定时器,并实现 ServiceControl,ServiceSuspend,ServiceShutdown 接口,实现日志服务");

            });

 

            Console.WriteLine("OK");

        }

    }

}

Ø  可以看出,一个是普通类 LogTimer,一个是实现了 ServiceControl, ServiceSuspend, ServiceShutdown 口的类 LogControl

Ø  下面分别安装这两种服务:

Ø  安装注意事项:

1.   所安装的服务路径中不能包含空格。

2.   命令提示符尽量使用管理员运行,否则可能报错:The LogTimerService service can only be installed as an administrator

1.   安装第一种方式(自定义类加定时器

1)   打开命令提示符,输入:X:\xxx\xxx.exe install,如图:

clip_image005[14]

2)   这样服务就安装成功了,运行 service.msc,在服务列表中可查看:

clip_image007[14]

3)   右键启动该服务,或者命令行输入:X:\xxx\xxx.exe start,如图:

clip_image009[14]

4)   启动服务后,等待20秒后再停止服务,将看到如下日志:

clip_image010[14]

5)   这样第一种方式的服务就运行起来了,接下来卸载该服务,命令行输入:X:\xxx\xxx.exe uninstall,如图:

clip_image012[14]

 

2.   安装第二种方式(自定义类加定时器,并实现 ServiceControl,ServiceSuspend,ServiceShutdown 接口

1)   首先,注释第一种方式,重新生成后再安装该服务,安装成功后如图:

clip_image014[14]

2)   同样,启动服务后,等待20秒后再停止服务,将看到如下日志:

clip_image016[14]

3)   第二种方式的服务也运行起来了,最后卸载该服务。

4)   可见第二种方式,具有更好的可读性,且比较规范,推荐使用该方式。

 

5.   最后,列出 Windows 服务的常用命令

1)   X:\xxx\xxx.exe install          安装服务

2)   X:\xxx\xxx.exe start            启动服务

3)   X:\xxx\xxx.exe stop             停止服务

4)   X:\xxx\xxx.exe pause            暂停服务

5)   X:\xxx\xxx.exe continue         向服务发送 CONTINUE 控制请求

6)   X:\xxx\xxx.exe delete           删除服务

7)   X:\xxx\xxx.exe uninstall        卸载服务

 

Ø  总结

1.   经过以上步骤,一个 Windows 服务已经可以成功运行了,但只是一个简单的示例,使用了定时器模拟执行一些任务,在实际的工作中可能远远比这个复杂。

2.   可以发现,使用定时器执行任务,似乎有些受限或完全不能满足需要。所以,还需要一个任务调度框架—Quartz.NET,让这任务调度框架运行在 Windows Service 之上,去完成一些定时的任务。

3.   Windows 服务常用的应用场景:

1)   调用存储过程,完成对数据库的操作,可能包含(生成报表、同步数据、更新数据等)。

2)   定时发送邮件、短信,主动处理业务等。

3)   定时调用某接口,完成对数据的更新或写入等。

posted @ 2017-12-13 11:44  Abeam  阅读(2097)  评论(0编辑  收藏  举报