Параллельная загрузка потоковых данных из RabbitMQ в Postgres - PullRequest
0 голосов
/ 18 ноября 2018

Я все еще немного новичок в Node.js, поэтому я не настолько осведомлен о том, как параллелизм работает с параллельными операциями ввода-вывода, как мне хотелось бы.

Я планируюПриложение Node.js для загрузки потоковых данных из RabbitMQ в Postgres.Эти нагрузки будут происходить во время работы системы, поэтому это не массовая нагрузка.

Я ожидаю, что начальные требования к пропускной способности будут довольно низкими (возможно, 50-100 записей в минуту).Но я хотел бы спланировать приложение так, чтобы оно могло масштабироваться до больших объемов по мере появления требований.

Я пытаюсь продумать, как будет работать параллелизм.Мои первые впечатления о потоке и о том, как будет представлен параллелизм:

  1. Сообщение, прочитанное из очереди
  2. Запущен запрос на загрузку данных в Postgres, что приводит к обратному вызову в стек Node
  3. Цикл событий свободен для чтения другого сообщения из очереди, если оно доступно, при котором запускается другой запрос
  4. Повтор

Я считаю запросызапускается таким образом, будет работать параллельно до количества соединений в моем пуле соединений PG.Это хорошее предположение?

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

Я не нашел большой справки о том, сколько параллельных узлов ввода / вывода будет создано.Будет ли узел в конечном итоге блокироваться, так как мой цикл обработки событий генерирует слишком много запросов ввода-вывода, которые еще не разрешены (если нет, я предполагаю, что pg поместит мой запрос в стек обратного вызова, когда мне придется ждать соединения)?Есть ли шкалы, которые я могу включить, чтобы повлиять на эти ограничения, установив переключатели при запуске Node?Правильно ли я предполагаю, что libuv и lib "pg" на самом деле будут выполнять эти запросы параллельно в рамках одного процесса Node.js?Если эти предположения верны, я подумаю, что достигну пределов размера пула соединений, прежде чем столкнусь с лимитами параллелизма libuv (или, возможно, в то же время, если размер пула соединений будет соответствовать количеству ядер на сервере).

Кроме того, в связи с обсуждением выше, посвященным запуску параллельных запросов ввода-вывода Node, как я могу запретить Node извлекать сообщения из очереди так быстро, как они поступают и ставить в очередь запросы ввода-вывода?Я думаю, что в какой-то момент это может вызвать проблемы с использованием памяти.Это относится к моему вопросу о параметрах запуска, чтобы ограничить количество создаваемых запросов параллельного ввода-вывода.Я не очень хорошо понимаю это в данный момент, поэтому, может быть, это не проблема (может быть, по умолчанию Node не будет создавать больше параллельных запросов ввода-вывода, чем ядра, обеспечивая естественный предел?).

Другая вещь, которую мне интересно, это когда / как параллельное выполнение нескольких копий этой программы поможет?Имеет ли это значение даже для одного хоста, поскольку пул соединений Postgres, похоже, является драйвером параллелизма?Если это так, я бы, вероятно, запустил только одну копию для каждого хоста и запустил бы дополнительные копии на других хостах, чтобы распределить нагрузку.

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

1 Ответ

0 голосов
/ 04 декабря 2018

Я решил это с помощью теста прототипа, который я написал. Несколько замечаний:

  1. Если я не установлю предварительную выборку на канале RabbitMQ, узел вытянет ВСЕ сообщения из очереди в считанные секунды. Я выполнил тест со 100K-сообщениями из очереди, и Node вытащил все 100K за секунды, хотя на самом деле обработка сообщений заняла много минут.
  2. Поведение, упомянутое в # 1 выше, нежелательно, потому что тогда Node должен кэшировать все сообщения в памяти. В моем тесте Node занял 2 ГБ при быстром удалении всех этих сообщений, тогда как если я установил предварительную выборку в соответствии с количеством соединений с базой данных, Node занял только 80 МБ и медленно опустошил очередь, так как он завершил обработку сообщений и отправлены обратно ACK.
  3. Один экземпляр Node, на котором запущена эта программа, сохранил загрузку моих процессоров на 100%.

Итак, мораль этой истории выглядит так:

  1. Узел может порождать любое количество асинхронных обработчиков ввода / вывода (ограничено доступной памятью)
  2. В таком случае вы хотите ограничить количество асинхронных запросов ввода-вывода, которые создаются узлом, чтобы избежать чрезмерного использования памяти.
  3. Создание дополнительных дочерних процессов для этой рабочей нагрузки не имеет значения. Единицей параллелизма был размер пула соединений с базой данных. Если бы моя рабочая нагрузка делала больше в JavaScript, а не просто делегировала Postgres, помогли бы дополнительные дочерние процессы. Но в данном случае это все операции ввода-вывода (и, к счастью, ввод-вывод не нуждается в пуле потоков Node), поэтому дополнительные дочерние процессы ничего не делают.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...