Интеграция A-Parser с Redis: продвинутое API
Сравнение с HTTP API
A-Parser Redis API был разработан для замены методов oneRequest и bulkRequest для более производительной реализации и поддержки дополнильных сценариев использования:
- в качестве сервера запросов и результатов выступает Redis
- возможность запрашивать результаты асинхронно или в блокирующем режиме
- возможность подключить множество парсеров(как на одном так и на разных серверах) для обработки запросов с единой точкой входа
- возможность задасть количество потоков для обработки запросов и просматривать логи работы
- возможность организации таймаутов на операции
- автоматический Expire невостребованных результатов
Предварительная настройка
В отличие от A-Parser HTTP API для использования Redis API необходимо предварительно настроить и запустить задание с парсером API::Server::Redis:
- установить и запустить сервер Redis(локально или удаленно)
- создать пресет настроек для парсера
API::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::Redis
- в качестве запросов необходимо указать {num:1:N}, где N должно соответствовать числу потоков, указанному в задании
- вы можете также включить опцию ведения лога, таким образом будет доступна возможность просмотра лога по каждому запросу
Выполнение запросов
Работа Redis API основана на Redis Lists(списках), операции над списками позволяют добавлять в очередь неограниченной число запросов(ограничено оперативной памятью), а также получать результаты в блокирующем режиме с таймаутом(blpop
) или в асинхронном режиме(lpop
).
- все настройки, кроме
useproxy
,proxyChecker
иproxybannedcleanup
берутся из пресета вызываемого парсера +overrideOpts
- настройки
useproxy
,proxyChecker
иproxybannedcleanup
берутся из пресетаAPI::Server::Redis +
overrideOpts
Запрос добавляется в Redis командой lpush
, каждый запрос состоит из массива [queryId, parser, preset, query, overrideOpts, apiOpts]
сериализованного с помощью JSON
:
parser
,preset
,query
соответствует аналогичным для API запроса oneRequestqueryId
- формируется вместе с запросом, рекомендуем использовать порядковый номер из вашей базы или хороший рандом, по данному 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
запроса