Pull to refresh

Comments 36

Огромное спасибо! Буквально на днях столкнулся с memory leak при множественных http-запросах. Для Express добавил ваш код сюда node_modules\express\lib\response.js и память перестала уходить! Вот интересно, ждать патча от NodeJS или просить добавить в framework'и костыль?
Можно добавить, а потом ждать. Когда будет патч, то ничего не испортится, патч с заплаткой не конфликтуют.
… патч с заплаткой не конфликтуют.

Черт, как я отстал от жизни… всегда думал что patch и заплатка — одно и то же :-)
UFO just landed and posted this here
Скоро я отправлю пул-реквесты соответственно в joyent/node и nodejs/node (бывший io.js, а сейчас в нём уже склеенные проекты).
Насколько скоро? (Хотя бы примерно?)
Заранее сложно сказать, осталось еще несколько моментов, которые я хочу протестировать не только нодовскими тестами, а в условиях реальной нагрузки. Например, склеить свои изменения с правкой donnerjack13589 по поводу смены prefinish на finish, потому, что я ее случайно затер, перенеся бездумно свою правку из ноды в io.js.
Большое спасибо на находку и ее исследования!
А при данном баге память постоянно течет? Т.е. даже после каких-то долгих таймаутов (2 минуты, например) не очищается?
У нас есть high-load приложение на ноде, 3-4 тысячи пользователей онлайн на каждом из серверов, запросов очень много. Память течет, исследовать настолько глубоко нет времени/средств, пришлось делать под 20 воркеров на каждом сервере (которые расходуют по 10-20% цпу) и перезагружать раз в сутки эти воркеры, чтобы очистить память. Если делаем мало воркеров (10, например), то в пиках нагрузки они начинают грузить проц под 100%, не могут обработать запрос и цпу не снижается даже сли нагрузку вообще убрать.
Как думаете, ваш багфикс сможет нам помочь?
Дескрипторы сокетов и память накапливаются не навсегда, срок освобождения — 2 минуты, но этого достаточно, чтобы скорость утечки была выше скорости освобождения и чтобы ушла вся память (1-2Гб), а процесс залип (бывает) или вывалился (это немного лучше, чем залип). В приведенном тесте делается 15к запросов за 200 секунд, это очень мало, всего 75 rps, но уходит 100 Мб. А при 3к-5к rps (сильно зависит еще от того, как приложение расходует память) уже и достигается критический объем утечки, чтобы все навернулось.
А что значит «критический объем утечки»? Почему после какого-то предела по памяти нода залипает по цпу?
У нас на серверах памяти много, по ней не упираемся в какие-то лимиты, но если нода начинает много памяти кушать, то она начинает залипать по цпу… и это проблема.

У нас ~500 запросов в секунду в пиках, каждый из воркеров потребляет ~200 мб памяти
По умолчанию, если не делать --max_old_space_size, то у ноды же известное ограничение по памяти, а если подымать его упомянутым ключом, то менеджер памяти становится не эффективным, залипает (в зависимости от сложности структур данных и интенсивности их выделения/освобождения) Вы знаете. У нас ~150k rps на группе серверов в целом и до 2.5 млн открытых сокетов на каждом сервере (20/40 core/thread, 256 Гб памяти). И это стало проблемой. А у людей памяти то часто бывает поменьше и для них это совсем критическая проблема — процессы перегружаются каждые 10 минут.
не думали на жаву перейти с такими нагрузками? я на своем домашнем 4х ядерном амд поднимал 2кк коннектов, хватило гигов 12ти памяти
У нас не все 256 Гб используются на ноду, на 2.5 млн соединений порядка 40 Гб уходит, но там еще много всякой бизнес-логики, структур данных в памяти, не знаю точно, сколько под сокетные буферы из этого.
А у вас нода без перезапусков работает? Утечек вообще никаких нет?
Мы стараемся данные держать где-нить в Redis'е. При обильном использовании памяти нода быстро уходила в цпу 100%, правда это еще было при версии ноды ~0.5…
Без перезапусков, мы сделали для этого все возможное и это себя оправдало. Несколько серверов, на каждом много процессов и все соединены через шину событий и синхронизируют состояние между процессами. Раньше делали это через ZMQ, а теперь по своему протоколу. И да, у нас свой сервер приложений Impress: habrahabr.ru/post/247543 На других фреймворках есть утечки, и они такие, что перезапуск процессов считается нормальной практикой. У нас если процессы и перезапускаются, то не все вместе, и вновь поднятые забирают состояние из соседних. Процессы падают только от ошибок в прикладном поде, которые после некоторого времени тестовой эксплуатации ликвидируются. Есть специальные процессы (менеджеры сервера приложений) которые не обрабатывают бизнес-логики и запросов от клиента вообще, поэтому они не падают и гарантировано держат состояние, кроме того, даже этих специальных процессов несколько, на каждом сервере свой.
Спасибо за подробный ответ! У нас тоже есть немного схожего с вашей архитектурой: тоже есть мастер процесс, который следит за остальными и не работает с запросами и бизнес логикой, процессы перезагружаются раз в сутки в разнобой (не все разом), нормально завершая свою работу (обрабатывая последние запросы и не принимая следующие), а состояние все в редисе хранится.
Но я очень рад слышать, что все-таки можно иметь приложение без утечек, нам уже казалось это из ряда фантастики для node.js.

