Windows 服务 .Net 8 上的 FileSystemWatcher [IHostedService]

问题描述 投票:0回答:1

我正在尝试使用 .Net 8 创建一个 Windows 服务应用程序作为托管服务,我的应用程序的想法是监视文件夹列表。问题是我正在丢失由

Created
触发的事件
FileSystemWatcher
。在上下文中,我的应用程序应该以这种方式工作:

  1. 在观察者文件夹内创建一个文件。
  2. 文件通过扩展名(.txt 或 .zip)进行验证,而不是空内容。
  3. 文件已上传到AWS Bucket。
  4. 文件已从文件夹中删除。

这是我的快乐之路,当我调试某些文件时效果很好,但如果文件上传非常重,那么应用程序会丢失

FileSystemWatcher
的事件。

我正在尝试用生产者/消费者模式解决这个问题。我关注这些文章:

https://medium.com/@dayanandthombare/background-services-in-net-core-c32f15e28678#:~:text=%E2%9C%85%20Solution%3A%20Background%20Service%20for%20Asynchronous%20File %20正在处理

FileSystemWatcher 在队列中丢失文件

但对我不起作用。

我的代码看起来:

// Worker.cs
public class Worker : BackgroundService
{
private readonly ConcurrentQueue<FileTask> _fileTasks = new ConcurrentQueue<FileTask>();
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            _logger.LogInformation("Starting sync files.");

            _synchronizer.SetUp();

            while (!stoppingToken.IsCancellationRequested)
            {
                await _signal.WaitAsync(stoppingToken);

                if (_fileTasks.TryDequeue(out FileTask? task) && task is not null && task.Process is not null && task.FileRelation is not null)
                {
                    await task.Process(task.FileRelation, stoppingToken);
                }

                await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
            }
        }
        catch (Exception exception)
        {
            _logger.LogError($"An unhandled error has occurred. Exception {exception}");
            throw;
        }
    }
}

// FileServiceSynchronizer.cs
public class FileServiceSynchronizer : ISynchronizer
{
public void SetUp()
{
 Task.Factory.StartNew(() => 
                    {
                        _logger.LogInformation($"[Watching] {location.FileSystemLocation}");

                        CustomFileSystemWatcher watcher = new CustomFileSystemWatcher(location.FileSystemLocation)
                        {
                            IncludeSubdirectories = true,
                            EnableRaisingEvents = true,
                            Filter = "",
                            CloudLocation = location.CloudLocation,
                            FileSystemErrorLocation = location.FileSystemErrorLocation,
                            NotifyFilter = NotifyFilters.DirectoryName
                                        | NotifyFilters.FileName
                                        | NotifyFilters.LastAccess
                                        | NotifyFilters.LastWrite
                        };

                        watcher.Created += OnFileCreated;
                    });
}

//_fileRead.Process: Validate and Upload to Bucket.
private void OnFileCreated(object sender, FileSystemEventArgs e)
{
worker.EnqueuedFileTask(new FileTask()
                {
                    FileRelation = new RelationLocation()
                    {
                        FileSystemLocation = e.FullPath,
                        CloudLocation = request.CloudLocation,
                        FileSystemErrorLocation = request.FileSystemErrorLocation
                    },
                    Process = _fileRead.Process
                });

}
}

实际上,我正在尝试使用 Sqlite 解决此问题,其中我将事件(文件路径)发送到数据库(而不是排队文件任务),然后在 Worker 类中尝试获取路径和进程。但我仍然会输掉一些事件,直到什么都没有了。

更新

为了最终一切正常,只需将我的

FileSystemWatcher
更改为FileWatcherEx并保留此处使用的模式,这个Nuget可以以最佳方式管理发生的所有事件。

windows-services .net-8.0 producer-consumer filesystemwatcher concurrent-queue
1个回答
0
投票

我至少看到一个问题:

Task.Factory.StartNew(() => 
{
    _logger.LogInformation($"[Watching] {location.FileSystemLocation}");

    CustomFileSystemWatcher watcher = new CustomFileSystemWatcher(location.FileSystemLocation)
    {
        //...
    };

    watcher.Created += OnFileCreated;
});

您正在声明一个局部变量,该变量将在下次运行时由 GC 收集,从而有效地停止文件观察器,将其分配给

FileServiceSynchronizer
字段:

public class FileServiceSynchronizer : ISynchronizer
{
    private CustomFileSystemWatcher _watcher;

    public void SetUp()
    {
        _watcher = new CustomFileSystemWatcher(location.FileSystemLocation)
        // ...
    }
}

这里也没有理由使用

TaskFactory

© www.soinside.com 2019 - 2024. All rights reserved.