【问题标题】:Dotnet 5.0 Service Runs as EXE but when starting service times out with 1053: Service did not respond in time to start or control requestDotnet 5.0 服务作为 EXE 运行,但启动服务时超时 1053:服务未及时响应以启动或控制请求
【发布时间】:2021-12-01 12:13:34
【问题描述】:

我有 Linux 运行服务的背景,第一次在 Microsoft 环境中工作。我敢肯定,许多领域的代码都可以改进,第一步是获得一个正在运行的服务。我参考了很多帖子和文档,但与运行 django 应用程序的 linux 环境相比,我还不能完全理解 Microsoft 服务和 dotnet 的交互方式。

我使用 Visual Basic for Mac 作为开发,dotnet 5.0 C# 环境来构建一个服务,该服务侦听目录以创建新文件,然后运行包含文件信息的脚本。我部署到具有 dotnet 5.0 环境的 Windows 10 x-64。我可以编译一个 .exe 并在目标机器上运行,它工作正常。然后我使用 sc.exe 构建服务,无论我是从服务管理器 GUI 还是从 Powershell 运行,服务都会超时。我试图弄清楚如何让服务继续运行。该服务将写入我的日志文件,如果将新文件添加到目标目录,它甚至会处理该文件,但它总是会超时并出现以下错误:

Error 1053: the service did not respond to the start or control request in a timely fashion

我的步骤和代码如下:

使用 Visual Studio for Mac 创建辅助服务。

我的Program.cs 是从模板构建的,并添加了一些其他现在为空的方法,但我认为会很有用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    //config.AddJsonFile("custom_settings.json");
                })
                .ConfigureLogging(logging =>
                {
                    //builder.AddFile("logs/app-{Date}.json"), isJson: true);
                })
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });
    }
}

接下来是Worker.cs:

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using HttpClientSample;

namespace MyService
{

    public class Worker : BackgroundService
    {

        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            string path = @"C:\log.txt";
            string startText = "Start" + Environment.NewLine;
            File.AppendAllText(path, startText);
            await ExecuteAsync(cancellationToken);
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            string path = @"C:\log.txt";
            string stopText = "Stop" + Environment.NewLine;
            File.AppendAllText(path, stopText);
            await base.StopAsync(cancellationToken);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {

            string path = @"C:\log.txt";
            string writeText = "Execute Async" + Environment.NewLine;
            File.AppendAllText(path, writeText);
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = @"C:\files\";
            watcher.NotifyFilter = NotifyFilters.FileName;
            watcher.Created += OnCreated;
            watcher.Filter = "*.*";
            watcher.EnableRaisingEvents = true;
            File.AppendAllText(path, "watcher engaged.");

            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("worker running at: {time}", DateTimeOffset.Now);
                try
                {
                    await Task.Delay(1000, stoppingToken);
                }
                catch (OperationCanceledException)
                {
                    break;
                }
            }
            
        }

        private static void OnCreated(object sender, FileSystemEventArgs e)
        {
            string path = @"C:\log.txt";
            string writeText = $"e: {e}";
            File.AppendAllText(path, writeText);
            MyProgram myProgram = new MyProgram(e.FullPath);
        }
    }
}

然后我在 MyProgram 中进行处理,一切正常。

对于服务,我首先使用以下命令编译以更正运行时:dotnet publish -c Release -r win10-x64。然后我将发布文件夹通过 FTP 传输到 Windows 10 机器,将 .exe 作为管理员配置文件运行,它工作正常,然后通过以下文件从文件创建服务:sc.exe create myService binPath= "C:\path\to\executable.exe"。然后我继续从服务管理器或使用sc.exe start myService 的 Powershell 运行此服务。大约 60 秒后,我收到 1053 错误。

我不明白保持服务运行需要什么。我尝试过的步骤如下:

  1. 我已确保在构建服务时指向 exe。
  2. 我已经按照几个网站的建议通过注册表增加了超时时间:here
  3. 我在 Event Viewer 中检查了 dotnet 或服务管理器问题,但无法找到问题的原因。
  4. 我已尝试使用本地系统帐户和管理员帐户来运行服务,但仍然超时。
  5. 我已将 InstallUtil 视为 sc.exe 的建议替代品,但我还没有尝试过。我不确定这个工具有多大的不同?
  6. 尝试通过服务管理器运行此服务时,使用 WindowsService 与 BackgroundService 会有所不同吗?
  7. 程序是否需要执行某些信号或返回值才能让服务知道继续运行?已经实现了 Task.delay() 并为 StartTask 和 ExecuteTask 方法返回值。

任何有关故障排除、解决问题或改进我的帖子的建议都将不胜感激。谢谢!

【问题讨论】:

    标签: c# .net windows .net-core windows-services


    【解决方案1】:

    服务进程必须调用正确的 dll 函数来指示它正在启动,并接收暂停/停止事件。

    在 .net 中,System.ServiceProcess.ServiceBase 中内置了这些 dll 调用的实现

    在 asp.net 中内置了一个服务的实现,我相信它早于通用主机的引入。只需调用 IWebHost.RunAsService() 就足以将 Windows 服务启动/停止事件粘合到 Web 主机启动/停止生命周期中。

    我相信您需要做的就是在配置主机构建器时调用.UseWindowsService()。这应该适用于控制台程序或网络主机。

    【讨论】:

    • 感谢您对服务流程事件的解释,我会在工作时牢记这一点。我之前看过 UseWindowsService 方法,觉得它看起来很有用。我尝试添加到我的主机构建器配置,但得到一个error CS1061: 'IHostBuilder' does not contain a definition for 'UseWindowsService'。将更新上面的代码以反映并继续进行故障排除。另外,我不确定我使用 IHostBuilder 是否重要,而您提到了 IWebHost?
    • UseWindowsServiceMicrosoft.Extensions.Hosting.WindowsServices 中的扩展方法。 dotnet core 最初是作为 asp.net mvc 的替代品。通用主机稍后出现。我怀疑IWebHost.RunAsService() 仅出于遗留原因而存在。
    • 谢谢,我需要通过 NuGet 添加Microsoft.Extensions.Hosting.WindowsServices,然后该方法才起作用。新构建后服务启动没有问题。
    猜你喜欢
    • 1970-01-01
    • 2013-12-20
    • 2014-07-02
    • 1970-01-01
    • 2022-01-17
    • 2011-03-28
    • 2014-08-05
    • 2011-01-05
    相关资源
    最近更新 更多