我试图描述一个持续处于或接近100%CPU使用率的Akka应用程序。我使用visualvm
获取了一个CPU样本。该示例表明有2个线程占CPU使用率的98.9%。 79%的cpu时间花在了一个名为sun.misc.Unsafe
的方法上。 Other answers on SO说它只是意味着一个线程在等待但在本机实现层(在jvm之外)。
在类似于我的问题中,人们told to look elsewhere没有得到具体细节。我应该在哪里找出引起cpu峰值的原因?
该应用程序是一个主要使用Akka IO来侦听TCP套接字连接的服务器。
答案 0 :(得分:3)
如果没有看到任何源代码,或者甚至不知道你正在谈论什么IO通道(套接字,文件等),那么这里的任何人都无法提供任何洞察力。
我确实有一些相当一般的建议。
首先,您应该在应用程序中使用被动技术和被动IO。这个问题可能正在发生,因为您在紧密循环中轮询某些资源的状态,或者在使用阻塞调用时你应该使用被动的。这往往是一种反模式和性能消耗,因为你可以花费CPU周期而只是“主动等待”。我建议仔细检查:
Future
代替map
其次,您不应该在应用程序中使用互斥锁或其他线程同步。如果是这样,那么您可能会遇到活锁。与死锁不同,实时锁表现出100%CPU使用率等症状,因为线程不断锁定和解锁并发原语以试图“抓住所有”。 Wikipedia对活锁的外观有一个很好的技术描述。使用Akka,您不需要使用互斥锁或任何线程同步原语。如果你那么你可能需要重新设计你的应用程序。
第三,你应该限制IO(以及重新连接尝试等错误处理)。这个问题可能正在发生,因为你的系统缺乏有效的限制。通常使用数据通道,我们不受带宽限制。然而,当该通道达到100%饱和并开始从系统的其他部分窃取资源时,这可能成为一个问题。例如,如果您在没有合理限制的情况下移动大型文件,就会发生这种情况。
或者,您还需要在遇到任何错误时限制连接重试,而不是立即重试。如果许多系统失去连接,它们将尝试重新连接到服务器。虽然通常需要,但如果您使用天真的重新连接策略,这可能会导致问题行为。例如,想象一下以这种方式编写的网络客户端:
class MyClient extends Client {
... other code...
def onDisconnect() = {
reconnect()
}
}
每次客户端因任何原因断开连接时,它都会尝试重新连接。如果Wifi断路器或网络电缆已拔下,您可以看到这将导致错误处理代码与客户端之间的紧密循环。
第四,您的应用程序应该有明确定义的数据源和接收器。您的问题可能是由“数据循环”引起的,即一组Akka演员只是向下一个发送消息链中的演员,最后一个演员将消息发送回链中的第一个演员。确保您有明确的方式让消息进入和退出系统。
第五,为您的应用程序使用适当的分析和工具。使用Kamon或Coda Hale的Metrics库来检测您的应用程序。
找到合适的分析器将会更加困难,因为我们作为一个社区有很长的路要走,为反应式应用开发成熟的工具。就个人而言,我发现visualvm
很有用,但对于检测CPU绑定的代码路径并不总是非常有帮助。问题是采样分析器只能在JVM到达安全点时收集数据。这有可能偏向某些代码路径。修复方法是使用支持AsyncGetStackTrace
。
祝你好运!如果可以,请添加更多上下文。