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

Интеграция 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 запроса