用 Topshelf 构建 Windows 服务

 

概述

 Topshelf 是一款开源的跨平台的基于.Net Framework宿主服务框架,它支持 Windows、Mono 系统,如果你安装了 Topshelf.Linux,那它还支持 Linux 系统
 

运行环境

Topshelf 是一款需要.net 运行时支持的 并且为 C# 语言准备的 .net framework 框架
使用平台:.net 3.5 Service Pack1 或 .net 4.0 的 windows 操作系统
 

较传统服务优势

调试测试简单,安装方便

官方及源码

1、官方网站:http://docs.topshelf-project.com/en/latest/
2、源码托管在:https://github.com/topshelf/Topshelf/downloads
3、新建项目,引用或者在NuGet中安装 Topshelf.dll ,如果还想记录日志的话你还可以引用上 Topshelf.Log4net
  • Install-Package Topshelf
  • Install-Package Topshelf.Log4net
4、 Topshelf 最新版本:4.0.4 (来自NuGet)
 

代码实例

 public class TownCrier
    {
        readonly Timer _timer;
        public TownCrier()
        {
            _timer = new Timer(1000) { AutoReset = true };
            _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);
        }
        public void Start() { _timer.Start(); }
        public void Stop() { _timer.Stop(); }
    }

    public class Program
    {
        public static void Main()
        {
            var rc = HostFactory.Run(x =>                                   //1. 这里,我们利用 HostFactory.Run 启动宿主,并且通过 lambda 表达式中的 x  提取暴露在外的所有宿主环境变量配置。我们也可以通过 11 行中的代码捕获服务返回的代码
            {
                x.Service<TownCrier>(s =>                                   //2. 这里,我们告诉 Topshelf 有一个 服务类型的 ‘TownCrier’,并且通过参数‘s’打开暴露在外的服务的配置项
                {
                    s.ConstructUsing(name => new TownCrier());                //3. 这里,我们告诉 Topshelf 如何去创建一个服务实例,当下,我们只是通过 new 的形式创建实例,简单一点,我们也可以从 IOC 容器中拉取,这样看起来就像是‘container.GetInstance<TownCrier>’ 
                    s.WhenStarted(tc => tc.Start());                         //4. Topshelf 如何启动服务
                    s.WhenStopped(tc => tc.Stop());                          //5. Topshelf 如何停止服务
                });
                x.RunAsLocalSystem();                                       //6. 服务使用NETWORK_SERVICE内置帐户运行。身份标识,有好几种方式,如:x.RunAs("username", "password");  x.RunAsPrompt(); x.RunAsNetworkService(); 等

                x.SetDescription("Sample Topshelf Host");                   //7. 设置 Windows 服务 描述
                x.SetDisplayName("Stuff");                                  //8. 设置 Windows 服务 展示名称
                x.SetServiceName("Stuff");                                  //9. 设置 Windows 服务 服务名称
            });                                                             //10. lambda 表达式结束,配置将会被执行,宿主将要启动

            var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode());  //11. 返回服务退出码
            Environment.ExitCode = exitCode;
        }
    }

 

提示:3.x 版本后,不再支持多服务,这么做是因为代码继承会变得脆弱并且很难调试
 
 

Topshelf 配置

 

Service Name

指定安装的服务在服务管理中的名称,也可以直接使用 Program.cs 文件 的命名空间作为默认名称
HostFactory.New(x =>
{
    x.SetServiceName("MyService");
});

 

Service Description

指定安装的服务在服务管理中的名称,这项是可选的
HostFactory.New(x =>
{
    x.SetDescription("My First Topshelf Service");
});

 

Display Name

指定安装的服务在服务管理中的展示名称,这项是可选的
HostFactory.New(x =>
{
    x.SetDisplayName("MyService");
});

 

HostFactory.New(x =>
{
    x.SetInstanceName("MyService");
});

 

Instance Name

