【问题标题】:How to catch exception and stop Topshelf service?如何捕获异常并停止 Topshelf 服务?
【发布时间】:2017-01-30 22:41:43
【问题描述】:

我有一个 topshelf windows 服务,我想在其中进行一些检查(即是否存在 xml 文件),如果检查失败,我需要停止 windows 服务。

所以我尝试在 Start() 方法中进行检查,然后引发异常:

public void Start()
{
    if (!File.Exists(_xmlFile) throw new FileNotFoundException();
    // Do some work here if xml file exists.
}

但是,Windows 服务在异常之后作为一个进程保留,然后我必须在任务管理器中手动终止。

如果某些条件(即未找到文件)成立,有没有办法不运行服务?

【问题讨论】:

  • 你为什么抛出一个异常而没有任何东西可以捕获它?
  • 另外你不需要抛出FileNotFoundException clr 会为你做的。
  • 我正在尝试让 Windows 服务在出现异常时停止运行,并希望 topshelf 能够停止它。
  • 你是如何启动服务的?我从一个旧项目中找到了一个 topshelf 服务示例,它使用 HostFactory 来启动服务
  • @JD 重点是通过显式抛出异常你什么也得不到。如果文件不存在,CLR 将为您抛出 FileNotFound 异常。

标签: c# topshelf


【解决方案1】:

您可以使用 HostControl 对象并像这样修改您的方法:

public bool Start(HostControl hostControl)
{
    if (!File.Exists(_xmlFile) 
    {
        hostControl.Stop();
        return true;
    }

    // Do some work here if xml file exists.
    ...
}

您需要将 HostControl 传递给 Start 方法,如下所示:

HostFactory.Run(conf =>
{
    conf.Service<YourService>(svcConf =>
    {
        svcConf.WhenStarted((service, hostControl) =>
        {
            return service.Start(hostControl);
        }
    }
}

【讨论】:

  • 我建议在失败的情况下返回false - 这似乎使 TopShelf 向服务控制管理器报告服务无法启动。
  • 不需要调用“hostControl.Stop()”。只需在 Start 方法中返回 false 就足够了。
  • 这很有帮助。我的应用程序的启动过程比我认为可以接受的等待服务本身启动的过程要长。为此,我从 Start 方法返回 true,但如果我的应用程序无法完成启动过程,请按住 hostcontrol 并调用 hostcontrol.Stop。
  • 这非常有效。不过,TopShelf 文档中的记录很差。
【解决方案2】:

每个WhenXxx方法还可以带一个HostControl接口的参数,可以用来请求停止服务、请求额外的启动/停止时间等。

在这种情况下,将 start() 的签名更改为 bool start(HostControl hc)。在服务中保留对该 HostControl 的引用,如下所示:

public bool Start(HostControl hc)
{
    hostControl = hc;
    Restart();
    return true;
}

现在,当您想停止服务时,请使用以下调用:

hostControl.Stop();

【讨论】:

    【解决方案3】:

    从 Topshelf 文档中的最佳实践或建议的角度来看,我对此感到好奇,但找不到任何东西。但是,我确实从 phatboyg 中找到了两个独立的 cmets...

    最佳评论...如何通过this issue 停止服务异常(我删掉了一些细节):

    如果你的服务的 Start 方法抛出异常,服务将无法启动。

    服务运行后,如果抛出未处理的异常,服务将停止,并将其作为崩溃报告给服务控制管理器。

    如果您需要以编程方式停止服务,请使用 HostControl 方法停止。

    所以我认为最简单的答案是抛出异常。

    您正在这样做,并且您提到“Windows 服务在异常之后作为一个进程保留”。这似乎是您代码中某个不相关的错误,或者您可能以某种方式运行了多个实例?我今天早上一直在测试这些场景,但在 start 方法中抛出异常后,我的服务没有运行。

    此外,与在接受的答案中提到的 HostFactory.Run 之前的检查有关,通过https://groups.google.com/forum/embed/#!topic/topshelf-discuss/nX97k3yOhJU

    “在调用 HostFactory.Run() 方法之前,您的应用程序应该只配置 NLog/Log4Net。”

    【讨论】:

    • 我试过这个...在启动方法中抛出异常并停止服务...但它不会在执行期间停止服务。正如@julio 所解释的,我不得不使用 hostControl.Stop() 来停止服务
    【解决方案4】:

    我刚刚遇到了这个问题,上面所有的答案似乎都过于复杂了。您需要做的就是使用接受Func&lt;T,HostControl,bool&gt;WhenStarted 重载并在您的内部服务引导失败时返回false。我认为不需要显式调用hostControl.Stop()

    //Here is bit from inside the .Service<T>() call
    s.WhenStarted((YourService svc, HostControl hc) => svc.Start());
    
    //And the svc.Start method would look something like this:
    class YourService
    {
       public bool Start() {
         //return true if all is well
         //or false if you want service startup to be halted
       }
    }
    

    【讨论】:

    • 我想指出 Topshelf 忽略了从 Start 方法返回的 false 值。服务在服务 mmc 中显示为已启动。但是从 Start 抛出的异常停止服务
    【解决方案5】:

    我已经“借用”了topshelf功能设置的示例代码来说明一个观点:

    HostFactory.Run(x =>                                 //1
        {
            x.Service<TownCrier>(s =>                        //2
            {
               s.ConstructUsing(name=> new TownCrier());     //3
               s.WhenStarted(tc => tc.Start());              //4
               s.WhenStopped(tc => tc.Stop());               //5
            });
            x.RunAsLocalSystem();                            //6
    
            x.SetDescription("Sample Topshelf Host");        //7
            x.SetDisplayName("Stuff");                       //8
            x.SetServiceName("stuff");                       //9
        });    
    

    在上述代码运行之前,您必须对文件系统进行检查。让我们考虑一下。拥有服务的目的是确保它运行并保持运行。您首先试图颠覆拥有服务应用程序的基本原则。不要因为丢失文件而试图停止服务,而是想办法提醒您的支持人员,而不是根据丢失的文件做任何事情。

    【讨论】:

    • 感谢您的代码。我早些时候尝试过,但无法停止服务。我目前正在登录,因此支持人员有办法知道它失败了。我只是不想让他们先去杀死服务。
    • 我不同意这一点。您可能有一些先决条件来检查,如果不满足,运行服务是没有意义的。
    • 我有点好奇 Topshelf 的作者对这种情况的看法。我还没有找到任何可以回答这个问题的东西,但确实看到了这条评论 here:“您的应用程序应该在调用 HostFactory.Run() 方法之前配置 NLog/Log4Net。”
    • Topshelf 确实提供了一种以这种方式关闭的方法。请参阅下面@julio.g 的答案
    • 经典堆栈溢出的东西。 “我希望我的服务自行停止”“你不应该这样做,因为你正在颠覆一个基本原则”。看在上帝的份上,我是程序员,让我选择是否停止它。感谢 julio.g 下面的一些理智。
    【解决方案6】:

    当你捕捉到异常时,你可以使用ServiceBase.Stop() 方法自行停止服务。

    try
    {
        // Your Code
    }
    catch (Exception ex)
    {
        // The code for stopping service
    }
    

    在某些情况下你也可以有多个 catch 块:

    try
    {
        // Your Code
    }
    catch (IndexOutOfRengeException ex)
    {
        // The code for stopping service
    }
    catch (FileNotFoundException exc)
    {
        // The code for stopping service
    }
    

    阅读更多关于ServiceBase.Stop()

    【讨论】:

    • 捕捉Exception 然后FileNotFoundException?
    • 谢谢你的说。 @SriramSakthivel
    • @ManyRootsofAllEvil 你为什么这么说?没有理由不在 Windows 服务中工作。
    • 问题不是捕获异常,而是停止服务。
    • 这是一项 TopShelf 服务。没有易于访问的 ServiceBase 对象。他应该打电话给HostControl.Stop()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多