Не видя какого-либо исходного кода или даже не зная, о каком канале IO вы говорите (сокеты, файлы и т. Д.), Очень мало информации о том, что каждый может вам дать.
У меня есть некоторые общие предложения.
Во-первых, вы должны использовать реактивные методы и реактивный IO в своем приложении. Эта проблема может возникнуть из-за того, что вы проверяете статус какого-либо ресурса в узком цикле или используете блокирующий вызов, когда вы должны использовать реактивный. Это, как правило, анти-шаблон и производительность, потому что вы можете проводить циклы CPU, ничего не делая, кроме «активного ожидания». Я рекомендую двойную проверку для:
- избирательного ресурса
- блокировки вызовов
- системы вызывает
- диска флеши
- ждет на
Future
, когда это будет уместно map
вместо этого
Во-вторых, вы не должны использовать Mutexes или другую синхронизацию потоков в своем приложении. Если да, тогда вы можете страдать от живого замка. В отличие от мертвых замков, live-locks проявляются с такими симптомами, как 100% -ное использование ЦП, поскольку потоки постоянно блокируют и разблокируют примитивы параллелизма, пытаясь «поймать их всех». Wikipedia имеет хорошее техническое описание того, как выглядит живой замок. При использовании Akka вам не нужно будет использовать Mutexes или примитивы синхронизации нитей. Если вы тогда, вам, вероятно, придется перепроектировать свое приложение.
В-третьих, вы должны дросселировать IO (а также обработку ошибок, например, попытки повторного соединения). Эта проблема может возникать, потому что вашей системе не хватает эффективного дросселирования. Часто с каналами передачи данных мы оставляем их пропускную способность без ограничений. Однако это может стать проблемой, когда этот канал достигает 100% насыщенности и начинает красть ресурсы из других частей системы. Это может произойти, например, если вы перемещаете большие файлы без разумного ограничения.
В качестве альтернативы вам также необходимо активировать попытки повторного подключения при возникновении любых ошибок, а не повторять попытку немедленно. Многие системы попытаются повторно подключиться к серверу, если они потеряют соединение. Хотя это обычно желательно, это может привести к проблемному поведению, если вы используете стратегию наивного пересоединения.Например, представьте себе, сетевой клиент, который был написан так:
class MyClient extends Client {
... other code...
def onDisconnect() = {
reconnect()
}
}
Каждый раз, когда клиент отсоединяется по любой причине, он будет пытаться восстановить соединение. Вы можете увидеть, как это приведет к жесткому циклу между кодом обработки ошибок и клиентом, если отключен Wi-Fi-выход или сетевой кабель.
В-четвертых, ваше приложение должно иметь четко определенные источники данных и раковины. Ваша проблема может быть вызвана «циклом данных», то есть некоторым набором аккских актеров, которые просто отправляют сообщения следующему актеру в цепочке, а последний актер отправляет сообщение первому актеру в цепочке. Удостоверьтесь, что у вас есть четкий и четкий способ ввода сообщений и выхода из системы.
В-пятых, используйте соответствующие профили и приборы для вашего применения. Прикрепите приложение к Kamon или библиотеке Metrics Coda Hale.
Поиск подходящего профайлера будет сложнее, поскольку мы, как сообщество, должны далеко продвинуться в разработке зрелых инструментов для реактивных приложений. Лично я нашел visualvm
полезной, но не всегда чрезвычайно полезной для обнаружения путей кода, связанных с ЦП. Проблема в том, что профилировщики выборки могут собирать данные только в том случае, если JVM достигает безопасного места. Это может привести к смещению определенных кодов кода. Исправление заключается в использовании профилировщика, который поддерживает AsyncGetStackTrace
.
Удачи! И, пожалуйста, добавьте больше контекста, если сможете.
Вы говорите, что вы взяли образец ЦП. Что ты конкретно имеешь ввиду? –
sun.misc.Unsafe - это не метод, это класс. Продемонстрируйте полную трассировку стека. –