1053 Windows 服务错误,使用 .NET Core 3.1 工作器服务。

3
我创建了一个Worker Service,并在Visual Studio 2019的调试和发布模式下正常工作。该服务会监视指定目录下的.csv文件,将其以正确编码(UTF-8)重写到另一个目录下。但是,在我发布并创建Windows服务后,启动Windows服务时出现“Error 1053: The service did not respond to the start or control request in a timely fashion”错误。我理解这是因为我的OnStart方法没有及时返回,但不确定如何进行调试。 程序类
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using System;

namespace SupplyProUTF8service
{
    public class Program
    {
        public static void Main(string[] args)
        {

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .WriteTo.File(@"\\REP-APP\temp\workerservice\log.txt", rollingInterval: RollingInterval.Day)
                .CreateLogger();

            try
            {
                Log.Information("Application Started.");
                CreateHostBuilder(args).Build().Run();

            }
            catch (Exception e)
            {

                Log.Fatal(e, "Application terminated unexpectedly");
            }
            finally
            {
                Log.CloseAndFlush();
            }

            CreateHostBuilder(args).Build().Run();

        }


        public static IHostBuilder CreateHostBuilder(string[] args)
            => Host.CreateDefaultBuilder(args).UseWindowsService().ConfigureServices((hostContext, services)
                => { services.AddHostedService<Worker>(); }).UseSerilog();
    }
}

