Почему в ботах телеги желательно использовать Webhook вместо Polling

от автора

Привет, коллеги!

Сегодня у нас на повестке дня выбор между двумя технологиями: Polling и Webhook. Почему почему именно Webhook является go-to решением для большинства проектов?

Помните, как в начале 2010-х все разрабы активно юзали Polling? Это был золотой стандарт для многих мессенджер-ботов. Но технологии не стоят на месте. Webhook занял свое место, предлагая свои решения.

Polling

Polling, или опрос, – это процесс, при котором клиентский скрипт периодически отправляет запросы к серверу для проверки наличия новой инфы. В телеге это регулярный запрос к АПИ для получения новых обновлений или сообщений.

При увеличении нагрузки Polling может стать python-telegram-bot (с версии 20), существует встроенный механизм BaseRateLimiter, который контролирует количество API запросов, совершаемых ботом за определенный временной интервал

Простейший скрипт Polling бота:

import time import requests  TOKEN = "токен_бота" URL = f"https://api.telegram.org/bot{TOKEN}/getUpdates"  def get_updates():     response = requests.get(URL)     return response.json()  def main():     while True:         updates = get_updates()         if updates["result"]:             # Обработка каждого нового сообщения             for item in updates["result"]:                 print(f"Сообщение от пользователя: {item['message']['text']}")         time.sleep(2)  # Задержка в 2 секунды между запросами  if __name__ == "__main__":     main()

Бот каждые две секунды спрашивает у сервера Telegram, не пришло ли новых сообщений. В реальности тут конечно должна быть обработка ошибок, логирование и другие элементы

С Polling было связано множество трудностей. Во-первых, нагрузка на сервера. Каждый бот постоянно что то запрашивал у сервера. Представьте себе тысячи ботов, делающих это каждую секунду. Это было не только неэффективно с точки зрения трафика, но и могло вызывать задержки в доставке сообщений.

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

Со временем, когда телега становилась всё более попсовой, разработчики начали искать более эффективные способы взаимодействия с API. И здесь появляются вебхуки. В отличие от Polling, где бот активно запрашивал данные, Webhook позволяет серверу телеграма самому отправлять обновления боту, как только они появляются.

Webhook

Webhook – это, по сути, колллбек. Это URL, который вы предоставляете телеграму, и по которому телеграм будет отправлять обновления в формате JSON каждый раз, когда вашему боту приходит сообщение или происходит другое событие.

В начале вам нужно настроить Webhook. Это делается путем отправки запроса к Telegram API с указанием URL вашего сервера. Телега будет использовать этот URL для отправки обновлений:

from telegram.ext import Updater, CommandHandler from telegram import Bot import logging  # логирование logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',                     level=logging.INFO) logger = logging.getLogger(__name__)  # функция для обработки команды /start def start(update, context):     update.message.reply_text('Привет!')  def main():     TOKEN = 'token'     bot = Bot(TOKEN)      # Updater и передача токена вашего бота     updater = Updater(token=TOKEN, use_context=True)      # диспетчер для регистрации обработчиков     dp = updater.dispatcher      # регистрация команды /start     dp.add_handler(CommandHandler("start", start))      # настроим вебхук     updater.start_webhook(listen='0.0.0.0',                           port=8443,                           url_path=TOKEN,                           key='YOUR_PRIVATE.key',                           cert='YOUR_PUBLIC.pem',                           webhook_url='https://YOUR.SERVER.IP.ADDRESS:8443/' + TOKEN)      updater.idle()  if __name__ == '__main__':     main()

YOUR_PRIVATE.key и YOUR_PUBLIC.pem это путь к ключу SSL, и YOUR.SERVER.IP.ADDRESS адрес сервера.

Для Webhook необходим SSL-сертификат. Telegram требует, чтобы обмен данными был защищен. Телеграм поддерживает ограниченное количество портов для Webhook: 443, 80, 88, 8443.

Для настройки SSL-терминации на веб-сервере нужно установить и настроить Nginx или Apache. На Nginx:

server {     listen 443 ssl;     server_name YOUR.SERVER.IP.ADDRESS;      ssl_certificate /path/to/YOUR_PUBLIC.pem;     ssl_certificate_key /path/to/YOUR_PRIVATE.key;      location / {         proxy_pass http://localhost:8443;         proxy_set_header Host $host;         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header X-Forwarded-Proto $scheme;     } }

Как только кто-то взаимодействует с ботом (например, отправляет ему сообщение), телеграм делает запрос на юрл вебхук с деталями этого события. Эти данные приходят в виде JSON-объекта, который содержат информацию, к примеру:

{     "update_id": 123456789,     "message": {         "message_id": 111,         "from": {             "id": 222,             "is_bot": false,             "first_name": "viktor",             "username": "viktor12313"         },         "chat": {             "id": 333,             "first_name": "viktor",             "username": "viktor12313",             "type": "private"         },         "date": 1609459200,         "text": "ку"     } }

Сервер затем обрабатывает этот запрос, парсит полученный JSON и выполняет необходимые действия, например, отвечает на сообщение пользователя.

Следующий шаг — это создание обработчика для входящих сообщений. В коде это обычно реализуется через определение функций с декораторами, указывающими на типы обрабатываемых сообщений (например, текст, изображения и т.д.). В качестве примера, можно использовать библиотеку fastapi вместе с telebot. Создаётся экземпляр класса AsyncTeleBot, затем настраивается webhook и определяются функции для обработки команд (/start, /help) и обычных текстовых сообщений. При получении обновления, бот использует await для асинхронной обработки сообщения и отправляет ответ:

import fastapi from telebot.async_telebot import AsyncTeleBot import telebot  API_TOKEN = 'токен' WEBHOOK_URL = 'вебхук'  bot = AsyncTeleBot(API_TOKEN) app = fastapi.FastAPI()  @app.post('/')   async def process_webhook(update: dict):       if update:           update = telebot.types.Update.de_json(update)           await bot.process_new_updates([update])    @bot.message_handler(commands=['help', 'start'])   async def send_welcome(message):       await bot.reply_to(message, "Бот запущен...")    @bot.message_handler(func=lambda message: True, content_types=['text'])   async def echo_message(message):       await bot.reply_to(message, message.text)  

Итого в таблице сравнение выглядит так:

Критерий

Polling

Webhook

Время отклика

Относительно медленное

Почти мгновенное

Нагрузка на сервер

Высокая, требует постоянных запросов

Низкая, сервер ждёт уведомлений

Сложность реализации

Простая, легко реализуемая

Средняя, требует настройки HTTPS, SSL

Масштабируемость

Ограничена, увеличение нагрузки на сервер с ростом числа пользователей

Высокая, эффективна при большом количестве пользователей

Использование ресурсов

Активное, постоянный опрос сервера

Пассивное,проще говоря принимай когда пришло

Управление трафиком

Неэффективное, из-за постоянных запросов

Эффективное, обрабатываются только актуальные события

Безопасность

Более уязвим для ддос атак из-за открытых запросов

Более безопасный, использует зашифрованные соединения

Настройка и обслуживание

Простая, не требует дополнительной настройки

Сложнее, требует настройки веб-сервера и SSL-сертификата


Webhook хоть и немного сложнее в настройке, имеет гораздо больше плюсов по сравнению с Polling.

В завершение хочу порекомендовать бесплатный урок на котором коллеги из OTUS разберут практические подходы, которые помогут вам писать чистый, поддерживаемый код на Python.


ссылка на оригинал статьи https://habr.com/ru/articles/786754/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *