作者: Dotnet Core Tutorials
译者: Lamond Lu
译文: 使用. NET Core 创建 Windows 服务(一) - 使用官方推荐方式
创建 Windows 服务来运行批处理任务或者运行后台任务, 是一种非常常见的模式, 但是由于云服务 (Amazon Lambda, Azure webJobs 以及 Azure Functions) 的激增, 你可能不会经常使用 Windows 服务了. 个人而言, 我非常喜欢使用 Azure WebJobs, 因为我可以直接编写一个控制台程序, 而不需要考虑如何云中运行它, 一个批处理文件可以将其装换成一个自动化任务, 并且可以保证 7*24 小时的运行.
但是也许你还没有使用云服务, 或者你有一堆要作为 Windows 服务运行的旧版应用程序需要转换为. NET Core, 但是不能完全将他们转换为 "无服务器"(serverless)应用. 那么这边文章就是适合你的.
在许多方面,.NET Core 中的 Windows 服务和. NET Framework 中的 Windows 服务完全相同. 但是, 在编写服务的时候, 你可能会遇到一些小问题. 此外, 本文中, 我们仅介绍 "Microsoft" 方式的 Windows 服务创建, 在后续, 我会继续介绍如何使用第三方库 TopShelf 来简化这该过程.
安装
由于 Visual Studio 没有提供创建 Windows 服务的模板, 所以我们需要通过创建控制台程序的方式来创建一个 Windows 服务.
创建完成之后, 我们需要安装一个 Nuget 程序包, 这个程序包会将一些 Windows 特定的 API 添加到. NET Core 中, 这些 API 实际上已经在完整框架中提供了, 但是其中许多是 Windows 特有的, 例如 Windows 服务. 因此, 它们并没有包含在. NET Core 的基础库中, 但是可以通过将 Nuget 程序包的方式引入到. NET Core 中.
下面我们就可以在 Package Manager Console 中输入以下命令.
Install-Package Microsoft.Windows.Compatibility
代码
以上引入的 Nuget 程序包中, 最让我们感兴趣的是 ServiceBase 类. 这是一个用于编写 Windows 服务的基类, 它提供了一系列的事件钩子, 包含服务启动, 结束, 暂停等.
下面呢, 我们将在代码中创建一个类, 这个类负责将一些简单的日志输出到一个临时文件中. 我们将使用这个例子来了解其中的原理. 我们的代码如下:
- class LoggingService : ServiceBase
- {
- private const string _logFileLocation = @"C:\temp\servicelog.txt";
- private void Log(string logMessage)
- {
- Directory.CreateDirectory(Path.GetDirectoryName(_logFileLocation));
- File.AppendAllText(_logFileLocation, DateTime.UtcNow.ToString() + ":" + logMessage + Environment.NewLine);
- }
- protected override void OnStart(string[] args)
- {
- Log("Starting");
- base.OnStart(args);
- }
- protected override void OnStop()
- {
- Log("Stopping");
- base.OnStop();
- }
- protected override void OnPause()
- {
- Log("Pausing");
- base.OnPause();
- }
- }
所以这里你会注意到, 我们的类是继承了 ServiceBase 类, 并且我们重写了几个事件方法, 输出了一些日志. 在服务启动时, 会触发 OnStart 事件, 在服务终止的时候, 会触发 OnStop 事件. 这里我们不应该将过于繁重的任务放置在 OnStart 事件中来处理.
如果我们想从 Main 方式中启动这个服务, 代码非常的简单.
- static void Main(string[] args)
- {
- ServiceBase.Run(new LoggingService());
- }
以上就是全部代码.
服务部署
在发布服务的时候, 我们不可能仅依靠 Visual Studio 来构建我们所需要的服务, 我们还需要专门针对 Windows 运行时进行构建. 为此, 我们需要在项目根目录的命令提示符下运行以下命令. 注意, 这里我们传入了一个 - r 标记来告诉它要构建那个平台.
dotnet publish -r win-x64 -c Release
命令运行完毕之后, 我们可以检查以下 / bin/release/netcoreappX.X/publish 目录, 我们可以找到所有的发布代码, 但是最重要的是, 这里我们可以得到一个可执行的 exe 文件. 如果我们不指定运行时, 我们只会获得一个. NET Core 的 dll 程序集, 使用这个程序集, 我们是没有办法创建 Windows 服务的.
现在我们可以将这个发布目录移动带其他的任何地方, 但是现在我们就暂时使用当前的发布目录.
下一步, 我们需要使用管理员角色打开一个命令提示符, 然后输入一下命令.
sc create TestService BinPath=C:\full\path\to\publish\dir\WindowsServiceExample.exe
SC 命令是一个标准的 Windows 命令(与. NET Core 无关), 它可以用来安装 Windows 服务. 这里我们将我们的测试服务命名为 TestService, 更重要的是, 我们通过 BinPath 参数指定了可执行 exe 文件.
运行之后, 我们应该会得到以下结果.
[SC] CreateService SUCCESS
然后我们要做的就是启动服务.
sc start TestService
现在我们可以查看一下我们的日志文件, 查看服务的运行情况.
如果想要停止并删除服务, 我们可以使用一下命令.
- sc stop TestService
- sc delete TestService
服务调试
在这里, 我真的认为, 使用 "Microsoft" 的方式注定会失败. 因为调试服务实在是太繁琐了.
首先, 我们将 ServiceBase 中重写的方法设置为受保护, 这意味着我们无法在类之外访问它们, 这使得调试它们变得更加困难. 这里我发现最好的方法是为每个事件提供一个 public 方法, 并在受保护方法中调用这些 public 方法来完成功能, 这虽然有点混乱,
- public void OnStartPublic(string[] args)
- {
- Log("Starting");
- }
- protected override void OnStart(string[] args)
- {
- OnStartPublic(args);
- base.OnStart(args);
- }
但是至少我们可以做如下了事情了.
- static void Main(string[] args)
- {
- var loggingService = new LoggingService();
- if (true) //Some check to see if we are in debug mode (Either #IF Debug etc or an App setting)
- {
- loggingService.OnStartPublic(new string[0]);
- while(true)
- {
- //Just spin wait here.
- Thread.Sleep(1000);
- }
- //Call stop here etc.
- }
- else
- {
- ServiceBase.Run(new LoggingService());
- }
- }
你的另一个选择是, 在调试模式下进行项目发布, 安装服务, 然后附加调试器. 实际上, 这是 Microsoft 建议你使用的方式, 但是我认为这简直一团糟.
后续
实际上, 我们可以在这里做一些其他非常有用的事情, 比如我们可以通过创建一个 install.bat 批处理文件来为我们运行 SC Create 命令. 但我认为, 上面我们看到的调试问题, 已经让我不再想使用这种方式了. 幸运的是, 有一个名为 Topshelf 的库可以帮助我们减轻很多麻烦, 在本系列的下一部分中, 我们将研究如何它.
使用. NET Core 创建 Windows 服务(一) - 使用官方推荐方式
来源: http://www.bubuko.com/infodetail-3224443.html