2011-10-03 5 views
10

A partir d'une csharp-example et en prenant dûment note des questions liées SO (Restart a windows services from C# et Cannot restart a Service) et diverses autres questions relatives à redémarrant seul service, je me demande quelle est la meilleure méthode est pour redémarrer un service avec les services dépendants (par exemple Message Queuing, sur lequel dépend, ou IIS, sur lequel FTP Publishing et World Wide Web Publishing dépendent). Le composant logiciel enfichable mmc le fait automatiquement, mais le code ne semble pas offrir la même fonctionnalité (du moins pas aussi facilement). «Si des services dépendent de ce service pour leur fonctionnement, ils seront arrêtés avant que ce service ne soit arrêté La propriété DependentServices contient l'ensemble des services qui en dépendent,» et DependentServices renvoie un tableau de services . En supposant StartService() et StopService() suivre les conventions décrites dans les exemples et comme mentionné ci-dessus (à l'exception qu'ils acceptent ServiceControllers et TimeSpans directement), j'ai commencé avec:Redémarrer un service avec des services dépendants?

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); 
    } 
} 

Mais si les dépendances de service sont imbriquées (récursive) ou cyclique (si c'est même possible ...) - si Service A est dépendait par Service B1 et Service B2 et Service C1dépend deService B1, il semble « redémarrer » Service A par cette méthode arrêterait Service C1 mais ne le redémarrer ...

Pour rendre cet exemple image plus claire, je vais suivre le modèle dans les services composant logiciel enfichable MMC:

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

Y at-il une meilleure façon de s'y prendre ceci, ou devrait-il juste récursivement intervenir dans et arrêter chaque service dépendant et puis les relancer tous après qu'il redémarre le service principal?

De plus, sera dépendante mais actuellement arrêté services sont répertoriés sous DependentServices? Si oui, cela ne les relancerait-il pas? Si oui, devrions-nous contrôler cela aussi? Cela semble juste plus désordonné et plus désordonné ...

* Note: Je me rends compte que le timeout n'est pas complètement appliqué correctement ici (le délai d'attente global pourrait être plusieurs fois plus long que prévu), mais pour l'instant ce n'est pas le problème Je suis préoccupé par - si vous voulez le réparer, bien, mais ne dites pas simplement 'timeout est cassé ...'

Mise à jour: Après quelques tests préliminaires, j'ai découvert (/ confirmé) le comportements suivants:

  • arrêt d'un service (par exemple Service A) que d'autres services (e .g. Service B1) dépendent arrêtera les autres services (y compris les dépendances « imbriquées », comme Service C1)
  • DependentServicesne comprennent les services dépendants dans tous les états (marche, arrêt, etc.), et il comprend également des dépendances imbriquées, à savoirService_A.DependentServices contiendrait {Service B1, Service C1, Service B2} (dans cet ordre, C1 dépend de B1). Le démarrage d'un service dépendant d'autres services (par exemple, Service B1dépend deService A) lancera également les services requis.

Le code ci-dessus peut donc être simplifiée (au moins en partie) juste arrêter le service principal (qui arrêtera tous les services dépendants), puis en redémarrant les services les plus dépendants (par exemple Service C1 et Service B2) (ou tout simplement redémarrer "tous" les services dépendants - il ignorera ceux déjà commencés), mais cela ne fait que retarder le démarrage du service principal jusqu'à ce que l'une des dépendances s'en plaigne, cela n'aide donc pas vraiment.

Attend pour l'instant comme juste de redémarrer toutes les dépendances est la façon la plus simple, mais qui ne tient pas compte (pour l'instant) la gestion des services qui sont déjà arrêté et comme ...

Répondre

4

D'accord, enfin mis en œuvre cela. Je l'ai posté comme une réponse distincte comme je l'avais déjà venu à cette conclusion dans la mise à jour originale de ma question, qui a été posté avant la première réponse.

Encore une fois, les StartService(), StopService() et RestartService() méthodes suivent les conventions décrites dans les exemples et comme déjà mentionnés dans la question elle-même (ils enveloppez le comportement Start/Stop pour éviter « déjà commencé/arrêt » -type exceptions) avec le addendum que si un Service est transmis (comme c'est le cas ci-dessous), Refresh() est appelé sur ce service avant de vérifier son 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

Le commentaire "leur état précédent est pres erved dans la collection "n'est pas valide. Le statut semblait être mis à jour lorsque j'exécutais ce code. J'ai donc dû le modifier pour enregistrer l'état initial avant le redémarrage du service principal. Peut-être que la mise en œuvre interne de .NET a changé depuis sa publication? – Stif

1

On dirait que vous voulez redémarrer une « base "service et faire redémarrer tout ce qui en dépend. Si tel est le cas, vous ne pouvez pas simplement redémarrer tous les services dépendants, car ils ne fonctionnaient peut-être pas auparavant. Il n'y a pas d'API pour cela que je sache.

La façon dont je le ferais est juste écrire une fonction récursive pour analyser tous les services dépendants (et leurs dépendances), et ajouter tous les services qui étaient en cours d'exécution à une liste dans l'ordre. Lorsque vous redémarrez le service de base, vous pouvez simplement parcourir cette liste et tout démarrer. Si vous n'avez pas trié de nouveau la liste, les services devraient commencer dans le bon ordre, et tout ira bien.

+0

Ouais, c'est la conclusion que j'ai réglé sur ainsi ... Ce n'est pas terriblement difficile, c'est juste plus compliqué que le mmc automagique - oh, eh bien ... :) – johnny

+0

Et il s'avère qu'il n'a pas besoin d'être récursif - notez (à partir de ma mise à jour) qu'obtenir des dépendances sur une base liste les services dépendants de _all_ dans l'ordre de leur dépendance, donc vous auriez juste à filtrer cette liste pour ceux qui sont en cours d'exécution avant de redémarrer la base ... – johnny

1

S'il vous plaît Notez que les services ServiceController.Stop() arrêts de charge »et ServiceController.Start() commence « dépendant » services - donc après l'arrêt du service que vous aurez seulement besoin de démarrer les services qui sont les feuilles de l'arbre des dépendances.

En supposant aucune dépendance cycliques sont autorisés, le code suivant obtient les services qui doivent être commencé:

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); 
     } 
    } 

Et avec une méthode simple (par exempleméthode d'extension):

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); 
     } 
    } 

Vous pouvez simplement redémarrer un service:

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

Points d'intérêt:

Pour le code clearity Je n'ai pas ajouter:

  • dépassements de délai
  • erreur de vérification

S'il vous plaît notez que l'application peut se retrouver dans un état étrange, lorsque certains services sont redémarrés et certains ne sont pas ...

+0

Dans votre instruction if, vous devez également vérifier que le service est en cours d'exécution avant de l'ajouter. if (! dependencyAdded &&! controllers.Contains (contrôleur) && (controller.status! = ServiceControllerStatus.Stopped && controller.status! = ServiceControllerStatus.StopPending)) { contrôleurs.Add (contrôleur); } – user398039

Questions connexes