[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
7
3 164
+27
Поблагодарить автора Отправить деньги

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

Павел Голубев
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с битрикс связываете одно с другим и продажи прут! Там же все из коробки уже настроено! Осталось только подключить! :))) Я бы взял у Павла гвоздь, и вбил бы его в голову тому гениальному маркетологу.
        brioni
        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 просто по крону?
                Андрей
                07 июня 2020, 10:20
                0
                Добрый день.
                Подскажите как решить такую проблему: при обращении парсером к сайту по селектору body — другой вообще не хочет брать пишет вот что:
                Array
                (
                    [pagetitle] => <h1 class="error">
                			<span>
                									<a class="chrome" href="http://www.google.com/chrome" title="Download Chrome"></a>
                					<a class="firefox" href="http://www.mozilla.com" title="Download Firefox"></a>
                					<a class="opera" href="http://www.opera.com" title="Download Opera"></a>
                					<a class="safari" href="http://www.apple.com/safari" title="Download Safari"></a>
                					<a class="ie" href="http://www.microsoft.com/downloads" title="Download Internet Explorer 9"></a>
                							</span>
                		</h1>
                		<h2 class="title">Обновите браузер</h2>
                		<p class="message">Просматриваемый вами сайт правильно работает только в современных браузерах. Обновите свой браузер для увелечения безопасности и комфортной работы. Вы можете выбрать один из представленных ниже браузеров. Если все же решите продолжить просмотр, <a href="https://site.ru/?forwardOutdatedBrowser=1">нажмите сюда</a></p>
                )
                  Алексей
                  23 июня 2021, 15:38
                  0
                  Демон постоянно сыпет ошибками:
                  pages timeout done
                  Error: connect ECONNREFUSED 207.241.237.3:443
                      at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1132:16) {
                    errno: -61,
                    code: 'ECONNREFUSED',
                    syscall: 'connect',
                    address: '207.241.237.3',
                    port: 443,
                    config: {
                  .........
                  MacBook-Pro:~ alex$ node -v
                  v16.4.0
                  В чём может быть дело? уже обновил ноду с 10 до 16 — сильно не помогло. Что я делаю не так?
                    Павел Гвоздь
                    24 июня 2021, 14:56
                    0
                    Не знаю, я на маке не запускал. Возможно в этом дело. На линуксе всё работает, если поднимать по документации.
                      Алексей
                      25 июня 2021, 07:59
                      0
                      может тогда docker контэйнер запилить для маководов? провозился наверное целый день — никак не запустил, проще было дождаться php-версии парсера
                    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                    20