[xParser] 1.8.0 Вспомогательный Node JS демон или ускоряем парсинг в 27 раз!


Встала задача ускорить, насколько это возможно, работу парсера, т.к. у клиента отработка задания на парсинг 7к ресурсов длилась в течение ~2-3 суток!
Первым делом я подумал, что проблема таится в получении данных со стороннего ресурса. 7 тысяч запросов к стороннему ресурсу только за текстовой информацией, а ещё у каждой записи по 5 картинок, в общей сложности получается 42к запросов к стороннему ресурсу.
Если в среднем запрос длится по 1 секунде, то получается 42 тысячи секунд, что эквивалентно 11 часам и ещё 40 минутам! А ведь ресурс ещё нужно сохранить, на что в среднем уходит по 1-2 секунды. Это ещё +22 часа работы. Итого ~34 часа на отработку задания на парсинг 7к ресурсов, в лучшем случае. Мда… не дело!


А как же встроенная асинхронность?


Помнится, с версии 1.6.0 у компонента появилась классная функция асинхронных запросов к стороннему ресурсу через библиотеку ReactPHP. На деле оказалось, что не на всех серверах оно работает корректно — где-то вообще не работает, а где-то часть запросов теряется.
К сожалению, могу констатировать, что ReactPHP, который был встроен в xParser, не добавляет стабильной асинхронности при запросах к внешнему ресурсу. Поэтому, в будущих версиях он будет либо заменён на что-то другое, либо вообще удалён из компонента. Пока в новых версиях этот функционал принудительно отключён, но сегодня не об этом.

Ищем решение


Поразмышляв, я пришёл к выводу, что компоненту не хватает реальной асинхронности, которая есть, например, в JavaScript. Такие размышления навели меня на мысль: «А что если у компонента будет свой Node JS демон, который будет выполнять запросы к сторонним ресурсам?», идея мне показалась интересной и я принялся её воплощать.

Первое препятствие


Когда всё было готово и тестовый запуск произведён, оказалось, что запросы к сторонним ресурсам — не единственная проблема. Когда парсер собрал все данные и выгрузил картинки к себе, он переходит к сохранению ресурсов через стандартный процессор MODX (resource/create или resource/update). Путём наблюдения за этим процессом было выяснено, что первые ресурсы сохраняются за пол секунды, а то и меньше, но чем дальше в лес, тем дольше времени надо на это действие. Доходит до того, что 1 ресурс где-то на ~1.500 обработанных записей, обновляется через стандартный процессор в течение 40 секунд! С чем это связано, я так и не разобрался (хоть и пытался), может какая утечка памяти или что-то в этом роде…

Прокачиваем демон


Решено было отдать демону и сохранение ресурса. Написав необходимый функционал на стороне Node JS и доработав xParser таким образом, чтобы при доступности демона он отдавал все необходимые данные ему, мы снова принялись тестировать скорость работы парсинга. Результат удивил даже меня. Очень удивил!

Приятный итог


В общем, не буду томить, то же задание на 7к записей отработало за 1 час 15 минут. Мне сначала показалось, что произошёл какой-то сбой и я полез проверять целостность спарсенных данных, оказалось, что всё на месте! Поэтому, если взять изначальные 34 часа, которые по нашим подсчётам требовались на парсинг этого задания и разделить их на итоговый результат, то получается, что работа парсера на этом задании была ускорена в ~27 раз!

P.S.


Я скоро дополню документацию компонента разделом о том, как любой обладатель xParser сможет запустить на своём сервере вспомогательный демон и настроить его. А пока я озабочен безопасностью этого решения.
Павел Гвоздь
29 октября 2019, 10:25
modx.pro
6
1 190
+27
Поблагодарить автора Отправить деньги

Комментарии: 15

