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

Интеграция 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 сервера, по умолчанию 127.0.0.1
    • Redis Port - порт Redis сервера, по умолчанию 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 должно соответствовать числу потоков, указанному в задании
    • вы можете также включить опцию ведения лога, таким образом будет доступна возможность просмотра лога по каждому запросу

Пример настройки задания с API::Server::RedisAPI::Server::Redis

Получение запроса API

Запуск A-Parser вместе с Redis используя docker-compose

При таком способе запуска в качестве адреса Redis сервера (Redis Host) вместо IP можно указать имя сервиса, в примерах ниже это redis

Если А-Парсер ранее не запускался через docker-compose

  1. Загрузите и распакуйте дистрибутив (одноразовую ссылку предварительно нужно взять в Личном кабинете, как описано здесь):
curl -O https://a-parser.com/members/onetime/ce42f308eaa577b5/aparser.tar.gz
tar zxf aparser.tar.gz
rm -f aparser.tar.gz
  1. Создайте файл docker-compose.yml и поместите в него следующее содержимое:

    • Базовый вариант без пароля и открытия порта, Redis будет доступен только внутри Docker сети
    version: '3'

    services:
    a-parser:
    image: aparser/runtime:latest
    command: ./aparser
    restart: always
    volumes:
    - ./aparser:/app
    ports:
    - 9091:9091

    redis:
    image: redis:latest
    restart: always
    • Вариант с паролем и открытием порта, Redis будет доступен из вне, поэтому настоятельно рекомендуется использовать пароль
    version: '3'

    services:
    a-parser:
    image: aparser/runtime:latest
    command: ./aparser
    restart: always
    volumes:
    - ./aparser:/app
    ports:
    - 9091:9091

    redis:
    image: redis:latest
    restart: always
    command: redis-server --requirepass ТУТ_ПАРОЛЬ_ДЛЯ_REDIS
    ports:
    - 6379:6379

    Вместо ТУТ_ПАРОЛЬ_ДЛЯ_REDIS придумайте и укажите пароль, который будет использоваться при авторизации в Redis.

  2. Запустите контейнеры:

docker compose up -d

Если А-Парсер ранее уже был запущен через docker-compose

  1. Отредактируйте файл docker-compose.yml добавив в конец следующее содержимое:

    • Базовый вариант без пароля и открытия порта, Redis будет доступен только внутри Docker сети
      redis:
    image: redis:latest
    restart: always
    • Вариант с паролем и открытием порта, Redis будет доступен из вне, поэтому настоятельно рекомендуется использовать пароль
      redis:
    image: redis:latest
    restart: always
    command: redis-server --requirepass ТУТ_ПАРОЛЬ_ДЛЯ_REDIS
    ports:
    - 6379:6379

    Вместо ТУТ_ПАРОЛЬ_ДЛЯ_REDIS придумайте и укажите пароль, который будет использоваться при авторизации в Redis.

  2. Запустите контейнеры:

docker compose up -d
примечание

Если A-Parser уже был запущен и его конфигурация не изменилась, то он не будет перезапущен, а Docker просто добавит и запустит Redis.

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

Работа 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 этап форматирования результата пропускается, т.к. весь результат передается в виде JSON для дальнейшей программной обработки.

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 запроса