使用从属服务重新启动服务?
从csharp示例开始并正式注意相关的SO问题( 从C#重新启动Windows服务并且无法重新启动服务 )以及与重新启动一项服务相关的各种其他问题,我想知道重启服务的最佳方法是什么依赖服务 (例如, Message Queuing Triggers
依赖的Message Queuing Triggers
,或者是FTP Publishing
和World Wide Web Publishing
依赖的IIS
)。 mmc管理单元自动执行此操作,但代码似乎不提供相同的function(至少不那么容易)。
Stop的MSDN文档说“如果任何服务依赖于此服务进行操作,它们将在此服务停止之前停止.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 B1
依赖于 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将列出依赖但当前已停止的服务? 如果是这样,这不会重启它们吗? 如果是这样,我们也应该控制它吗? 这似乎变得更加混乱和混乱……
*注意:我意识到这里没有完全正确地应用超时(整体超时可能比预期长很多倍),但是现在这不是我关心的问题 – 如果你想修复它,很好,但不要只是说’超时被打破……’
更新:经过一些初步测试后,我发现(/确认)了以下行为:
- 停止其他服务(例如
Service B1
)所依赖的服务(例如Service A
)将停止其他服务(包括“嵌套”依赖性,例如Service C1
) -
DependentServices
确实包括所有状态(运行,停止等)的依赖服务,它还包括嵌套依赖项,即Service_A.DependentServices
将包含{Service B1, Service C1, Service B2}
(按顺序,因为C1
依赖于B1
)。 - 启动依赖于其他
Service B1
(例如,Service B1
依赖于Service A
)也将启动必需的服务。
因此,上面的代码可以简化(至少部分)只是停止主服务(将停止所有相关服务),然后重新启动最依赖的服务(例如Service C1
和Service B2
)(或者只是重新启动“全部”)依赖服务 – 它将跳过已经开始的服务),但这只是暂时推迟主服务的启动,直到其中一个依赖关系抱怨它,所以这并没有真正帮助。
现在寻找就像重新启动所有依赖项是最简单的方法,但是忽略(现在)管理已经停止的服务等等……
好吧,终于实现了这一点。 我已经将它作为单独的答案发布,因为我已经在我的问题的原始更新中得出了这个结论,该更新是在第一个答案之前发布的。
同样, StartService()
, StopService()
和RestartService()
方法遵循示例中概述的约定,并且已在问题本身中引用(即它们包装Start / Stop行为以避免“已启动/已停止”类型的exception)如果传入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; } } }
听起来你想要重新启动一个“基础”服务并让所有依赖它的东西也重新启动。 如果是这样,您不能只重新启动所有相关服务,因为它们可能没有预先运行。 我知道没有这方面的API。
我这样做的方法就是编写一个递归函数来扫描所有依赖服务(及其依赖项),并按顺序将所有正在运行的服务添加到列表中。
当您重新启动基本服务时,您可以只运行此列表并启动所有内容。 如果您没有对列表进行重新排序,那么服务应该以正确的顺序开始,一切都会顺利进行。
请注意ServiceController.Stop()
停止’依赖’服务, ServiceController.Start()
启动’依赖’服务 – 因此在停止服务后,您只需要启动依赖树叶的服务。
假设不允许循环依赖,以下代码获取需要启动的服务:
private static void FillDependencyTreeLeaves(ServiceController controller, List 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 dependencies = new List (); 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(); }
兴趣点:
为了代码清晰,我没有添加:
- 超时
- 错误检查
请注意,当某些服务重新启动而某些服务不是……时,应用程序可能会处于一种奇怪的状态。