重新启动具有依赖服务的服务
本文关键字:服务 依赖 重新启动 | 更新日期: 2023-09-27 18:10:16
从csharp-example开始并适当注意相关SO问题(从c#重新启动windows服务并且不能重新启动服务)以及与重新启动一个服务有关的各种其他问题,我想知道重新启动服务的最佳方法是依赖服务(例如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
,但不会重新启动它…
为了使这个示例更清晰,我将遵循services mmc管理单元中的模型:
The following system components depend on [Service A]:
- Service B1
- Service C1
- Service B2
是否有更好的方法来解决这个问题,或者它只需要递归地进入并停止每个依赖的服务,然后在重新启动主服务后重新启动它们?
另外,依赖的但当前已停止的服务会列在DependentServices下吗?如果是这样,这难道不会重启他们吗?如果是这样,我们也应该控制吗?这似乎变得越来越乱…
*注意:我意识到timeout
没有被完全正确地应用在这里(整体超时可能比预期的长很多很多倍),但现在这不是我关心的问题-如果你想修复它,很好,但不要只是说'超时坏了…'
更新:经过一些初步测试,我发现(/确认)以下行为:
- 停止其他服务(如
Service B1
)依赖的服务(如Service A
)将停止其他服务(包括"嵌套"依赖项,如Service C1
) -
DependentServices
确实包含所有状态下的依赖服务(Running, Stopped等),并且它还包括嵌套依赖项,即Service_A.DependentServices
将包含{Service B1, Service C1, Service B2}
(按此顺序,C1
依赖于B1
)。 - 启动依赖于其他服务的服务(例如
Service B1
依赖于Service A
)也会启动必需的服务。
上面的代码因此可以简化(至少部分简化),只停止主服务(这将停止所有依赖服务),然后重新启动最依赖的服务(例如Service C1
和Service B2
)(或者只是重新启动"所有"依赖服务-它将跳过已经启动的服务),但这实际上只是暂时推迟主服务的启动,直到其中一个依赖抱怨,所以这并没有真正帮助。
现在看起来只是重新启动所有的依赖是最简单的方法,但这忽略了(目前)管理已经停止的服务等…
好了,终于实现了。我已经把它作为一个单独的答案发布了,因为我已经在我的问题的原始更新中得出了这个结论,这是在第一个答案之前发布的。
再次,StartService()
, StopService()
和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;
}
}
}
请注意,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();
}
兴趣点:
为了代码清晰,我没有添加:
- 超时
- 错误检查
请注意,应用程序可能会在一个奇怪的状态结束,当一些服务重新启动,而另一些没有…
听起来你想要重新启动一个"基础"服务,并让所有依赖于它的东西也重新启动。如果是这样,就不能重新启动所有依赖服务,因为它们可能事先没有运行。据我所知,没有这样的API。
我的方法是写一个递归函数来扫描所有的依赖服务(和它们的依赖关系),并将所有运行的服务按顺序添加到一个列表中。当您重新启动基本服务时,您可以运行此列表并启动所有内容。如果您没有重新排序列表,那么服务应该以正确的顺序启动,并且一切都会很好。