2011-10-03 58 views
10

csharp-example並正式開始注意到與SO有關問題(Restart a windows services from C#Cannot restart a Service)以及其他各種問題,重新啓動只是一個服務,我想知道,最好的方法是什麼重新啓動服務與相關服務 (例如Message Queuing,其上Message Queuing Triggers取決於,或者IIS,其中FTP PublishingWorld Wide Web Publishing取決於)。 mmc管理單元自動執行此操作,但代碼似乎沒有提供相同的功能(至少不那麼容易)。重新啓動具有相關服務的服務?

MSDN documentation for Stop說:「如果任何服務依賴於該服務爲他們的運營,他們將停止此服務被終止之前的DependentServices屬性包含一套依賴於這個服務的,」和DependentServices返回一系列的服務。假設StartService()和​​遵循的例子,並概述了公約,如上述(除非他們接受ServiceControllersTimeSpans直接),我開始引用:

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 B1Service B2Service C1取決於Service B1,似乎「重新啓動」通過這種方法Service A將停止Service C1,但不會重新啓動它...

爲了使這個例子畫面更清晰,我會按照在服務MMC管理模式管理單元:

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

是否有更好的方法去這個,還是隻是遞歸地進入並停止每個依賴服務,然後在重啓主服務後重新啓動它們?

此外,將從屬於目前停止服務將列在依賴服務?如果是這樣,反正他們不會重新啓動它們嗎?如果是這樣,我們是否應該控制它?這似乎變得更混亂和更混亂...

*注意:我意識到timeout沒有被正確應用在這裏(總體超時可能比預期長很多倍),但現在不是這個問題我很擔心 - 如果你想修復它,但沒關係,只是不要說'超時'壞了......'

更新:經過一些初步測試,我發現(/確認)以下行爲:

  • 停止服務(例如Service A)的其他服務(E 。G。 Service B1)取決於將停止其他服務(包括「嵌套」的依賴關係如Service C1
  • DependentServices確實包括在所有狀態相關的服務(運行,停止等),並且它也包括嵌套的依賴關係,即Service_A.DependentServices將包含{Service B1, Service C1, Service B2}(以該順序,因爲C1取決於B1)。
  • 啓動一項取決於他人的服務(例如Service B1取決於Service A)也將啓動必要的服務。

的代碼因此上述可以被簡化(至少部分地)到剛停止主服務(這將停止所有相關服務),然後重新啓動最依賴的服務(例如Service C1Service B2)(或只是重新啓動「所有」依賴服務 - 它會跳過已經啓動的服務),但這只是暫時延遲主服務的啓動,直到其中一個依賴項抱怨爲止,所以這並沒有什麼幫助。

看起來現在好像剛剛重新啓動所有的依賴是最簡單的方式,但忽略了(現在)管理那些已經停止服務,並且這樣的......

回答

4

好吧,終於實現了這個。我已經發布了它作爲一個單獨的答案,因爲我已經在我的問題的原始更新中得出了這個結論,該問題在第一個答案之前發佈。

再次,StartService(),​​和RestartService()方法遵循在實施例中列出,並已等在問題本身所引用的約定(即它們包裹啓動/停止行爲,避免「已啓動/停止」型例外)與補充說明如果傳入Service(如下所示),則在檢查其Status之前,該服務會調用Refresh()

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

評論「他們以前的狀態是壓力在集合中出現「是無效的。當我執行此代碼時,狀態似乎已更新。所以我不得不修改它來記錄主服務重啓之前的初始狀態。也許.NET內部實現自發布後發生了變化? – Stif

1

這聽起來像你想重新啓動「基地「服務,並讓所有依賴它的東西也重新啓動。如果是這樣,您不能只重新啓動所有依賴服務,因爲它們可能沒有預先運行。沒有我知道的API。

我這樣做的方式只是寫一個遞歸函數來掃描所有依賴服務(及其依賴關係),並將按順序運行的所有服務添加到列表中。

當您重新啓動基本服務時,您可以運行這個列表並開始一切。如果您沒有重新排列列表,服務應該以正確的順序開始,一切都會好的。

+0

是啊,這就是結論,我看中以及...這不是非常困難,它只是比automagic mmc更復雜 - 哦,以及... :) – johnny

+0

事實證明,它不需要遞歸 - 注意(從我的更新)獲取依賴關係在一個基地列出依賴的順序依賴的服務,所以你只需要過濾該列表的實際運行之前,你重新啓動基地... – johnny

1

請注意「對因」服務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(); 
    } 

問題的興趣:

對於代碼clearity我沒加:

  • 超時
  • 錯誤檢查

請注意,該應用程序可以在一個陌生的國家,結束時,一些服務將重新啓動,有些則不是...

+0

在您的if語句中,您還需要在添加之前檢查服務是否正在運行。如果 { controllers.Add(控制器)(dependencyAdded && controllers.Contains(控制器)&&(controller.status = ServiceControllerStatus.Stopped && controller.status = ServiceControllerStatus.StopPending)!!); } – user398039