Лучший способ справиться с изменением узлов? - Наблюдатель за файлами C# и кластеризованный файловый сервер

Недавно мы посетили кластерный файловый сервер CSVFS. У меня есть наблюдатель за файлами, который отслеживает 4 каталога для событий OnCreated и OnRenamed, но всякий раз, когда узел изменяется, это вызывает переполнение буфера с ошибкой

Слишком много изменений одновременно в каталоге

Наблюдатели автоматически перезапускаются, и процесс продолжает работать, но начинает писать ошибки при запуске событий OnCreated/OnRenamed.

Не удается получить доступ к ликвидированному объекту.
Имя объекта: 'FileSystemWatcher'.
at System.IO.FileSystemWatcher.StartRaisingEvents()
at System.IO.FileSystemWatcher.set_EnableRaisingEvents(логическое значение)

В приведенном ниже методе OnCreated, если бы я это сделал, должно ли это сработать?

watchit = source as FileSystemWatcher;

На самом деле я нигде больше не назначаю только что созданный FileSystemWatcher watchit.

Подробнее/код

Наблюдатели создаются с помощью цикла foreach при первоначальном запуске процесса. FileChange — это просто метод, который определяет тип изменения, выполняет некоторую работу, а затем инициирует правильное действие для типа изменения.

foreach (string subDir in subDirs)
{
    string localFolder = $"{subDir}";

    watchit = new FileSystemWatcher
                    {
                        Path = localFolder,
                        EnableRaisingEvents = true,
                        IncludeSubdirectories = false,
                        NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime,
                        Filter = watchFor,
                        InternalBufferSize = 65536,
                        SynchronizingObject = null //,
                    };

    watchit.Changed += FileChange;
    watchit.Created += FileChange;
    watchit.Deleted += FileChange;
    watchit.Renamed += OnRename;
    watchit.Error += OnError;
    watchit.EnableRaisingEvents = true;

    watchers.Add(watchit);

    Console.WriteLine($"watching {subDir} for {watchFor}");
}

watchit — это статический FileSystemWatcher глобальный набор.

private static async Task<int> OnCreated<T>(object source, FileSystemEventArgs e, string ext)
{
    int insertResult = 0;

    try
    {
        watchit.EnableRaisingEvents = false;
        EventLogWriter.WriteEntry("File: " + e.FullPath + " " + e.ChangeType);
        Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType + " " + DateTime.Now);
        insertResult = await FileHandler.FileHandlers().ConfigureAwait(false);
        watchit.EnableRaisingEvents = true;
        // if (insertResult > 0) File.Delete(e.FullPath);
    }
    catch (Exception ex)
    {
        Logger.Trace($"{ex.Message} {ex.StackTrace} {ex.InnerException}");
        EventLogWriter.WriteEntry($"{ex.Message} {ex.StackTrace} {ex.InnerException}",
        EventLogEntryType.Error);
        watchit.EnableRaisingEvents = true;
    }
    finally
    {
        watchit.EnableRaisingEvents = true;
    }

    return insertResult;
}

Это мои методы обработки ошибок.

private static void OnError(object source, ErrorEventArgs e)
{
    if (e.GetException().GetType() == typeof(InternalBufferOverflowException))
    {
        EventLogWriter.WriteEntry($"Error: File System Watcher internal buffer overflow at {DateTime.Now}", EventLogEntryType.Warning);
    }
    else
    {
        EventLogWriter.WriteEntry($"Error: Watched directory not accessible at {DateTime.Now}", EventLogEntryType.Warning);
    }

    MailSend.SendUploadEmail($"ASSIST NOTES: {e.GetException().Message}", "The notes service had a failure and should be restarted.", "admins", e.GetException(), MailPriority.High);
    NotAccessibleError(source as FileSystemWatcher, e);
}

    /// <summary>
    /// triggered on accessible error.
    /// </summary>
    /// <param name="source">The source.</param>
    /// <param name="e">The <see cref="ErrorEventArgs"/> instance containing the event data.</param>
    private static void NotAccessibleError(FileSystemWatcher source, ErrorEventArgs e)
        {
        EventLogWriter.WriteEntry($"Not Accessible issue. {e.GetException().Message}" + DateTime.Now.ToString("HH:mm:ss"));

        int iMaxAttempts = 120;
        int iTimeOut = 30000;
        int i = 0;
        string watchPath = source.Path;
        string watchFilter = source.Filter;
        int dirExists = 0;
        try
            {
            dirExists = Directory.GetFiles(watchPath).Length;
            }
        catch (Exception) { }
        try
            {
            while (dirExists == 0 && i < iMaxAttempts)
                {
                i += 1;
                try
                    {
                    source.EnableRaisingEvents = false;
                    if (!Directory.Exists(source.Path))
                        {
                        EventLogWriter.WriteEntry(
                            "Directory Inaccessible " + source.Path + " at " +
                            DateTime.Now.ToString("HH:mm:ss"));
                        Console.WriteLine(
                            "Directory Inaccessible " + source.Path + " at " +
                            DateTime.Now.ToString("HH:mm:ss"));
                        System.Threading.Thread.Sleep(iTimeOut);
                        }
                    else
                        {
                        // ReInitialize the Component
                        source.Dispose();
                        source = null;
                        source = new System.IO.FileSystemWatcher();
                        ((System.ComponentModel.ISupportInitialize)(source)).BeginInit();
                        source.EnableRaisingEvents = true;
                        source.Filter = watchFilter;
                        source.Path = watchPath;
                        source.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime;
                        source.Created += FileChange;
                        source.Renamed += OnRename;
                        source.Error += new ErrorEventHandler(OnError);
                        ((System.ComponentModel.ISupportInitialize)(source)).EndInit();
                        EventLogWriter.WriteEntry(
                            $"Restarting watcher {watchPath} at " + DateTime.Now.ToString("HH:mm:ss"));
                        dirExists = 1;
                        }
                    }
                catch (Exception error)
                    {
                    EventLogWriter.WriteEntry($"Error trying Restart Service {watchPath} " + error.StackTrace +
                                               " at " + DateTime.Now.ToString("HH:mm:ss"));
                    source.EnableRaisingEvents = false;
                    System.Threading.Thread.Sleep(iTimeOut);
                    }
                }
            //Starts a new version of this console appp if retries exceeded
            //Exits current process
            var runTime = DateTime.UtcNow - Process.GetCurrentProcess().StartTime.ToUniversalTime();
            if (i >= 120 && runTime > TimeSpan.Parse("0:00:30"))
                {
                Process.Start(Assembly.GetExecutingAssembly().Location);
                Environment.Exit(666);
                }
            }
        catch (Exception erw) { }
        }

person Andrew    schedule 17.11.2017    source источник


Ответы (1)


Вы пытаетесь сделать слишком много работы в событиях FileSystemWatcher. Наблюдатели поддерживаются неуправляемыми буферами, которые необходимо очищать как можно быстрее, чтобы не отставать от изменений.

В идеале все события должны считывать некоторые очень простые данные, такие как измененный путь и тип изменения, и помещать их в очередь для обработки в другом потоке. Этот другой поток может выполнять тяжелую работу, поскольку он не будет блокировать неуправляемый буфер изменений.

person Bradley Uffner    schedule 17.11.2017