CoderCastrov logo
CoderCastrov
Производитель-потребитель Проблема

Я спас себя от падения в черную дыру, созданную браузерами Chrome - Nodejs

Я спас себя от падения в черную дыру, созданную браузерами Chrome - Nodejs
просмотров
5 мин чтение
#Производитель-потребитель Проблема

Опыт ограничения создания критических ресурсов, решение проблемы производителя-потребителя.

Image courtesy - pexels.com

Это был "пятничный" вечер, и я собирался закончить свою задачу, используя браузер Chrome без графического интерфейса на основе Selenium для загрузки URL-адресов в рамках парсинга.

Все URL-адреса были настроены для загрузки в браузере без графического интерфейса, и я был готов, как солдат, готовый атаковать врага по команде своего командира, чтобы нажать кнопку запуска и, наконец, с энергией и стильно, я нажал клавишу Enter, чтобы запустить скрипт.

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

Со всей своей стильностью нарушенной, я снова запустил приложение. Вскоре я увидел, что в системном мониторе создаются сотни процессов с именем chrome. Я понял, что Chrome тяжелее черной дыры 😄, но он затянет все мои ресурсы в свою точку сингулярности..!!!, я этого никогда не ожидал.

Итак, прежде чем загружать все URL-адреса сразу, я взял около 10 URL-адресов для загрузки. Затем, проверяя парсер с этими 10 URL-адресами, я понял, что нужно ограничить количество одновременно создаваемых браузеров Chrome без графического интерфейса.

Все мои планы на выходные разбились вдребезги...!!

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

Я рассмотрел это как типичную проблему производителя-потребителя. Итак, я начал с идеи создания пула драйверов. Пул драйверов можно рассматривать как кого-то, кто ограничивает количество активных создаваемых драйверов. Если требуется новый драйвер, мы должны запросить его у пула драйверов. Пул драйверов был назван AutoClearConnectionDriverPool (извините за мое плохое умение придумывать названия).

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

Держитесь крепче, начинается решение.



Архитектура и объяснение:

Ниже приведена архитектура AutoCloseConnectionDriverPool, которую мы будем реализовывать по мере продвижения в этой статье.

**Примечание**: Слева находится фактический код, который инициализирует и использует наш AutoClearConnectionDriverPool. Реализуйте его только после завершения реализации предстоящих файлов кода, поскольку он использует все остальные файлы кода для выполнения.

Объяснение:

Как мы можем видеть на приведенной выше архитектурной диаграмме Auto Clear Connection driver pool, метод getDriver() вызывается несколько раз, что создает интервал при каждом его вызове. Каждый новый созданный интервал пытается получить доступ к блоку Create Driver. Блок Create Driver передается только интервалу 1, поскольку он захватывает блокировку, и количество экземпляров драйвера в массиве drivers меньше maxDrivers.

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

Интервал PoolWatchInterval запускается каждые 500 мс для проверки, завершили ли какие-либо экземпляры драйвера свою работу, и удаляет эти экземпляры драйвера из массива drivers, что позволяет следующим ожидающим интервалам создавать новый экземпляр драйвера, захватив блокировку.

Теперь мы можем видеть, что драйверы создаются только тогда, когда в driverPool есть свободное место и блокировка не захвачена никем, что позволяет нам полностью контролировать создание драйверов (включая создание критических ресурсов), просто изменив один параметр, а именно maxDrivers, что в свою очередь решает нашу проблему.



Реализация

Поскольку я предпочитаю объектно-ориентированное программирование, я предпочитаю TypeScript перед JavaScript. Поэтому в качестве первого шага я начал визуализировать структуру в виде следующей диаграммы классов.

Диаграмма классов

Если мы посмотрим на диаграмму классов, то у нас есть интерфейс IDriver, который реализуется классом ChromeDriver. Интерфейс IDriverPool реализуется классом AutoClearConnectionDriverPool. Таким образом, IDriverPool использует интерфейс IDriver для выполнения конкретных задач, а у интерфейса IDriver есть перечисление IDriver.State, которое представляет состояние экземпляра IDriver.

Ниже приведена реализация кода в разных файлах. Имя файла можно увидеть внизу блока кода. Давайте начнем с написания интерфейсов, как указано на диаграмме классов.

Давайте реализуем интерфейс IDriver в классе ChromeDriver, и если мы посмотрим на метод build в классе ChromeDriver, мы создаем безголовый драйвер Selenium Chrome.

Наконец, давайте реализуем интерфейс IDriverPool в классе AutoClearConnectionDriverPool.

Если мы рассмотрим реализацию AutoClearConnectionDriverPool.

При создании объекта AutoClearConnectionDriverPool:

  • Мы создали список/массив drivers, присвоив максимальное количество допустимых активных драйверов maxDrivers.
  • Мы создали интервал, чтобы постоянно отслеживать остановленные экземпляры драйверов в массиве drivers.
  • Если найдены какие-либо остановленные экземпляры драйверов, мы удаляем их из массива drivers (это своего рода автоматический механизм очистки).

При вызове метода getDriver():

  • Мы возвращаем промис, который заставляет цикл событий ждать, пока промис не будет разрешен/отклонен.
  • Внутри промиса мы создали интервал, чтобы постоянно искать свободное место в массиве drivers, не превышая максимальное количество драйверов maxDrivers.
  • Затем проверяем, есть ли место в массиве drivers (drivers.length < maxDrivers) и переменная lock равна false (то есть блокировка не получена).
  • Переменная lock гарантирует, что создание экземпляров драйверов происходит один за другим и не позволяет другим интервалам проходить условие if параллельно. См. диаграмму архитектуры, чтобы увидеть назначение блокировки.
  • Как только условие if пройдено, блокировка применяется, установив ее в true.
  • Создается клон baseDriver и добавляется в массив drivers.
  • Затем блокировка снимается, установив ее в false.
  • Теперь мы очищаем интервал myInterval, чтобы прекратить постоянное выполнение цикла.
  • И, наконец, экземпляр драйвера возвращается, разрешая промис.

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

Поделитесь с друзьями, если считаете это полезным. Поставьте максимальное количество 👏 - 50.

С уважением, Паван Кумар Йекаботе.