为什么在 Topshelf 中多次调用 Stop 方法
Why Stop method is invoked many times in Topshelf
Topshelf 在我们的应用程序中充当 windows 服务代理。今天早上,我们发现 Stop 方法被调用了很多次。这是相关的代码。
class Program
{
static void Main(string[] args)
{
ILog Log = new FileLog();
try
{
HostFactory.Run(serviceConfig =>
{
serviceConfig.Service<ServiceManager>(serviceInstance =>
{
serviceInstance.ConstructUsing(() => new ServiceManager());
serviceInstance.WhenStarted(execute => execute.Start());
serviceInstance.WhenStopped(execute => execute.Stop());
});
});
}
catch (Exception ex)
{
Console.WriteLine(ex);
Log.Error("Program.Main", ex, LogType.Error);
Console.ReadLine();
};
}
}
在 ServiceManager 中,我们有 Stop 方法,当 TopShelf 收到来自操作系统的停止信号时将调用该方法。
class ServiceManager
{
xxx.....
public bool Stop()
{
try
{
_log.Info("The service is stopping", LogType.Info);
_service.StopExecuteProduceMessage();
Task.WaitAll(_tasks.ToArray());
_log.Info("The service is stopped", LogType.Info);
}
catch (Exception ex)
{
_log.Error("Stop", ex, LogType.Error);
}
return true;
}
}
今天早上,我们发现服务已停止,原因不明。并且有很多行记录了这个停止操作。
我猜 Topshelf 多次调用 ServiceManager.Stop 方法。以前有人遇到过这个问题吗?我想知道我可以追踪为什么会发生这种情况。
有人可以帮忙吗?非常感谢。
您遇到此行为是因为您的 Stop() 方法需要一段时间但没有响应停止请求。
您的方法基本上如下所示:
Stop() {
log-stopping;
wait-a-while;
log-stopped;
}
在您等待期间,服务状态保持 "Running"。这会导致请求者(可能是 Windows 本身或另一个程序)不断重新发送停止请求,从而导致多次 parallel/overlapping 调用 Stop()。这说明了您包含的日志中的前 10 行。
您可以看到 "wait" 需要将近 20 秒才能完成(从 05:39:45 到 05:40:04)。
在那之后,看起来 Topshelf 可能会卡住。这会导致发送更多消息。 (请注意,在日志的下一行中,同时记录了停止和开始对,因为您的任务已停止并且没有等待)。
要解决此问题,您应该:
修改 WhenStopped() 调用以将 HostControl 参数传递给 Stop():
serviceInstance.WhenStopped((执行, hostControl) => execute.Stop(hostControl));
更新 Stop() 方法以获取 HostControl 参数并在调用 Task.WaitAll() 之前进行此调用:
hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30));
这将通知 Windows 您的服务已收到请求并且可能正在处理它长达 30 秒。那应该避免重复调用。
Topshelf 在我们的应用程序中充当 windows 服务代理。今天早上,我们发现 Stop 方法被调用了很多次。这是相关的代码。
class Program
{
static void Main(string[] args)
{
ILog Log = new FileLog();
try
{
HostFactory.Run(serviceConfig =>
{
serviceConfig.Service<ServiceManager>(serviceInstance =>
{
serviceInstance.ConstructUsing(() => new ServiceManager());
serviceInstance.WhenStarted(execute => execute.Start());
serviceInstance.WhenStopped(execute => execute.Stop());
});
});
}
catch (Exception ex)
{
Console.WriteLine(ex);
Log.Error("Program.Main", ex, LogType.Error);
Console.ReadLine();
};
}
}
在 ServiceManager 中,我们有 Stop 方法,当 TopShelf 收到来自操作系统的停止信号时将调用该方法。
class ServiceManager
{
xxx.....
public bool Stop()
{
try
{
_log.Info("The service is stopping", LogType.Info);
_service.StopExecuteProduceMessage();
Task.WaitAll(_tasks.ToArray());
_log.Info("The service is stopped", LogType.Info);
}
catch (Exception ex)
{
_log.Error("Stop", ex, LogType.Error);
}
return true;
}
}
今天早上,我们发现服务已停止,原因不明。并且有很多行记录了这个停止操作。
我猜 Topshelf 多次调用 ServiceManager.Stop 方法。以前有人遇到过这个问题吗?我想知道我可以追踪为什么会发生这种情况。
有人可以帮忙吗?非常感谢。
您遇到此行为是因为您的 Stop() 方法需要一段时间但没有响应停止请求。
您的方法基本上如下所示:
Stop() {
log-stopping;
wait-a-while;
log-stopped;
}
在您等待期间,服务状态保持 "Running"。这会导致请求者(可能是 Windows 本身或另一个程序)不断重新发送停止请求,从而导致多次 parallel/overlapping 调用 Stop()。这说明了您包含的日志中的前 10 行。
您可以看到 "wait" 需要将近 20 秒才能完成(从 05:39:45 到 05:40:04)。
在那之后,看起来 Topshelf 可能会卡住。这会导致发送更多消息。 (请注意,在日志的下一行中,同时记录了停止和开始对,因为您的任务已停止并且没有等待)。
要解决此问题,您应该:
修改 WhenStopped() 调用以将 HostControl 参数传递给 Stop():
serviceInstance.WhenStopped((执行, hostControl) => execute.Stop(hostControl));
更新 Stop() 方法以获取 HostControl 参数并在调用 Task.WaitAll() 之前进行此调用:
hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30));
这将通知 Windows 您的服务已收到请求并且可能正在处理它长达 30 秒。那应该避免重复调用。