Windows Service增加CPU消耗
在我的工作中,我负责使用C2003编写的六个Windows服务。这些服务中的每一个都包含一个计时器,该计时器每分钟左右触发一次,其中大部分工作都在该计时器上进行。
我的问题是,随着这些服务的运行,即使没有任何有意义的工作要做,它们也会在循环的每次迭代中消耗越来越多的CPU时间(即,它们只是闲着,浏览数据库)做某事)。当它们启动时,每个服务平均使用(大约)4个CPU的2-3%,这很好。 24小时后,每个服务将在其循环运行期间消耗整个处理器。
有人可以帮忙吗?我不知道是什么原因造成的。我们当前的解决方案是每天重启一次服务(它们会自行关闭,然后脚本会发现它们处于脱机状态,并在凌晨3点左右重启它们)。但这不是一个长期的解决方案。我担心的是,随着服务变得越来越繁忙,每天重启一次可能还不够...但是由于存在一个巨大的启动惩罚(它们都使用NHibernate进行数据访问),因此它们变得越来越繁忙,这正是我们所不希望的。想要做的是更频繁地重新启动它们。
@akmad:是的,这很困难。
- 是的,随着时间的推移,单独运行的服务将显示相同的症状。
- 不,不是。我们已经看过了。这可能发生在上午10点或者下午6点或者深夜。没有一致性。
- 我们的确是;他们是。这些服务完全按照应有的方式工作,而没有其他事情。
- 不幸的是,这需要准确地预知服务将在何时达到CPU的最大化,这发生在无法预测的时间表上,而且永远不会很快...这使事情变得非常困难,因为我的老板将在运行时重新启动并运行它们。问题,无需考虑调试问题。
- 不,他们使用的内存数量相当稳定(每个内存约60-80MB,而计算机上的4GB内存除外)。
好的建议,但请放心,我们已经尝试了所有常见的故障排除方法。我希望这是一个人们可能知道的.NET问题,我们可以进行解决。我老板的解决方案(我不希望实现)是在数据库中放置一个字段,该字段保存多次以使服务在一天中重新启动,这样他就可以解决问题,而不必考虑它了。我正拼命地寻找真正问题的原因,以便我能够解决它,因为该解决方案将在大约六个月内成为灾难。
@Yaakov Ellis:它们各自具有不同的功能。一个人从异地的Oracle数据库中读取记录;另一个人从Oracle数据库中读取记录。另一个处理这些记录,并将属于这些记录的文件传输到我们的系统中;第三位检查这些文件,以确保它们与我们期望的一样;另一个是维护服务,它会不断检查磁盘空间(我们有足够的空间)之类的东西,并轮询其他服务器以确保它们还活着。一个正在运行只是为了确保所有其他所有这些都在运行并且正在执行它们的工作,监视和报告错误,并重新启动所有无法使整个系统每天24小时正常运行的内容。
因此,如果我们要问的是我想问的问题,那么不行,所有这些服务(通过NHibernate进行数据库访问除外)都没有一个我可以指出的潜在问题。不幸的是,如果事实证明这是实际的问题(这不会令我感到惊讶),那么整个过程可能会搞砸了-我最终将用简单的SQL重写所有问题。我希望这是一个垃圾收集器问题,或者比NHibernate更容易处理的问题。
@Joshdan:没什么秘密。正如我所说,我们已经尝试了所有常见的故障排除方法。分析没有帮助:我们使用的探查器无法指向CPU使用率高时实际执行的任何代码。这些服务大约一个月前被撕裂,以寻找此问题。对代码的每个部分都进行了分析,以试图确定我们的代码是否是问题所在;我不是在这里问,因为我还没有做完作业。如果这是一个简单的案例,说明服务所做的工作比预期的多,那将是我们早已意识到的事情。
这里的问题是,在大多数情况下,服务根本不做任何事情,但仍然设法消耗了四个CPU内核的25%或者更多:它们找不到任何工作要做,退出循环并等待下一次迭代。从字面上看,这几乎不需要占用任何CPU时间。
这是我们看到的一个行为示例,该服务在两天都没有工作要做的服务上(在不变的环境中)。这是上周捕获的:
第1天,上午8点:平均CPU使用率约3%
第1天,下午6点:平均CPU使用率约8%
第2天,上午7点:平均CPU使用率约20%
第2天,上午11点:平均CPU使用率约30%
So does the CPU spike happen immediately preceding the timer callback, within the timer callback, or immediately following the timer callback?
在考虑了所有可能的普遍原因之后,我在这里提出了这个问题,因为我认为(正确的是,事实证明)我会得到更具创新性的答案(例如Ubiguchi的答案),或者指向我没有的东西的指针没想到(就像伊恩的建议一样)。
你误会了。这不是一个峰值。如果是这样,那将没有问题。我可以应付尖峰。但这不是... CPU使用率总体呈上升趋势。即使服务什么也不做,也要等待下一个计时器命中。当服务启动时,一切都会变得平静起来,并且该图看起来就像我们所期望的...通常,使用率为0%,随着NHibernate命中数据库或者该服务完成了一些琐碎的工作,峰值会上升到10% 。但是,在过程运行期间,这始终会增加25%的使用率(如果我让它走得太远的话会更多)。
I just noted a sense of desperation in the question -- that your problems would continue barring a small miracle
这使伊恩的建议成为合乎逻辑的灵丹妙药(NHibernate在我们不看时会做很多事情)。 las,我已经实现了他的解决方案,但是没有起到任何作用(我没有证明这一点,但是我实际上认为这使情况变得更糟了……现在平均使用率似乎增长得更快)。请注意,剥离NHibernate"节"(如我们所建议的)是不可行的,因为那样会剥离服务中约90%的代码,这将使我排除计时器是一个问题(我绝对打算这样做)尝试),但不能帮助我排除NHibernate是问题所在,因为如果NHibernate导致了此问题,那么已实施的躲避错误的解决方案(请参见下文)就只能成为The System The Works Works;我们非常依赖NHibernate进行此项目,以至于PM根本不接受这会导致无法解决的结构问题。
That is extremely intriguing (insofar as you trust your profiler).
并不意味着它会以这种方式消失。目前,这些服务每天都在重新启动(可以选择输入一天中的任何时间以关闭和重新启动它们),这修补了该问题,但是一旦进入生产机器,就不能成为长期解决方案开始变得忙碌无论是解决问题还是由PM维护,这些问题都不会继续存在。显然,我希望实施一个真正的修复程序,但是由于最初的测试没有显示出这样做的原因,并且已经对服务进行了广泛的审查,因此PM宁愿让它们重新启动多次,也不想花费更多时间尝试修复它们。 。那完全是我无法控制的,这使我们正在谈论的奇迹比原本更重要。
解决方案
回答
我不。但是,这些是在Windows 2000计算机上运行的.NET 1.1编写的Windows服务,由狡猾的Nant脚本部署,使用旧版本的NHibernate进行数据库访问。我实际上会说我信任的那台机器上几乎没有。
- 当我们一次只运行其中一项服务时,会发生什么情况?我们是否仍然看到速度变慢?这可能表明服务之间存在某些争用。
- 无论服务运行了多长时间,问题是否总是在大约同一时间发生?这可能表明其他原因(备份,病毒扫描等)导致整个计算机(或者数据库)变慢。
- 我们是否具有日志记录或者其他某种机制来确保该服务仅按我们认为应该的频率工作?
- 如果我们在短时间内看到性能下降,请尝试运行该服务一段时间,然后连接一个探查器,以准确查看与CPU挂钩的原因。
- 我们没有提及任何有关内存使用的信息。我们是否有任何有关服务的信息?可能是因为我们用完了大部分的RAM,并导致磁盘损坏,或者出现了类似的问题。
远程调试我们未知的应用程序显然很困难……但是,我要看一些事情:
回答
祝你好运!
弗雷德这个答案只会为我们提供一些指导,但在.NET Windows Services中遇到过类似的问题后,我有两种想法可能会有所帮助。
我的第一个建议是服务在处理内存的方式或者处理非托管内存的方式上可能存在一些错误。上次我找到类似问题的原因是,我们发现了第三方OSS libray,我们正在使用存储的句柄对静态内存中的非托管对象进行处理。服务运行的时间越长,处理的服务越多,这导致进程的CPU性能迅速下降。尝试解决此类问题以确保服务在计时器调用之间不将任何内容存储在内存中的方法,尽管如果第三方库使用静态内存,则可能必须做一些聪明的事情,例如为计时器调用和沟渠创建应用程序域处理完成后,应用程序doamin(及其静态内存)就会消失。
我在类似情况下看到的另一个问题是怀疑计时器同步代码,实际上这使多个线程可以同时运行处理代码。当我们调试代码时,我们发现第一个线程阻塞了第二个线程,而当第二个线程启动时,第三个线程被阻塞了。随着时间的流逝,阻塞持续的时间越来越长,因此CPU使用率越来越高。我们用于解决此问题的解决方案是实施适当的同步代码,因此,计时器只会在不会阻塞的情况下才启动另一个线程。
回答
希望这会有所帮助,但是如果我的想法都是红色鲱鱼,请提前道歉。
听起来像计时器的线程问题。我们可能有一个工作单元阻止另一个工作线程在不同的工作线程上运行,从而导致它们在每次计时器触发时堆积起来。否则,实例可能会比预期的寿命更长。
我建议重构计时器。将其替换为使ThreadPool上的工作排队的单个线程。我们可以使用Sleep()线程来控制寻找新工作的频率。确保这是代码是多线程的唯一位置。在工作准备就绪时应实例化所有其他对象,并在完成工作后销毁所有其他对象。状态是多线程代码中的敌人。
回答
缺乏设计的另一个方面似乎是我们有多个服务正在轮询资源以执行某项操作。我建议将它们统一在一项服务下。他们可能会做一些单独的事情,但是他们正在一致地工作。我们只是使用文件系统,数据库等代替方法调用。还有,2003年?我为你感到伤心。
回答
我建议将问题分解成几部分。
首先,找到一种方法可以100%地,快速地重现问题。降低计时器,以使服务更频繁地启动(例如,比正常速度快10倍)。如果问题出现的速度快了10倍,则问题与迭代次数有关,与服务的实时性或者实际工作无关。我们将能够比每天更快地执行下一步。
其次,注释掉所有实际的工作代码,只允许服务,计时器和同步机制。如果问题仍然存在,那么它将出现在代码的该部分中。
如果不是,则开始一次添加回注释掉的代码。最终,我们应该找出导致问题的代码的哪一部分。
回答
Good suggestions, but rest assured, we have tried all of the usual troubleshooting. What I'm hoping is that this is a .NET issue that someone might know about, that we can work on solving.
我们提到我们正在使用NHibernate,是在适当的时间关闭NHibernate会话(例如每次迭代的结束吗?)。
如果不是,则加载到内存中的对象映射的大小将随着时间的推移逐渐增加,并且每次会话刷新将占用越来越多的CPU时间。
我的感觉是,不管根本原因多么奇怪,通常的故障排除步骤都是找到问题的最佳选择。
由于这是一个性能问题,因此进行良好的测量非常重要。整个进程的CPU使用率范围太广。服务花在哪里?我们可以使用探查器进行测量,或者仅记录各个节的开始和停止。如果我们甚至无法做到这一点,请使用Andrea Bertani的建议-删除其他部分来隔离这些部分。
回答
找到常规区域后,就可以进行更细粒度的测量,直到我们找出CPU使用率的来源为止。如果那时如何解决尚不明显,那么我们至少需要弹药来解决更具体的问题。
- 获取Process Explorer,并在JIT中显示%Time,在GC中显示%Time,CPU周期增量,CPU时间,CPU%和线程。
- 我们还需要内核和用户时间,以及一些代表性的堆栈跟踪,但是我认为我们必须点击Properties才能获取快照。
- 比较拍摄前后。
如果我们实际上已经完成了所有这些常见的故障排除,请让我们秘密进行。
- GC过多(GC时间增加的百分比。此外,Perfmon GC和CPU计数器也将对应)
- 过多的线程和关联的上下文切换(正在运行的线程数)
- 轮询(堆栈跟踪始终在单个函数中捕获)
- 过多的内核时间(内核时间长-当CPU高时,任务管理器显示较大的内核时间)
- 异常(" PE .NET"选项卡中抛出的异常很高并且越来越高。还有一个Perfmon计数器)
- 病毒/ rootkit(好,这是最后一个解决方案-但可以构造一个隐藏在TaskManager中的rootkit。我怀疑如果足够狡猾的话,然后可以将不可避免的CPU使用率分配给另一个进程。此外,如果我们已排除上述所有情况,那么我现在就没有主意了)
段落数量不匹配
段落数量不匹配