没有看到任何源代码,甚至不知道您在谈论什么 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 的分析器。
祝你好运!如果可以,请添加更多上下文。