为什么在Topshelf中多次调用Stop方法

时间:2018-12-17 03:26:20

标签: windows-services topshelf

Topshelf在我们的应用程序中充当Windows Service Broker。今天早上,我们发现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;
    }
}

今天早上,我们发现服务因不清楚的原因而停止。而且有许多行记录了此停止操作。

enter image description here

我猜想Topshelf会多次调用ServiceManager.Stop方法。有人遇到过这个问题吗?我想知道我可以追踪为什么发生这种情况。

任何人都可以帮忙吗?非常感谢。

1 个答案:

答案 0 :(得分:1)

您遇到此现象的原因是您的Stop()方法需要一段时间,但没有响应停止请求。

您的方法本质上是这样的:

Stop() {
   log-stopping;
   wait-a-while;
   log-stopped;
}

在等待时,服务的状态仍为“正在运行”。这导致请求者(可能是Windows本身或其他程序)继续重新发送停止请求,从而导致对Stop()的多个并行/重叠调用。这占了您包含的日志的前10行。

您可以看到“等待”完成大约需要20秒(从05:39:45到05:40:04)。

在那之后,看来Topshelf可能会卡住。这导致发送更多消息。 (请注意,在日志的下一行中,停止对和开始对同时记录,因为您的任务已停止并且没有等待时间。)

要解决此问题,您应该:

  1. 修改您的WhenStopped()调用,以将HostControl参数传递给Stop():

    serviceInstance.WhenStopped((execute,hostControl)=> execute.Stop(hostControl));

  2. 更新Stop()方法以使用HostControl参数,并在调用Task.WaitAll()之前进行此调用:

    hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30));

这将通知Windows您的服务已收到该请求,并且可能正在处理该请求长达30秒钟。那应该避免重复通话。

参考:Topshelf documentation