指定安装的服务的实例名称,指定的名称会和服务名称通过 $ 符号分割,这项是可选的,但如果选择添加那么他一定是唯一的
 
 
服务配置
服务可以以多种方式配置,每种方式都有不同的目标,对于能够处理 Topshelf  依赖的服务,ServiceControl 接口提供大量基于 service control 的实现,还有就是如果你不进行任何配置,一个只通过lambda表达式调用服务类中的各种方法的零依赖的解决放也是可以的。
 

Simple Service

HostFactory.New(x =>
{
    x.Service<MyService>();
});

// Service implements the ServiceControl methods directly and has a default constructor
class MyService : ServiceControl
{}

 

如果服务没有默认的构造函数,允许服务通过应用来创建从而指定构造函数,例如当你用到容器的时候。
HostFactory.New(x =>
{
    x.Service<MyService>(() => ObjectFactory.GetInstance<MyService>());
});

// Service implements the ServiceControl methods directly and has a default constructor
class MyService : ServiceControl
{
    public MyService(SomeDependency dependency)
    {}
}

 

 
如果在服务构造的过程中需要访问宿主设置,那么可以用方法重载实现
HostFactory.New(x =>
{
    x.Service<MyService>(hostSettings => new MyService(hostSettings));
});

// Service implements the ServiceControl methods directly and has a default constructor
class MyService : ServiceControl
{
    public MyService(HostSettings settings)
    {}
}

 

Custom Service

配置一个完整的常规服务,例如一个不依赖 Topshelf ,以下的配置可以实现
HostFactory.New(x =>
{
    x.Service<MyService>(sc =>
    {
        sc.ConstructUsing(() => new MyService());

        // the start and stop methods for the service
        sc.WhenStarted(s => s.Start());
        sc.WhenStopped(s => s.Stop());

        // optional pause/continue methods if used
        sc.WhenPaused(s => s.Pause());
        sc.WhenContinued(s => s.Continue());

        // optional, when shutdown is supported
        sc.WhenShutdown(s => s.Shutdown());
    });
});

 

Service Start Modes

服务的启动模式有多重,每种都可以通过配置的方式来指定,这个选项只有当服务被安装了才可以使用
HostFactory.New(x =>
{
    x.StartAutomatically(); // 自动
    x.StartAutomaticallyDelayed(); // 自动(延迟)
    x.StartManually(); // 手动
    x.Disabled(); // 设置不可用
});

 

 

Service Recovery

你也可以通过 Topshelf 暴露的选项来配置服务如何恢复
 
 
HostFactory.New(x =>
{
    x.EnableServiceRecovery(r =>
    {
        //you can have up to three of these
        r.RestartComputer(5, "message");
        r.RestartService(0);
        //the last one will act for all subsequent failures
        r.RunProgram(7, "ping google.com");

        //should this be true for crashed or non-zero exits
        r.OnCrashOnly();

        //number of days until the error count resets
        r.SetResetPeriod(1);
    });
});

 

Service Identity

服务同时可以被配置成运行时的身份验证
通过指定的用户名密码来运行,也可以通过命令行来配置,但一定要确认包含了当前域或者 upn 后缀 例如:domain\username 或 username@suffix.com
HostFactory.New(x =>
{
    x.RunAs("username", "password");
});

 

 
安装服务后,用来提示启动服务的用户名密码
 
HostFactory.New(x =>
{
    x.RunAsPrompt();
});

 

使用 NETWORK_SERVICE 内置账户
HostFactory.New(x =>
{
    x.RunAsNetworkService();
});

 

 
使用本地账户
HostFactory.New(x =>
{
    x.RunAsLocalSystem();
});

 

使用本地服务账户
HostFactory.New(x =>
{
    x.RunAsLocalService();
});

 

Custom Install Actions

以下这些配置允许用户在服务安装或者卸载过程中可以指定执行某些代码
 

Before Install Actions

