Перейти к основному содержимому

Интеграция A-Parser с Redis: продвинутое API

Сравнение с HTTP API

A-Parser Redis API был разработан для замены методов oneRequest и bulkRequest для более производительной реализации и поддержки дополнильных сценариев использования:

  • в качестве сервера запросов и результатов выступает Redis
  • возможность запрашивать результаты асинхронно или в блокирующем режиме
  • возможность подключить множество парсеров(как на одном так и на разных серверах) для обработки запросов с единой точкой входа
  • возможность задасть количество потоков для обработки запросов и просматривать логи работы
  • возможность организации таймаутов на операции
  • автоматический Expire невостребованных результатов

Тема обсуждения на форуме

Предварительная настройка

В отличие от A-Parser HTTP API для использования Redis API необходимо предварительно настроить и запустить задание с парсером API::Server::RedisAPI::Server::Redis:

  • установить и запустить сервер Redis(локально или удаленно)
  • создать пресет настроек для парсера API::Server::RedisAPI::Server::Redis, указать:
    • Redis Host и Redis Port - адрес и порт Redis сервера, по умолчанию 127.0.0.1, порт 6379
    • Redis Queue Key - название ключа для обмена данными с A-Parser, по умолчанию aparser_redis_api, вы можете создавать раздельные очереди и обрабатывать их разными заданиями или разными копиями A-Parser'а
    • Result Expire(TTL) - время жизни результата в секунда, служит для автоматического контроля и удаления невостребованных результатов, по умолчанию 3600 секунд(1 час)
  • добавить задание с парсером API::Server::RedisAPI::Server::Redis
    • в качестве запросов необходимо указать {num:1:N}, где N должно соответствовать числу потоков, указанному в задании
    • вы можете также включить опцию ведения лога, таким образом будет доступна возможность просмотра лога по каждому запросу

Выполнение запросов

Работа Redis API основана на Redis Lists(списках), операции над списками позволяют добавлять в очередь неограниченной число запросов(ограничено оперативной памятью), а также получать результаты в блокирующем режиме с таймаутом(blpop) или в асинхронном режиме(lpop).

  • все настройки, кроме useproxy, proxyChecker и proxybannedcleanup берутся из пресета вызываемого парсера + overrideOpts
  • настройки useproxy, proxyChecker и proxybannedcleanup берутся из пресета API::Server::RedisAPI::Server::Redis + overrideOpts

Запрос добавляется в Redis командой lpush, каждый запрос состоит из массива [queryId, parser, preset, query, overrideOpts, apiOpts] сериализованного с помощью JSON:

  • parser, preset, query соответствует аналогичным для API запроса oneRequest
  • queryId - формируется вместе с запросом, рекомендуем использовать порядковый номер из вашей базы или хороший рандом, по данному ID можно будет получить результат
  • overrideOpts - переопределние настроек для пресета парсера
  • apiOpts - дополнительные параметры обработки API

redis-cli

Пример выполнения запросов, для тестирования можно использовать redis-cli:

127.0.0.1:6379> lpush aparser_redis_api '["some_unique_id", "Net::HTTP", "default", "https://ya.ru"]'
(integer) 1
127.0.0.1:6379> blpop aparser_redis_api:some_unique_id 0
1) "aparser_redis_api:some_unique_id"
2) "{\"data\":\"<!DOCTYPE html><html.....

Различные кейсы

Асинхронная проверка наличия результата

lpop aparser_redis_api:some_unique_id

Вернет результат если он уже обработан или nil если запрос еще в процессе обработки

Блокирующее получение результата

blpop aparser_redis_api:some_unique_id 0

Данный запрос будет блокирован до момента получения результата, вы также можете указать максимальный таймаут на получение результата, после чего команда вернет nil

Сохранение результатов в единую очередь

По умолчанию A-Parser сохраняет результат для каждого запроса под своим уникальным ключом aparser_redis_api:query_id, что позволяет огранизовать многопоточную обработку, отправляя запросы и получая результаты отдельно для каждого потока

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

Для этого необходимо указать ключ output_queue для apiOpts:

lpush aparser_redis_api '["some_unique_id", "Net::HTTP", "default", "https://ya.ru", {}, {"output_queue": "aparser_results"}]'

Получение результата из общей очереди:

127.0.0.1:6379> blpop aparser_results 0
1) "aparser_results"
2) "{\"queryId\":\"some_unique_id\",\"results\":{\"data\":\"<!DOCTYPE html><html class=...

Пример реализации(кейс SpySERP)

Предположим мы создаем SaaS сервис, который производит оценку параметров доменов, для простоты мы будем проверять дату регистрации домена

Наш сервис состоит из 2ух страниц:

  • /index.php - лендинг страница, на которой расположена форма ввода домена
  • /results.php?domain=google.com - страница с результатами работы сервиса

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

При запросе к results.php мы в первую очередь выполняем запрос в A-Parser Redis API, формируя уникальный request_id:

​lpush aparser_redis_api '["request-1", "Net::Whois", "default", "google.com", {}, {}]'

После чего мы можем вывести страничку пользователю и отобразить лоадер на области отображения данных, за счет отсутсвия задержек ответ сервера будет ограничен только скоростью подключения Redis(обычно в пределах 10мс)

A-Parser начнет обработку запроса еще до того как браузер пользователя получит первый контент, после того как браузер подгрузит все необходимые ресурсы и скрипты, мы можем отобразить результат, для этого отправляем AJAX запрос на получение данных:

/get-results.php?request_id=request-1

Скрипт get-results.php выполняет блокирующий запрос к Redis с таймаутом 15 секунд:

blpop aparser_redis_api:request-1 15

И возвращает ответ сразу как только он будет получен от A-Parser, если мы получили нулевой результат по таймауту, то мы можем отобразить ошибку получения данных для пользователя

Таким образом, отправляя запрос в A-Parser при первом открытии страницы(/results.php) мы сокращаем необходимое время ожидания данных для пользователя(/get-results.php) на время, которое браузер пользователя тратит на ожидание контента, подгрузку скриптов и выполнение AJAX запроса