Long Polling vs Webhook
Telegram Bot API предоставляет два способа получения обновлений для вашего бота: long polling и webhook. У каждого подхода есть свои компромиссы в плане разработки, производительности и масштабируемости.
Как работает Long Polling
При long polling ваш бот непрерывно вызывает метод getUpdates в цикле. Telegram удерживает соединение открытым, пока не появятся новые обновления (или не истечёт таймаут), а затем отвечает ими.
Bot Telegram
│── getUpdates ──────────────►│
│ │ (ожидает обновлений...)
│◄─────────── [update] ───────│
│── getUpdates ──────────────►│
│ │ (ожидает...)
│◄─────────── [] ─────────────│ ← нет обновлений
│── getUpdates ──────────────►│
...- Ваш бот инициирует каждый запрос — Telegram никогда не обращается к вашему боту напрямую
- Параметр
timeoutконтролирует, как долго Telegram удерживает соединение (по умолчанию: 30, рекомендуется: 25–30 секунд) - Не требуется публичный URL или SSL-сертификат
- Просто запускается локально — достаточно стартовать бота
В GramIO long polling используется по умолчанию:
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
bot.on("message", (context) => {
return context.send("Hello!");
});
bot.start(); // ← используется long polling по умолчаниюКак работает Webhook
При использовании webhook вы регистрируете HTTPS URL в Telegram. Когда приходит обновление, Telegram отправляет HTTP POST запрос на этот URL с данными обновления.
Bot Telegram
│ │
│ (простой — нет трафика) │
│ │
│ │ ← пользователь отправил сообщение
│◄──── POST /webhook ────────│
│── 200 OK ──────────────────►│
│ │
│ (простой — нет трафика) │- Telegram пушит обновления вашему боту — цикл опроса не нужен
- Требуется публично доступный HTTPS URL с валидным SSL-сертификатом
- Меньшая задержка — обновления приходят мгновенно, без ожидания следующего цикла опроса
- Более эффективно — нет трафика, когда обновлений нет
Что выбрать?
Разработка — Long Polling
Long polling идеален для локальной разработки:
- Не нужен домен или SSL — работает на
localhostиз коробки - Без конфигурации — просто вызовите
bot.start() - Простая отладка — перезапуск бота мгновенный, не нужно настраивать туннели
- Нет проблем с файрволом — только исходящие соединения, не нужно открывать входящие порты
Продакшен — Webhook
Webhook — рекомендуемый выбор для продакшен-развёртываний:
- Меньшая задержка — обновления доставляются мгновенно, без задержки опроса
- Нет холостых соединений — ресурсы не тратятся, когда обновлений нет
- Горизонтальное масштабирование — несколько экземпляров сервера за балансировщиком нагрузки
- Совместимость с serverless — работает с Vercel, AWS Lambda, Cloudflare Workers и аналогичными платформами
- Эффективность ресурсов — нет фонового цикла опроса, потребляющего CPU и память
Несколько подов и масштабирование
Здесь выбор становится критически важным.
Long Polling: только один экземпляр
Telegram отклоняет одновременные вызовы getUpdates от одного токена бота. Если два процесса опрашивают одновременно, один получает ошибку 409 Conflict. Это означает:
- Можно запускать только один экземпляр с polling
- Нет горизонтального масштабирования
- Нет реплик в Kubernetes, нет сервисов Docker Swarm с
replicas > 1 - Если единственный процесс упадёт, обновления задерживаются до его перезапуска
Webhook: горизонтальное масштабирование
При webhook Telegram отправляет обновления на URL — ему всё равно, сколько серверов за ним стоит. Это открывает:
┌─── Pod 1 (bot) ◄──┐
Telegram ──POST──►│ Load Balancer │
├─── Pod 2 (bot) ◄──┤
└─── Pod 3 (bot) ◄──┘- Балансировщик нагрузки распределяет входящие запросы по подам
- Каждый под обрабатывает часть обновлений независимо
- Автомасштабирование работает естественно — добавляйте поды по мере роста нагрузки
- Бесшовные деплои — обновление подов без простоя (rolling updates)
WARNING
При масштабировании с webhook убедитесь, что логика вашего бота не хранит состояние локально (или используйте общее хранилище, например Redis, для сессий). Каждое обновление может прийти на другой под.
Сравнительная таблица
| Характеристика | Long Polling | Webhook |
|---|---|---|
| Сложность настройки | Минимальная | Требуется HTTPS URL + SSL |
| Нужен публичный URL | Нет | Да |
| Задержка | Зависит от интервала опроса | Мгновенная доставка |
| Ресурсы в простое | Постоянные (цикл опроса) | Нулевые |
| Горизонтальное масштабирование | Нет (один экземпляр) | Да (балансировщик) |
| Поддержка serverless | Нет | Да |
| Локальная разработка | Легко | Требуется туннель |
| Проблемы с файрволом | Нет (только исходящие) | Нужен входящий HTTPS |
Настройка Webhook в GramIO
Поддерживаемые фреймворки
Пример
import { Bot, webhookHandler } from "gramio";
import Fastify from "fastify";
const bot = new Bot(process.env.BOT_TOKEN as string);
const fastify = Fastify();
fastify.post("/telegram-webhook", webhookHandler(bot, "fastify"));
fastify.listen({ port: 3445, host: "::" });
bot.on("message", (context) => {
return context.send("Fastify!");
});
bot.start({
webhook: {
url: "https://example.com:3445/telegram-webhook",
},
});Использование вебхуков только в продакшене
Вместо использования туннелей вы можете просто использовать polling в среде разработки!
const bot = new Bot(process.env.BOT_TOKEN);
await bot.start({
webhook:
process.env.NODE_ENV === "production"
? {
url: `${process.env.API_URL}/${process.env.BOT_TOKEN}`,
}
: undefined,
});Когда webhook равен undefined, GramIO автоматически переключается на long polling.
Локальная разработка с вебхуками
Для локальной разработки с вебхуками мы рекомендуем использовать unjs/untun.
Untun — это инструмент для создания туннеля между вашим локальным HTTP(s) сервером и внешним миром!
IMPORTANT
Примеры запуска с конкретными фреймворками опущены. Смотрите этот пример.
Через API
Этот метод позволяет установить ссылку на наш туннель непосредственно в скрипте.
Установите пакет:
::: pm-add untun :::
Запустите туннель и установите вебхук:
import { startTunnel } from "untun";
const tunnel = await startTunnel({ port: 3000 });
bot.start({
webhook: {
url: await tunnel.getURL(),
},
});Через CLI
Мы прослушиваем порт 3000 локально. Поэтому открываем туннель следующим образом:
npx untun@latest tunnel http://localhost:3000yarn dlx untun@latest tunnel http://localhost:3000pnpx untun@latest tunnel http://localhost:3000bunx untun@latest tunnel http://localhost:3000◐ Starting cloudflared tunnel to http://localhost:3000
ℹ Waiting for tunnel URL...
✔ Tunnel ready at https://unjs-is-awesome.trycloudflare.comТеперь мы используем эту ссылку при установке вебхука:
bot.start({
webhook: {
url: "https://unjs-is-awesome.trycloudflare.com",
},
});Напишите свой собственный обработчик обновлений
// не существующий фреймворк для примера
import { App } from "some-http-framework";
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string).on("message", (context) =>
context.send("Hello!")
);
// init обязателен. Он используется для ленивой загрузки плагинов, а также получает информацию о боте.
await bot.init();
const app = new App().post("/telegram", async (req) => {
// req.body должен быть json эквивалентом TelegramUpdate
await bot.updates.handleUpdate(req.body);
});
app.listen(80);