Павел Голубев
29 октября 2019, 11:36
0
Если ReactPhp не подходит, то можно глянуть в сторону Swoole www.swoole.co.uk/
    srs
    srs
    29 октября 2019, 13:23
    +2
    По заголовку, ожидал хоть немного кода и пояснения того как автор прикручивал nodejs к xparser, но оказалось, что это скорее анонс. В любом случае, новость хорошая.
    Пару лет назад, писал парсер на php который должен был собирать данные и создавать/обновлять ресурс. Изначально нужно было парсить чуть меньше 500 позиций. Затем требования увеличились до 1,5-2к, через пару месяцев 5-7к. Соответственно время парсинга увеличилось в разы. Клиенты компании на которую я работаю, утверждали, что ваш modx говно, решение тоже говно, и мол на битриксе есть готовое решение которое делает это за 20-30 минут. В начале я думал, что просто я не компетентен и упускаю какую-то очень важную деталь, но все оказалось куда проще. Клиенты наглые врунишки!
    В итоге тоже пришлось родить монстра на nodejs+php. На текущий день нода обрабатывает порядка от 60к до 80к позиций от разных источников. Начиная от парсинга xml фидов, до работ с api сторонних сервисов. Нода отрабатывает все это дело от 8 до 20 минут (там есть еще пару факторов). В итоге получаем файлик с позициями которые нужно создать/обновить/удалить, этот файлик отрабатывает php, примерно от 10 до 80 минут. Такая разница возникает из-за того, что количество обновляемых/создаваемых ресурсов всегда разная, это может быть как 1к так и 10-15к. Но в среднем 3-3,5к на создание/обновление и 800-1500 позиций на удаление. Вот такая вот кулсторибоб)
      Павел Гвоздь
      29 октября 2019, 13:26
      +3
      ожидал хоть немного кода
      Пост в разделе «Новые дополнения и их версии» с префиксом «xParser 1.8.0» в названии. Да, это анонс.

      А битрикс, да! Маркетологам лишь бы продать этот кусок ? поэтому рождаются такие вот легенды.
        srs
        srs
        29 октября 2019, 18:29
        +1
        Пост в разделе «Новые дополнения и их версии»
        Да, не обратил внимания. Прошу прощенья.
        Егор
        30 октября 2019, 14:28
        +4
        на битриксе есть готовое решение
        аххаха как же знакомо! А что париться! Покупаете 1с предприятие, потом 1с битрикс связываете одно с другим и продажи прут! Там же все из коробки уже настроено! Осталось только подключить! :))) Я бы взял у Павла гвоздь, и вбил бы его в голову тому гениальному маркетологу.
        Yar
        Yar
        29 октября 2019, 13:38
        +2
        не поленился залогинититься, чтобы поставить лайк)
          Егор
          30 октября 2019, 13:03
          +1
          Ну что сказать… Не так давно нужно было спарсить несколько тыщ страниц. Использовал стороннее ПО, рекламировать не буду. 7 тыщ парсилось почти сутки. А потом клоудфлэр начал что-то подозревать и запломбировал доступ к сайту. Толи я прокси не так настроил, то ли… В общем, Павел выбора не оставляет — надо брать!
            Алексей
            26 января 2020, 19:48
            0
            будет ли версий 1.8 работать без серверного node? Не уверен, что он есть у меня на хостинге. И, в догонку, может будет какая-то инструкция:
            1. какой версии нужен node
            2. как его поставить, на VPS, ubuntu
              Павел Гвоздь
              27 января 2020, 07:47
              0
              будет ли версий 1.8 работать без серверного node?
              Конечно. Демон — это толкьо дополнение по желанию.
                Алексей
                03 февраля 2020, 11:07
                0
                Очень круто, потесчу скорость -)
                А можно ли сделать импорт/экспорт HTML/RSS заданий? (очень удобно создать шаблон, и по его примеру уже создавать новые задания, но не удобно дергать Sequel pro чтобы копировать шаблон задания через базу)
                  Павел Гвоздь
                  03 февраля 2020, 14:14
                  0
                  А можно ли сделать импорт/экспорт HTML/RSS заданий?
                  Могу добавить в бэклог, однако не обещаю, что скоро появится.
              Алексей
              10 марта 2020, 12:51
              0
              доброго дня! возникает ошибка после обновления, в какую сторону копать?
              запускаю из консоли файлик /core/components/xparser/cron/parser.php
              Fatal error: Uncaught InvalidArgumentException: Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "boolean". in /core/components/xparser/vendor/symfony/dom-crawler/Crawler.php:120
              Stack trace:
              #0 /core/components/xparser/vendor/symfony/dom-crawler/Crawler.php(67): Symfony\Component\DomCrawler\Crawler->add(false)
              #1 /core/components/xparser/handlers/domcrawler/xpdomcrawler.class.php(44): Symfony\Component\DomCrawler\Crawler->__construct(false)
              #2 /core/components/xparser/handlers/parser/xpparser.class.php(225): xpDomCrawler->factory(false)
              #3 /core/components/xparser/handlers/parser/xpparser.class.php(553): xpParser->getSourceItems(Array)
              #4 /core/components/xparser/handlers/parser/xpparser.class.php(704): xpParser->parseTask(Array)
              #5 /core/components/xparser/processors/mgr/task/parse.class.php(111): xpParser->parseTask(Array)
              #6 /core/components/xparser/vendor/symfony/dom-crawler/Crawler.php on line 120
                Павел Гвоздь
                10 марта 2020, 13:44
                0
                в какую сторону копать?
                В сторону ТехПоддержки на Модстор, пжл.
                  Алексей
                  11 марта 2020, 09:16
                  0
                  запустил по новой — всё ок! даже не знаю в чём могла быть ошибка

                  а даже когда сайт на локалке можно через поддержку обращаться? там же даже удалённо толком не зайти в админку, только если через удалённый рабочий стол
                Владимир
                06 апреля 2020, 11:25
                0
                Привет всем
                А Инстаграм ни кто не парсит? Так что б мимо их api просто по крону?
                  Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                  15