Перезапустить сервис с зависимыми сервисами? - PullRequest
10 голосов
/ 03 октября 2011

Начиная с csharp-примера и должным образом отмечая связанные вопросы SO ( Перезапустите службы Windows из C # и Невозможно перезапустить службу ) и различные другие вопросыОтносительно перезапуска только одной службы , мне интересно, как лучше всего перезапустить службу с зависимыми службами (например, Message Queuing, на которой Message Queuing Triggers зависит, или IIS, от которого зависят FTP Publishing и World Wide Web Publishing).Оснастка mmc делает это автоматически, но, похоже, код не обеспечивает такую ​​же функциональность (по крайней мере, не так просто).

Документация MSDN для Stop говорит: «Если какие-либо службызависит от этой службы для их работы, они будут остановлены до того, как эта служба будет остановлена. Свойство DependentServices содержит набор служб, которые зависят от этой службы ", и DependentServices возвращает массив служб.Предполагая, что StartService() и StopService() следуют соглашениям, изложенным в примерах, и указанным выше (за исключением того, что они принимают ServiceControllers и TimeSpans напрямую), я начал с:

public static void RestartServiceWithDependents(ServiceController service, TimeSpan timeout)
{
    ServiceController[] dependentServices = service.DependentServices;

    RestartService(service, timeout); // will stop dependent services, see note below* about timeout...

    foreach (ServiceController dependentService in dependentServices)
    {
        StartService(dependentService, timeout);
    }
}

Но что еслисервисные зависимости являются вложенными (рекурсивными) или циклическими (если это даже возможно ...) - если Service A равно зависит от от Service B1 и Service B2 и Service C1 зависит от Service B1, кажется, что «перезапуск» Service A таким способом остановит Service C1, но не перезапустит его ...

Чтобы сделать этот пример более понятным, я буду следоватьмодель в оснастке mmc служб:

The following system components depend on [Service A]:
  - Service B1
    - Service C1
  - Service B2

Есть ли лучший способ сделать это, или нужно просто рекурсивно входить и останавливать каждую зависимую службу, а затем перезапускать их все после перезапускаосновной сервис?

Кроме того, будут ли зависимые но в настоящее время остановленные сервисы перечислены в списке DependentServices?Если так, разве это не перезапустит их в любом случае?Если да, то должны ли мы это контролировать?Кажется, что это становится все более и более запутанным ...

* Примечание: я понимаю, что timeout применяется не совсем корректно (общее время ожидания может быть во много раз больше, чем ожидалось), но сейчасэто не та проблема, о которой я беспокоюсь - если вы хотите это исправить, хорошо, но не говорите «тайм-аут сломан ...»

Обновление: После некоторого предварительного тестированияЯ обнаружил (/ подтвердил) следующее поведение:

  • Остановка службы (например, Service A), от которой зависят другие службы (например, Service B1), остановит другие службы (включая "вложенные "зависимости", такие как Service C1)
  • DependentServices , включают зависимые службы во всех состояниях (работает, остановлен и т. д.), а также включаютвложенные зависимости, т.е. Service_A.DependentServices будет содержать {Service B1, Service C1, Service B2} (в этом порядке, поскольку C1 зависит от B1).
  • Запуск службы, зависящей от других (например, Service B1 зависит Service A) также запустит необходимые службы.

TПоэтому приведенный выше код можно упростить (по крайней мере, частично), чтобы просто остановить основной сервис (который остановит все зависимые сервисы), а затем перезапустить наиболее зависимые сервисы (например, Service C1 и Service B2) (или просто перезапустить "все "зависимые сервисы - он пропустит уже запущенные), но это на самом деле просто на мгновение откладывает запуск основного сервиса до тех пор, пока одна из зависимостей не пожалуется на это, так что это действительно не поможет.

Похоже, что простой перезапуск всех зависимостей - это самый простой способ, но он игнорирует (на данный момент) управление службами, которые уже остановлены и тому подобное ...

Ответы [ 3 ]

4 голосов
/ 12 октября 2011

Хорошо, наконец-то реализовал это.Я разместил его как отдельный ответ, так как уже пришел к такому выводу в оригинальном обновлении моего вопроса, которое было опубликовано до первого ответа.

Опять же, StartService(), StopService()и RestartService() методы следуют соглашениям, изложенным в примерах, и таким, которые уже упоминались в самом вопросе (т.е. они переносят поведение Start / Stop, чтобы избежать исключений типа «уже запущен / остановлен») с добавлением, что если Service являетсяпереданный (как в случае ниже), Refresh() вызывается для этой службы перед проверкой ее Status.

public static void RestartServiceWithDependents(ServiceController service, TimeSpan timeout)
{
    int tickCount1 = Environment.TickCount; // record when the task started

    // Get a list of all services that depend on this one (including nested
    //  dependencies)
    ServiceController[] dependentServices = service.DependentServices;

    // Restart the base service - will stop dependent services first
    RestartService(service, timeout);

    // Restore dependent services to their previous state - works because no
    //  Refresh() has taken place on this collection, so while the dependent
    //  services themselves may have been stopped in the meantime, their
    //  previous state is preserved in the collection.
    foreach (ServiceController dependentService in dependentServices)
    {
        // record when the previous task "ended"
        int tickCount2 = Environment.TickCount;
        // update remaining timeout
        timeout.Subtract(TimeSpan.FromMilliseconds(tickCount2 - tickCount1));
        // update task start time
        tickCount1 = tickCount2;
        switch (dependentService.Status)
        {
            case ServiceControllerStatus.Stopped:
            case ServiceControllerStatus.StopPending:
                // This Stop/StopPending section isn't really necessary in this
                //  case as it doesn't *do* anything, but it's included for
                //  completeness & to make the code easier to understand...
                break;
            case ServiceControllerStatus.Running:
            case ServiceControllerStatus.StartPending:
                StartService(dependentService, timeout);
                break;
            case ServiceControllerStatus.Paused:
            case ServiceControllerStatus.PausePending:
                StartService(dependentService, timeout);
                // I don't "wait" here for pause, but you can if you want to...
                dependentService.Pause();
                break;
        }
    }
}
1 голос
/ 05 декабря 2012

Обратите внимание, что ServiceController.Stop() останавливает «зависимые» службы, а ServiceController.Start() запускает «зависимые» службы - таким образом, после остановки службы вам нужно будет только запустить службы, которые являются листьями дерева зависимостей.

Предполагая, что циклические зависимости не разрешены, следующий код получает службы, которые необходимо запустить:

    private static void FillDependencyTreeLeaves(ServiceController controller, List<ServiceController> controllers)
    {
        bool dependencyAdded = false;
        foreach (ServiceController dependency in controller.DependentServices)
        {
            ServiceControllerStatus status = dependency.Status;
            // add only those that are actually running
            if (status != ServiceControllerStatus.Stopped && status != ServiceControllerStatus.StopPending)
            {
                dependencyAdded = true;
                FillDependencyTreeLeaves(dependency, controllers);
            }
        }
        // if no dependency has been added, the service is dependency tree's leaf
        if (!dependencyAdded && !controllers.Contains(controller))
        {
            controllers.Add(controller);
        }
    }

И с помощью простого метода (например, метода расширения):

    public static void Restart(this ServiceController controller)
    {
        List<ServiceController> dependencies = new List<ServiceController>();
        FillDependencyTreeLeaves(controller, dependencies);
        controller.Stop();
        controller.WaitForStatus(ServiceControllerStatus.Stopped);
        foreach (ServiceController dependency in dependencies)
        {
            dependency.Start();
            dependency.WaitForStatus(ServiceControllerStatus.Running);
        }
    }

Вы можете простоперезапустите службу:

    using (ServiceController controller = new ServiceController("winmgmt"))
    {
        controller.Restart();
    }

Достопримечательности:

Для ясности кода я не добавил:

  • таймауты
  • проверка ошибок

Обратите внимание, что приложение может оказаться в странном состоянии, когда некоторые службы перезапускаются, а некоторые нет ...

1 голос
/ 05 октября 2011

Звучит так, как будто вы хотите перезапустить «базовый» сервис и перезапустить все, что зависит от него.Если это так, вы не можете просто перезапустить все зависимые службы, потому что они, возможно, не работали заранее.У меня нет API для этого, о котором я знаю.

Я бы просто написал рекурсивную функцию для сканирования всех зависимых сервисов (и их зависимостей) и добавления всех сервисов которые запускали в список по порядку.

Когда вы перезапускаете базовую службу, вы можете просто запустить этот список и запустить все.При условии, что вы не пересортировали список, службы должны запускаться в правильном порядке, и все будет хорошо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...