Назад | Перейти на главную страницу

mongoose / mongodb DoS-атака на экземпляр nodeJS

У нас есть экземпляр nodeJS внутри AWS VPC, который отправляет запрос экземпляру mongodb, работающему в другой зоне доступности.

Экземпляр узла получает определенный запрос много. Этот запрос используется для получения большого количества информации из экземпляра mongodb. После первого запроса эта запись кешируется в течение определенного периода времени.

Вчера что-то случилось с объемом возвращаемых данных. С таким кодом, как:

 console.log('before retrieve');
 Model.find({}).exec(function() {
   console.log('after retrieve');
 });

Если 10 раз ударил бы «до получения», а затем просто остановился бы, DoS-ing сам. Я удалил некоторые данные, которые он извлекал, в качестве временного исправления.

На стороне mongoDB я иногда видел:

 SocketException handling request, closing client connection: 9001 socket exception [SEND_ERROR]

Как я могу этого избежать?

Проблема, которую вы описываете, имеет гораздо меньшее отношение к mongoose, mongodb и node, а скорее является проявлением проблемы, обычно называемой «паническое бегство в кеше» или «собачья куча».

Как следует из названия, паника кеша происходит, когда целая куча вещей пытается обновить кеш сразу. В вашем случае это происходит по истечении срока действия кеша или во время начальной загрузки данных в кеш. Внезапно приходит множество запросов и накладывает большую нагрузку на чтение вашей базы данных, что 1) приводит к медленному обновлению кеша и 2) вызывает еще большее количество запросов, которые складываются в ожидании кеша. Это в основном приводит к поведению, которое вы видели, когда все просто рушится.

Эта страница в Википедии достаточно четко описывает проблему и способы ее решения с помощью отдельного процесса или блокировок. Поскольку узел не имеет блокировок или потоков, это, вероятно, не решение. Кроме того, хотя отдельный процесс будет работать, он намного сложнее.

Один из методов, который я использовал в прошлом, заключается в использовании двух ключей кэша с истекающим сроком действия, один ключ используется только для указания того, когда следует обновить кеш, а другой содержит фактические данные.

Для иллюстрации предположим, что у меня есть объект foo которые я хочу кэшировать, и срок их действия истекает каждый час. Я могу создать еще один ключ foo_refresh что я истекаю за 1 минуту до foo ключ.

Когда foo_refresh истекает срок действия ключа, один воркер / запрос немедленно заменяет foo_refresh key и игнорирует кеш и вместо этого извлекает данные из базы данных, обновляя foo по завершении (также сбрасывая время истечения срока действия). Используя подобный механизм, мы получаем своего рода «блокировку» при обновлении кеша, что означает, что не более одного рабочего будет когда-либо выполнять дорогостоящее чтение.

Предполагая, что обновление кеша занимает менее 1 минуты, foo объект никогда не истекает, вместо этого он обновляется по истечении срока foo_refresh ключ.

Надеюсь, это поможет!