新的工作类 仍然出现1053错误

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace SupplyProUTF8service
{
    public class Worker : BackgroundService
    {

        private readonly string ordrstkPath;
        private readonly string conrstkPath;
        private readonly ILogger<Worker> _logger;

        public Worker(ILogger<Worker> logger)
        {
            ordrstkPath = @"\\Rep-app\sftp_root\supplypro\ordrstk";
            conrstkPath = @"\\Rep-app\sftp_root\supplypro\Conrstk";
            _logger = logger;
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {

            _logger.LogInformation("SupplyProRewrite Service started");
            return base.StartAsync(cancellationToken);
        }


        private FileSystemWatcher Watch(string path)
        {
            //initialize
            FileSystemWatcher watcher = new FileSystemWatcher
            {

                //assign paramater path
                Path = path,

                //don't watch subdirectories
                IncludeSubdirectories = false
            };

            //file created event
            watcher.Created += FileSystemWatcher_Created;

            //filters
            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size | NotifyFilters.Attributes;

            //only look for csv
            watcher.Filter = "*.csv";

            // Begin watching.
            watcher.EnableRaisingEvents = true;

            return watcher;
        }
        private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {
            _logger.LogInformation("{FullPath} has been created", e.FullPath);
            Thread.Sleep(10000);
            while (!IsFileLocked(e.FullPath))
            {
                ReadWriteStream(e.FullPath, e.Name);
                break;
            }
        }

        private static bool IsFileLocked(string filePath)
        {
            try
            {
                using FileStream originalFileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
                originalFileStream.Close();
            }
            catch (Exception)
            {
                return true;
            }
            return false;
        }

        private void ReadWriteStream(string path, string fileName)
        {

            string originalPath = path;
            //destination path by replacing SFTP user directory
            string destinationPath = path.Replace(@"\supplypro\", @"\ftpuser\");

            string currentLine;

            using FileStream originalFileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
            using FileStream destinationFileStream = new FileStream(destinationPath, FileMode.Create, FileAccess.Write);
            using StreamReader streamReader = new StreamReader(originalFileStream);
            using StreamWriter streamWriter = new StreamWriter(destinationFileStream);
            try
            {
                currentLine = streamReader.ReadLine();
                while (currentLine != null)
                {

                    streamWriter.WriteLine(currentLine);
                    currentLine = streamReader.ReadLine();

                }

                streamReader.Close();
                streamWriter.Close();

                //archive path
                string archivePath = path.Replace(fileName, @"archive\" + fileName);

                //move to archive path
                while (!IsFileLocked(originalPath))
                {
                    try
                    {
                        File.Move(originalPath, archivePath, true);
                        _logger.LogInformation("{FileName} moved to archive", fileName);
                        break;
                    }
                    catch (Exception e)
                    {
                        _logger.LogError("Unable to move {fileName} to archive", fileName, e);
                        break;
                    }
                }


            }
            catch (Exception e)
            {
                //error path
                string errorPath = path.Replace(fileName, @"error\" + fileName);

                //move to error path
                while (!IsFileLocked(originalPath))
                {
                    File.Move(path, errorPath);
                    _logger.LogError("{FullPath} file was moved to error", originalPath, e);
                    break;
                }

            }
            finally
            {
                destinationFileStream.Close();
                originalFileStream.Close();

            }
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            using (Watch(ordrstkPath))
            {
                _logger.LogInformation("ordrstk being watched");
                await Task.Delay(Timeout.Infinite, stoppingToken);
            }

            using(Watch(conrstkPath))
            {
                _logger.LogInformation("conrstk being watched");
                await Task.Delay(Timeout.Infinite, stoppingToken);
            }
        }
    }
}

你对这个模式的使用方式有误。BackgroundService不应该这样工作。相反,应该实现IHostedService而不是BackgroundService,并重写StartAsyncStopAsync方法。 - undefined
你应该使用ExecuteAsync而不是StartAsync吗?虽然我对Worker Service的经验还不够丰富,但我有一个类似的程序,它始终在监视,并且所有功能都在ExecuteAsync中。我认为StartAsync的代码没有完成,这就是为什么它会给出你所看到的响应的原因? - undefined
@Andy完全不正确。Worker Service项目的模板(用于使用.NET Core编写Windows服务)生成一个名为Worker的类,该类继承自BackgroundService,并提供了一个用于实现ExecuteAsync的存根方法。 - undefined
@Andy 我有条件地撤回我之前的评论。我所描述的是微软目前打算如何完成的方式。但是,存在这个隐匿的错误,解决方法涉及以旧的方式进行操作。 - undefined
4个回答

5

我不确定为什么我一开始没有这样做。对于那些花时间查看的人,我表示歉意。最终我在Powershell中运行了应用程序,并输入.\WorkerService.exe。这导致一个错误,通知我丢失了ASP.net Core的运行时。我之前已经安装了.NET Core并最近安装了5.0版本的运行时。但是这个工作服务可能需要特定的ASP.Net Core与托管运行时:下载。现在服务可以无问题启动。


4

在 Windows 11 计算机上使用 Visual Studio 2022 默认的 Dot Net Core 6 服务项目模板时出现了相同的问题。 在主机声明后使用 UseWindowsService()

IHost host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
    services.AddHostedService<Worker>();
}).UseWindowsService()
.Build();await host.RunAsync();

2
那是我的第二个案例。对于ASP.NET Core 7应用程序:A. 引用此包:"Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.0" B. 将以下内容添加到您的program.cs文件中:builder.Host.UseWindowsService(); - undefined
非常感谢你们两位。在这个问题上,Windows日志中没有任何错误。 - undefined

1

由于FileSystemWatcher不是async关键字意义上的异步,而是IDisposable,因此您的ExecuteAsync可能如下所示:

protected override async Task ExecuteAsync(CancellationToken cancel)
{
    using (/* method that sets up and returns your watcher */)
    {
        await Task.Delay(Timeout.Infinite, cancel);
    }
}

这里没有展示,但是当服务关闭时,您可能需要捕获由Task.Delay抛出的TaskCanceledException


好的,所以我的Watch方法应该在ExecuteAsync中被调用。现在我的当前Watch方法并不返回一个观察者,它只是触发我处理的创建事件。我应该用另一种方式来处理这个问题吗? - undefined
@RyanFreemark 我建议编写一个返回观察者的方法,以方便使代码更易读。我的示例代码只是一种简单的方式,确保在服务停止时观察者被正确清理。 - undefined
我已经将我的Watch方法移动到ExecuteAsync中,但是在进行干净发布和新服务时仍然收到1053错误。有没有办法可以调试这个问题?我感到很困惑。 - undefined

0

使用UseWindowsService()修复了错误1053,感谢您的建议。

我看到在NET core 3中不是这样的。

using WindowsService;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    }).UseWindowsService ()
    .Build();

host.Run();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接