zoukankan      html  css  js  c++  java
  • 使用 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:xxxxxx.exe install,如图:

    clip_image005[14]

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

    clip_image007[14]

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

    clip_image009[14]

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

    clip_image010[14]

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

    clip_image012[14]

     

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

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

    clip_image014[14]

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

    clip_image016[14]

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

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

     

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

    1)   X:xxxxxx.exe install          安装服务

    2)   X:xxxxxx.exe start            启动服务

    3)   X:xxxxxx.exe stop             停止服务

    4)   X:xxxxxx.exe pause            暂停服务

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

    6)   X:xxxxxx.exe delete           删除服务

    7)   X:xxxxxx.exe uninstall        卸载服务

     

    Ø  总结

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

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

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

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

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

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

  • 相关阅读:
    ASP.NET MVC 3 Internationalization
    Windows 8学习笔记(十)Notification
    Windows 8学习笔记(十二)集合控件
    Windows 8学习笔记(七)Input输入设备
    【配置与安装】解决类似umount target is busy挂载盘卸载不掉问题
    【coredump】coredump 使用
    【C++调试】"./xxx: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by ./xxx)" 执行报错
    【配置与安装】CentOS7 多磁盘合成卷组并创建LVM,挂载到同一目录
    Unix 知识
    3D创作概念入门
  • 原文地址:https://www.cnblogs.com/abeam/p/8032080.html
Copyright © 2011-2022 走看看