У нас в глубине приложения используются express и sockjs. Есть какой-то опыт с ними по поводу утечек?
Только не на принципе middleware, с тщательно отобранными модулями и с очень хорошо вычищенным прикладным кодом. Кроме того, мы оборачиваем прикладной код в песочницы (sandboxes) см. vm.createContext и при утечке можно уничтожать песочницу, а не процесс целиком, и вся выделенная в ней память уйдет. Новую песочницу можно создавать до удаления старой и после синхронизации стейта просто подменять их.
Если не секрет, что за проект с таким rps?
Проектов таких у меня несколько, их можно разделить на три группы:
1. Телевизионные интерактивные шоу на принципе второго экрана (уже все готово),
2. Телеметрия технологических процессов и серверных инфраструктур (внедряется и на подходе еще),
3. Финансовая аналитика и корпоративные информационные системы реального времени (в работе).
Помнится, раньше тоже таймеры в node.js чинил и оптимизировал. В частности, починил регрессию в node.js, из-за которой clearTimeout() работал в 15 раз медленней — github.com/joyent/node/issues/4225

И даже статейку написал про внутреннее устройство таймеров — habrahabr.ru/company/alawar/blog/155509. Не уверен, правда, что она все еще актуальна, особенно в свете выхода io.js.
Кстати, таймеры для таймаута сокетов сделаны через enroll/unenroll, которые не приводят к созданию отдельного таймера для каждого сокета. Мне это понравилось, потому, что при 2.5 млн одновременных соединений мне сложно представить, сколько бы CPU уходило на таймеры.
А влияет ли данный патч на соединения через веб-сокеты?
Только косвенно: на сервере обычно параллельно идут HTTP API запросы и WS запросы, чем лучше освобождается память и прикрываются соединения от API запросов, тем лучше для вебсокетов. По сути, запросы, висящие 2 минуты, не отличаются от WS по использовнию ресурсов и когда они быстрее завершаются, это хорошо.
А если используется только WS? Например веб-сервер — это ngnix, а само приложение общается с сервером только через WS.
Для этого случая ни как не влияет. Но тут можно существенно оптимизировать, убрав nginx из цепочки. В такой конфигурации от него нет смысла, кроме двойного использования ресурсов и задержки.
А чем плох такой вариант? nginx отдает страницы, стили, скрипты, картинки и ни в какой цепочке не учавствует, а само приложение общается с серверной частью через ws. Т.е., node используется только как WS сервер. Мне кажется такое разделение вполне логичным — никакого оверхеда на всякие заголовки http и все такое прочее.
Если между нодовским ws сервером и браузером ничего не стоит, то это хорошо. А статика отдается из nginx с другого порта, а лучше — с CDN. Если использовать CORS, то все ок. Но я люблю так: без CORS, нода отдает ws + API + HTML, а вот всю остальную статику отдает nginx или CDN. Конечно нода тоже должна уметь отдавать статику, чтобы ее забирал и кешировал CDN, раздавая потом ее с поддомена, например static.domainname.com. Если отдавать ws с того же хоста и порта, что и HTML, то можно обойтись и без CORS.
А в каком состоянии сейчас патч? Я так понимаю, от него не отказались совсем, но и вмерживать не торопятся. Там какие то принципиальные недочеты нашли?
Патч развивается, все можно почитать на странице с пул-реквестом: github.com/nodejs/node/pull/2534 Это слишком нежное место ноды, чтобы вот так сразу все включить в релиз. Сейчас решение уже немного другое, чем было при написании статьи, потому, что нужно еще восстанавливать обычный таймаут, если за время keepAliveTimeout восстановится активность в сокете и подобные нюансы. Сейчас уже все в порядке, все тесты проходят, ждем склейки.
Ну вроде пошла движуха, осталось склеить коммиты и коммент отформатить, должны вмержить куда-то около пятерки ) Ленивые гады, пинать их надо
Да там вообще много нужно переписать говнокодища, http и cluster никуда не годятся, только с таким подходом, что нельзя ничего трогать и всех изменений по минимуму, проще новый язык сделать уже, не то, что новый рантайм.
Ну, они хотя бы согласились вмержить то, что есть, это уже неплохо. Я правда очень хочу, чтобы ваш пул попал в мастер )
Sign up to leave a comment.

Articles