- PVSM.RU - https://www.pvsm.ru -
В предыдущем посте я рассказал про то, как настроить и использовать php телеграм клиент madelineProto для парсинга постов. Но при использовании библиотеки я столкнулся с несколькими недостатками:
Поэтому решил создать два микросервиса на php для парсинга телеграм каналов, используя асинхронное расширение swoole. Теперь эти пакеты упрощают и ускоряют работу с telegram api (не путать с bot api) в нескольких моих проектах. Хочется поделится ими и услышать мнение других разработчиков.
Под катом расскажу об архитектуре, использовании разных областей видимости в swoole server и устранении последствий ошибок в сторонних библиотеках и внешних api. Ссылки на репозитории с исходным кодом и на тестовый сервер — в конце поста.
Изначально планировался один пакет, который выступал бы в качестве парсера telegram и генератора RSS потоков. Но в процессе разработки код становился все более и более неподдерживаемым. Стало ясно, что нужно строже следовать одному из базовых канонов разработки: метод или библиотека должны решать только одну задачу.
В результате декомпозиции появились два микросервиса: TelegramSwooleClient и TelegramRSS.
Общая схема сервиса по генерации RSS потоков из telegram каналов
TelegramRSS
тот микросервис отвечает за коммуникацию с пользователями и генерацию RSS потоков. Упрощенная схема его работы:
В TelegramRSS использование swoole сервера было не обязательным, но дало небольшие преимущества:
TelegramSwooleClient
Эта библиотека / микросервис отвечает за коммуникацию с telegram api. По сути — это обертка над madelineProto, задача которой держать телеграм клиент в памяти и при получении http запроса вызывать соответствующий метод madelineProto.
Допустим, нам нужно получить 10 последних постов из канала. Пример кода из документации madelineProto:
if (!file_exists('madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
}
include 'madeline.php';
$MadelineProto = new danogMadelineProtoAPI('session.madeline');
$MadelineProto->start();
$messages_Messages = $MadelineProto->messages->getHistory([
'peer' =>'breakingmash',
'offset_id' => 0,
'offset_date' => 0,
'add_offset' => 0,
'limit' => 10,
'max_id' => 0,
'min_id' => 0,
'hash' => 0,
]);
Данный код будет выполняться минимум 1-2 секунды при горячем запуске или около 10 секунд при холодном. Большую часть этого времени занимает проверка или генерация временных ключей, соединение с серверами телеграм и авторизация на них.
Однако, если запустить swoole server, инициализировать madelineProto при запуске раз и в обработчике запросов использовать инициализированный экземпляр madelineProto, то мы получим сервис, который будет обрабатывать запросы за 100-200 мс. Пример простого обращения к такому микросервису, дающего аналогичный результат:
//Для примера используется file_get_contents, но через него сложно обрабатывает ошибки.
//В реальных задачах лучше использовать curl
$response = file_get_contents('http://127.0.0.1:9503/api/messages.getHistory/?data[peer]=breakingmash&data[limit]=10&data[offset_id]=0&data[offset_date]=0&data[add_offset]=0&data[max_id]=0&data[min_id]=0&data[hash]=0');
if ($response){
$response = json_decode($response, true);
}
$messages_Messages = $response['response'];
Основные преимущества микросервисного подхода:
Swoole сервер дает огромные преимущества, но сложно ли его запустить? Совсем нет.
Разберем на примере:
//Ради примера код скомпонован в "спагетти" и немного упрощен.
//В проекте он находится в нескольких разных методах
//Создаем сервер, который будет слушать запросы
$http_server = new swoole_http_server(
'127.0.0.1',
9503,
SWOOLE_BASE
);
//Указываем что обрабатываем все запросы в один поток и используем http сжатие данных
$http_server->set([
'worker_num' => 1,
'http_compression' => true,
]);
//Инициализируем класс, в котором будем хранить черный список ip адресов, время бана и тд...
$ban = new Ban();
//Инициализируем класс через который будем общаться madelineProto
//Инициализация займет 1-10 секунд, но будет произведена только 1 раз при старте сервера
$client = new TelegramSwooleClientClient();
//Создаем callback с обработчиком запросов
//На каждый запрос будет вызываться наш callback и в него будут передаваться объекты с запросом и ответом.
$http_server->on('request', function(SwooleHttpRequest $request, SwooleHttpResponse $response) use($client, $ban)
{
//На каждый запрос создаем новый экземпляр класса Controller.
//переменные $client и $ban - в данном случае глобальные. Они содержат классы, неизменные для всех запросов. Данные внутри этих классов хранятся пока работает сервер.
//Их так же можно использовать, как простой кеш.
new Controller($request, $response, $client, $ban);
});
//Запускаем сервер
//Этот метод будет выполняться все время работы сервера.
$http_server->start();
Это лишь часть кода из библиотеки TelegramSwooleClient, но она дает представление о том, как запустить http swoole сервер, и как использовать разные области видимости.
Надеюсь, вы уже хотите начать использовать swoole в своих проектах, поэтому распишу чуть подробнее про установку.
Для установки необходим, как минимум, виртуальный сервер с KVM, для возможности установки расширений php. На ubuntu 18.04, с php 7.3 сделал следующее:
# устанавливаем pecl
apt-get install php-dev
# для поддержки http2 в swoole нужно это расширение
apt install libnghttp2-dev
# иногда в системе нет g++, он нужен для компиляции расширения из исходников
apt-get install g++
# устанавливаем актуальную версию swoole
pecl install swoole
Далее последуют вопросы касательно включения разных модулей. В моем случае я сделал такой выбор:
enable sockets supports? [no] : yes
enable openssl support? [no] : no
enable http2 support? [no] : yes
enable mysqlnd support? [no] : yes
enable postgresql coroutine client support? [no] : no
На macOs вместо apt-get можно использовать brew. Но нужно помнить, что swoole не дружит с дебагерами (Xdebug), поэтому при запуске swoole сервера для локальной разработки надо предварительно отключить это расширение (удалять не обязательно).
В связи с тем, что swoole сервер — это по сути демон, который должен работать непрерывно, нужно предусмотреть автоматическое восстановление работы после падений. Для перезапуска при падении / выходе я использую supervisor.
Содержимое .conf файла для TelegramSwooleClient:
[program:telegram_client]
command=/usr/bin/php /home/admin/web/tg.i-c-a.su/TelegramSwooleClient/server.php
numprocs=1
directory=/home/admin/web/tg.i-c-a.su/TelegramSwooleClient/
autostart=true
autorestart=true
stdout_logfile=none
redirect_stderr=true
Для TelegramRSS конфигурация аналогична.
Логи в supervisor отключены, так как логирование реализовано внутри пакета. Главные параметры — это `autostart=true` (запускает микросервис при запуске системы) и `autorestart=true` (безусловный перезапуск, даже если работа была завершена без ошибок).
В последних версиях madelineProto есть неприятная особенность: при получении запроса после интервала более 10-15 минут без запросов библиотека выдает неустранимую ошибку. После этого требуется ее перезапускать. Все запросы, которые придут во время перезапуска вернут пользователям ошибку.
Для решения этой проблемы я сначала использовал рестарт по крону:
*/15 * * * * supervisorctl restart telegram_client
Но условия возникновения фатальной ошибки и интервал точно установить не удалось. Вероятно, в часы пик телеграм агрессивнее закрывает неактивные соединения, и такой сценарий не предусмотрен в библиотеке. Но из-за во время рестарта сервис гарантированно был недоступен, что было не приемлемо.
Сейчас реализован более элегантный костыль: TelegramSwooleSever завершает работу при определенном виде ошибки в madelineProto и происходит перезапуск через supervisor. TelegramRSS ждет, когда сервер восстановит работу и дублирует ему запросы. Таким образом, нет ненужных рестартов. А если при обработке запроса TelegramSwooleSever упал, то запрос все равно будет обработан корректно, хоть и с задержкой в несколько секунд.
Используя swoole версий 2 и 4 в течении последнего года, могу сказать: на умеренных нагрузках он стабилен, не вызывает утечек памяти и вполне подходит для продакшена. Но нужно тщательно тестировать все зависимости и свой код, что бы не было никаких не перехваченных Throwable.
ab тесты вроде
ab -c 100 -n 1000 https://tg.i-c-a.su/
Выдерживает без проблем.
Среднее использование памяти (параметр RSS из утилиты ps): до 30 МБ для TelegramRSS и до 60 МБ для TelegramSwooleClient.
Автор: xtrime
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/311313
Ссылки в тексте:
[1] VPS: https://www.reg.ru/?rlink=reflink-717
[2] github.com/xtrime-ru/TelegramSwooleClient: https://github.com/xtrime-ru/TelegramSwooleClient
[3] github.com/xtrime-ru/TelegramRSS: https://github.com/xtrime-ru/TelegramRSS
[4] tg.i-c-a.su: https://tg.i-c-a.su
[5] www.swoole.co.uk: https://www.swoole.co.uk
[6] docs.madelineproto.xyz: https://docs.madelineproto.xyz
[7] supervisord.org/configuration.html#program-x-section-settings: http://supervisord.org/configuration.html#program-x-section-settings
[8] www.draw.io: https://www.draw.io
[9] Источник: https://habr.com/ru/post/354000/?utm_campaign=354000
Нажмите здесь для печати.