【问题标题】:NServicebus with File System Watcher带有文件系统观察器的 NServicebus
【发布时间】:2015-03-21 18:40:34
【问题描述】:

我希望我的端点在检测到特定文件夹中的文件被删除时发送一个事件。我能够通过使用实现 IWantToRunWhenBusStartsAndStops 的类来使其工作,该类又设置了 FileSystemWatcher 来监视给定的文件夹。我的问题是,这是使用 nservicebus 解决此问题的最佳方式,还是我遗漏了一些可能会给我带来麻烦的东西?

这是我的代码:

public class FileSystem : IWantToRunWhenBusStartsAndStops
{
    private FileSystemWatcher watcher;

    public void Start()
    {
        ConfigFileWatcher();
    }

    public void Stop()
    {

    }

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    private void ConfigFileWatcher()
    {
        watcher = new FileSystemWatcher();
        watcher.Path = @"c:\";
        /* Watch for changes in LastAccess and LastWrite times, and
           the renaming of files or directories. */
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
           | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        // Only watch text files.
        watcher.Filter = "*.txt";

        // Add event handlers.
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);

        // Begin watching.
        watcher.EnableRaisingEvents = true;
    }

    // Define the event handlers. 
    private static void OnChanged(object source, FileSystemEventArgs e)
    {
        // Specify what is done when a file is changed, created, or deleted.
        Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);

        // fire off an event here...
    }

}

【问题讨论】:

  • 您的问题/顾虑到底是什么?
  • 我想我只是想知道是否有更好的方法可以做到这一点和/或这种方法是否存在任何性能问题? IWantToRunWhenBusStartsAndStops 接口是否打算这样使用?
  • 你测试过它可以工作吗?公车启动后不会被处理掉吗?
  • 是的,它确实有效,即使在公共汽车启动后它仍继续有效。我还以为它会被处理掉。这就是为什么我不确定这是否是正确的方法,因为它感觉“错误”:)

标签: c# nservicebus filesystemwatcher


【解决方案1】:

根据其他答案和 cmets,我相信您的 FileSystemWatcher 通过订阅 OnChanged 事件而保持活动状态。

这实际上是(相当)常见的内存泄漏原因,尽管在您的情况下,它实际上将您的观察者保留在内存中并允许它继续运行。从您的 OnChanged() 方法中删除 static 关键字,我相信您会看到您所期望的行为。

当然,您希望 FileSystemWatcher 继续运行。 . .一种选择可能是将观察者变量本身设为静态。如果你走那条路,我会把它放在一个单独的类中,然后通过实现 IWantToRunWhenBusStartsAndStops 的类对其进行初始化。

【讨论】:

  • 但是什么能让它保持活力呢?如果 Runner:IWantToRun... 类死亡,静态 FileWatcher.watcher 变量不会随之死亡吗?或者你是说在新的 FileWatcher 类中使 OnChanged 和观察者变量都是静态的? (在这种情况下,就像现在发生的那样,观察者仍然活着......)
  • 如果它是静态的,即使在类超出范围或被释放后它也会保持活动状态。
【解决方案2】:

如果你看NServiceBus源码,在容器初始化中,你会看到IWantToRunWhenBusStartsAndStops注册了一个每次调用的生命周期

ForAllTypes<IWantToRunWhenBusStartsAndStops>(TypesToScan, t => configurer.ConfigureComponent(t, DependencyLifecycle.InstancePerCall));

这意味着该类将在 Start() 被调用后被释放。您的实现有效,因为您的事件订阅了静态处理程序,该处理程序使订阅保持活动状态。

我们在生产中使用文件观察器,但我们将它们烘焙为高级卫星。卫星保证被初始化为单例并且不会被丢弃。它们也有 Start 和 Stop 方法。他们确实有地址并且应该能够处理传入的消息,但是您可以使用一些虚拟地址并且在处理程序中什么也不做,除非您想让您的文件系统观察程序卫星双向(即接收消息并将它们作为文件放在磁盘上)。

在 NServiceBus 中,建议将持续运行的非一次性进程设置为卫星。许多 NServiceBus 组件被制成卫星。

您可能会对如何自己制造卫星感到好奇,但这很容易做到。可以查看接口签名here

它看起来像这样

using System;
using System.IO;
using NServiceBus;
using NServiceBus.Satellites;

public class FileSystem : ISatellite
{
    private FileSystemWatcher _watcher;

    public bool Handle(TransportMessage message)
    {
        return true;
    }

    public void Start()
    {
        _watcher = new FileSystemWatcher
        {
            Path = @"c:\",
            NotifyFilter = NotifyFilters.LastAccess |    NotifyFilters.LastWrite
                           | NotifyFilters.FileName | NotifyFilters.DirectoryName,
            Filter = "*.txt"
        };
        _watcher.Changed += OnChanged;
        _watcher.Created += OnChanged;
        _watcher.Deleted += OnChanged;
        _watcher.EnableRaisingEvents = true;
    }

    public void Stop()
    {
        _watcher.Dispose();
    }

    public Address InputAddress
    {
        get { return Address.Parse("FileSystemSatellite"); }
    }

    public bool Disabled
    {
        get { return false; }
    }

    // Define the event handlers. 
    private void OnChanged(object source, FileSystemEventArgs e)
    {
        // Specify what is done when a file is changed, created, or    deleted.
        Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);

        // fire off an event here...
    }
}

您需要记住一件事:每颗卫星都有自己的队列。在这种情况下,它将始终为空。

【讨论】:

  • 这是有道理的,但奇怪的是,即使在调用 Start() 之后,File Watcher 事件也会继续触发。也许这个观察者资源正在阻止对象被处置?你有任何卫星文件吗?如果有帮助,我正在使用 5.0 版。
  • 正如菲尔在他的回答中提到的那样,因为您的事件处理程序是一个静态方法,所以订阅不会被释放,并且观察者由 GC 保持活动状态。我用这条评论编辑了我的答案,并添加了一个示例卫星实现,
  • 什么会导致卫星类开始?我用 Nuget 的 NServiceBus.Host 做了一个项目(使用 RavenDB 持久性)。当我从具有 IWantToRunWhenBusStartsAndStops 的类中运行静态 FileSystemWatcher 时,它运行正常并提供了文件更改事件。但是,当我根据 Alexy 的示例将其更改为使用实现 ISatellite 的类中的非静态 FileSystemWatcher 时,永远不会调用 Start() 方法。我还需要做些什么吗?
  • 我不使用他们的内部容器 API,但使用 Autofac,您需要使用 .AsImplementedInterfaces() 注册它,以便消费者能够将其解析为 ISatellite
  • 在与 NServiceBus 支持的 Hadi 交谈后,我发现我所要做的就是将 Disabled 参数设置为返回 false。根本不需要配置行(尽管它有效)。卫星“开箱即用”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 2011-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多