Почему в ботах телеги желательно использовать Webhook вместо Polling
Привет, коллеги!
Сегодня у нас на повестке дня выбор между двумя технологиями: Polling и Webhook. Почему почему именно Webhook является go-to решением для большинства проектов?
Помните, как в начале 2010-х все разрабы активно юзали Polling? Это был золотой стандарт для многих мессенджер-ботов. Но технологии не стоят на месте. Webhook занял свое место, предлагая свои решения.
Polling
Polling, или опрос, — это процесс, при котором клиентский скрипт периодически отправляет запросы к серверу для проверки наличия новой инфы. В телеге это регулярный запрос к АПИ для получения новых обновлений или сообщений.
При увеличении нагрузки Polling может стать бутылочным горлышком. Такую проблемку можно решить увеличением интервала между запросами в моменты низкой активности и его уменьшение в пиковые часы.
Если бот обрабатывает состояния или сессии пользователей нужно учитывать, что при Polling состояние должно храниться на стороне сервера. Это означает необходимость использования БДшек и т.п
Постоянные запросы к серверу создают дополнительную нагрузку. Необходимо настраивать подходящий баланс, чтобы избежать чрезмерного трафика и потенциального блокирования вашего бота из-за слишком частых запросов. API в телеграме ожидаемо имеет ограничения на количество запросов в секунду. Необходимо убедиться, что бот не превышает эти лимиты.
Согласно документации максимальное количество сообщений, отправляемых ботом, ограничено примерно 30 сообщениями в секунду для обычных сообщений и около 20 сообщений в минуту для групповых чатов.
Если бот достигает этих лимитов, будут вылетать ошибки RetryAfter
от API. Постоянные попытки повторной отправки сообщений, игнорируя ошибки API, могут привести к временной блокировке бота.
В 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. Также разберут основные ошибки и проведем код-ревью проекта.