【问题标题】:How to identify internal failures in a Windows service如何识别 Windows 服务中的内部故障
【发布时间】:2017-05-25 14:44:29
【问题描述】:

我们在应用程序中使用了大量自定义 Windows 服务。然而,我目前正在处理的问题有一个令人恼火的问题:当服务继续运行时,它只是停止运行。

服务的 Main 方法被包装在一个 try/catch 块中,如下所示:

static void Main()
{
    IRepository rep = new Repository();
    ILogger log = LogManager.GetLogger(GetType().Name);

    TimeSpan loadWindowStart = new TimeSpan(9, 0, 0);
    TimeSpan loadWindowEnd = new TimeSpan(18, 0, 0);

    foreach (SuppressionLoad sl in rep.GetSuppressionLoads().ToList())
    {
        try
        {
            // do stuff
        }
        catch(Exception ex)
        {
            // log error
        }
    }
}

该服务还会在它做的事情时记录下来,我们可以在它忙碌的时候看到日志填满。

但是,有时日志会停止。数据库中其他地方的活动表明整个服务已经停止工作。签入服务器上的服务,该服务仍显示“已启动”状态。在这种状态下,它占用的系统资源几乎为零,尽管它通常是处理器密集型的。如果您尝试阻止它,它只会尝试超时,而且据我们所知,它永远不会自行停止。该进程必须在任务管理器中终止。

在前往这些摊位的过程中,日志中没有任何不妥之处。我们在事件查看器中也找不到任何东西。

由于它没有记录错误,我不知道这里发生了什么,或者我们可以做些什么来尝试从这里诊断故障。它是高度间歇性的——它通常会在进入状态之前运行几天而没有问题。我们可以做些什么来调查发生了什么?

【问题讨论】:

  • 听起来您需要查看服务的代码,开始使用调试器并逐步执行代码..也许您可能需要考虑更改日志记录的方式..也许写直接写入日志文件或每次都创建一个新的日志文件。
  • @MethodMan 单步执行没问题,但是这种故障断断续续,并不是真正的实用选择。
  • 听起来问题可能在任何地方,不一定与提供的代码有太大关系。一个建议:当服务挂起时,附加一个调试器并查看线程以及每个线程的位置。要问的问题:是我所期待的所有线程还是有些已经消失或下落不明。线程是否陷入死锁(我怀疑这是正在发生的事情),如果是这样,在什么资源上。打开详细的日志记录并添加更多的调试日志语句,以隔离它上次在代码流中的位置和没有到达的位置,然后继续缩小位置。
  • 这听起来像死锁。您应该能够使用任务管理器进行小型转储,并在 WinDbg 等调试器中对其进行分析。 WinDbg 具有查找同步对象锁定的命令(我相信!locks 可以做到这一点)。
  • 会不会是日志系统出错了?那个可能不会在你的 try/catch 块中被捕获。假设有故障导致服务器中断并且无法记录。尝试每 x 秒记录一次滴答,当服务失败时,查看最后一次滴答时的 Windows 事件日志,看看系统可能注意到了其他事情。

标签: c# windows windows-services


【解决方案1】:

马特;在最好的条件下很难找到诸如此类的模糊问题 - 如果您的服务碰巧使用线程(我假设它确实如此),那么它变得非常困难,并且您不能依赖全局 try/catch。

一个简单的尝试是 NBug(无关联)。它将捕获未处理的异常并为您提供一些有关它们的信息。我不认为它会让你足够。

查找这类事物的一般方法是记录、记录、记录。您必须能够尽可能接近重现问题 - 您需要日志来告诉您进入每个方法的入口点、变量值、异常堆栈跟踪(如果命中)、您在每个方法中花费了多长时间等。有那里有一些非常好的工具来记录some logging tools,所以我不会费心推荐任何东西。您可以将日志记录在条件编译开关中,这样一旦发现问题,关闭它就不会影响性能。

可能不是您想要的答案,但多年来唯一真正对我有用的东西。

史蒂夫

【讨论】:

  • 不是我想听的,而是其他人认为有帮助的。 +1
【解决方案2】:

听起来问题可能存在于任何地方,不一定与提供的代码有太大关系。

关于如何去做的建议

  1. 当服务挂起时,附加一个调试器并查看线程并查看每个线程的位置。您可能需要重新构建并运行解决方案的调试版本,以便调试器拥有必要的上下文符号数据。要问的问题:

    1. 我期望的所有线程都在那里,还是有些已经消失或下落不明?
    2. 线程是否陷入死锁(我怀疑这是发生了什么),如果是,在什么资源上。
  2. 打开详细的日志记录并添加更多的调试日志语句以隔离它最后在代码流中的位置和没有到达的位置,然后继续缩小位置。考虑记录上下文数据,这样当您隔离有问题的行或代码块时,您就有上下文来尝试理解为什么会发生奇怪的行为。请注意记录敏感信息(即密码、PII 等)
  3. 完全相信 IInspectable 的评论,您可以尝试完全转储该过程(SysInternal's Process ExplorerProcDump 让你这样做,或 Task Manager)。使用该工具往往是一种相当复杂的体验,但正确使用可以提供很多洞察力,并可能在第一次出现时发现问题。

考虑到这种情况很少发生,而且内容和地点的范围很广,因此可能需要多次迭代才能触发问题以缩小范围。

【讨论】:

  • 如果您缩小范围但仍不明白原因,请更新您的问题并联系我 - 我很乐意进一步提供帮助。
猜你喜欢
  • 1970-01-01
  • 2021-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-08
  • 1970-01-01
相关资源
最近更新 更多