服务安装前,被执行
HostFactory.New(x =>
{
    x.BeforeInstall(() => { ... });
});

 

After Install Actions    

服务安装后,被执行
 
HostFactory.New(x =>
{
    x.AfterInstall(() => { ... });
});

 

Before Uninstall Actions

服务卸载前,被执行
HostFactory.New(x =>
{
    x.BeforeUninstall(() => { ... });
});

 

 

After Uninstall Actions

服务卸载后,被执行
HostFactory.New(x =>
{
    x.AfterUninstall(() => { ... });
});

 

 

Service Dependencies

服务的依赖项也可以定制化,例如 服务 A 直到服务 B 启动它才会启动,不过这种依赖是Windows的服务管理控制的,并非Topshelf自己
 
HostFactory.New(x =>
{
    x.DependsOn("SomeOtherService");
});

 

还有一系列内置的扩展方法
 
 
HostFactory.New(x =>
{
    x.DependsOnMsmq(); // Microsoft Message Queueing
    x.DependsOnMsSql(); // Microsoft SQL Server
    x.DependsOnEventLog(); // Windows Event Log
    x.DependsOnIis(); // Internet Information Server
});

 

Advanced Settings

EnablePauseAndContinue

指定服务支持是否禁用、暂停、继续操作,允许服务控制管理器执行这些命令
HostFactory.New(x =>
{
    x.EnablePauseAndContinue();
});

 

 

EnableShutdown

指定服务支持关闭操作,允许服务控制管理器快速关闭服务
 
 
HostFactory.New(x =>
{
    x.EnableShutdown();
});

 

OnException

当服务允许异常是提供了一个回调,这个回调并不是一个处理程序,它也不会影响Topshelf魔人提供的异常处理,只是为了触发外部行为,记录日志等。
HostFactory.New(x =>
{
    x.OnException(ex =>
    {
        // Do something with the exception
    });
});

 

 

Service Recovery

指定服务的恢复选项,一个配置器可以用于一个或多个服务的恢复动作,恢复选项仅在安装的时候使用,并且一旦服务安装成功,这些选项就被设定好了。
 
HostFactory.New(x =>
{
    x.EnableServiceRecovery(rc =>
    {
        rc.RestartService(1); // restart the service after 1 minute
        rc.RestartSystem(1, "System is restarting!"); // restart the system after 1 minute
        rc.RunProgram(1, "notepad.exe"); // run a program
        rc.SetResetPeriod(1); // set the reset interval to one day
    })
});

 

恢复操作,会根据指定的顺序执行,当服务运行再次失败时,上一个行为执行完,这个行为才可以被执行。并且执行多少动作行为是有限制的,通常2-3个动作。
 

Logging Integration with Topshelf

默认情况下Topshelf使用资源跟踪路径的方法记录日志,这是.net framework的一部分,所以也不会有其他额外的依赖项,然而好多应用用了比较先进的日志库例如NLog等。为支持这种情况,Topshelf 向外暴露一个可扩展的日志接口。这可以将日志从你的代码中剥离出来,为了避免磁盘空间用完,你还可以使用现代的存储组件,例如ElasticSearch 和 influxDB.
using (var logary = ...Result)
    HostFactory.New(x =>
    {
        x.UseLogary(logary);
    });

 

 

log4net Integration

如果你要使用 log4net 记录日志,你需要从NuGet 上下载 Topshelf.Log4Net包,包添加到你的项目后,你可以借助配置文件来设置Topshelf来使用log4net
 
HostFactory.New(x =>
{
    x.UseLog4Net();
});

 

上面的代码就是将使用 HostLogger 改变为使用 log4net,UseLog4Net()有一个重载,允许通过配置文件设置log4net的日志路径以及日志别。
 

NLog Integration

使用NLog记录日志的话,你需要从NuGet上下载 Topshelf.NLog包。
 
posted @ 2019-04-30 21:34  NCat  阅读(446)  评论(0)    收藏  举报