Тривале опитування проти вебхуків
Ваш бот може отримувати повідомлення з серверів Telegram двома способами. Вони називаються тривале опитування і вебхуки. grammY підтримує обидва способи, тоді як типовим способом є тривале опитування.
У цьому розділі описано, що таке тривале опитування та вебхуки, а далі наведено деякі переваги та недоліки використання того чи іншого методу розгортання. Також буде описано, як використовувати їх із grammY.
Вступ
Ви можете розглядати всю розповідь про вебхуки та тривале опитування як питання про те, який тип розгортання використовувати. Іншими словами, є два принципово різні способи розміщення вашого бота та запуску його на якомусь сервері; вони відрізняються яким способом повідомлення досягають вашого бота і обробляються grammY.
Цей вибір має велике значення, коли вам потрібно вирішити, де розмістити свого бота. Наприклад, деякі постачальники інфраструктури підтримують лише один із двох типів розгортання.
Ваш бот може отримувати оновлення через тривале опитування або сервери Telegram можуть надсилати їх вашому боту через вебхуки.
Якщо ви вже знаєте, як ці речі працюють, прокрутіть вниз, щоб дізнатися, як використовувати тривале опитування або вебхуки з grammY.
Як працює тривале опитування?
Уявіть, що ви купуєте собі кульку морозива у своєму улюбленому кафе-морозиві. Ви підходите до працівника і просите морозиво, яке вам подобається. На жаль, він каже, що його немає в наявності.
Наступного дня вам знову хочеться того смачного морозива, тож ви повертаєтеся в те саме місце й просите те саме морозиво. Гарні новини! За ніч вони поповнили запаси, щоб ви сьогодні могли насолоджуватися морозивом із солоною карамеллю! Смакота.
Опитування означає, що grammY завчасно надсилає запит до Telegram із запитом на нові оновлення (читайте повідомлення). Якщо там немає повідомлень, Telegram поверне порожній масив, вказуючи, що з моменту останнього запиту вашому боту не було надіслано нових повідомлень.
Коли grammY надсилає запит до Telegram і тим часом вашому боту надсилаються нові повідомлення, Telegram повертає їх у вигляді масиву до 100 обʼєктів.
______________ _____________
| | | |
| | <--- чи є повідомлення? --- | |
| | --- ні. ---> | |
| | | |
| | <--- чи є повідомлення? --- | |
| Telegram | --- ні. ---> | Бот |
| | | |
| | <--- чи є повідомлення? --- | |
| | --- так, ось ---> | |
| | | |
|____________| |___________|
Одразу видно, що це має деякі недоліки. Ваш бот отримує нові повідомлення лише щоразу, як запитує, тобто приблизно кожні кілька секунд. Щоб ваш бот відповідав швидше, ви можете просто надіслати більше запитів і не чекати так довго між ними. Ми могли б, наприклад, запитувати нові повідомлення кожну мілісекунду! Що може піти не так…
Замість того, щоб спамити сервери Telegram, ми використовуватимемо тривале опитування замість звичайного опитування.
Тривале опитування означає, що grammY завчасно надсилає запит до Telegram із запитом на нові оновлення. Якщо повідомлень немає, Telegram залишатиме зʼєднання відкритим до надходження нових повідомлень, а потім відповідатиме на запит цими новими повідомленнями.
Знову час для морозива! Працівник вже вітається з вами по імені. На ваше запитання про морозиво улюбленого сорту співробітник посміхається вам і завмирає. Фактично, ви не отримали ніякої відповіді. Тому ви вирішуєте почекати, посміхаючись у відповідь. І ви чекаєте. Чекаєте.
За кілька годин до світанку прибуває вантажівка місцевої компанії з доставки їжі і привозить пару великих ящиків у складське приміщення кафе. На зовнішній частині написано морозиво. Співробітник нарешті знову починає рухатися. “Звичайно, у нас є солона карамель! Дві мірні ложки з бризками, як звичайно?”
Ніби нічого не сталося, ви насолоджуєтеся морозивом, покидаючи найнереалістичніше у світі кафе-морозиво.
______________ _____________
| | | |
| | <--- чи є повідомлення? --- | |
| | . | |
| | . | |
| | . *обидва чекаєте* | |
| Telegram | . | Бот |
| | . | |
| | . | |
| | --- так, ось ---> | |
| | | |
|____________| |___________|
Зауважте, що насправді жодне зʼєднання не буде відкритим годинами. Запити на тривале опитування мають типовий 30-ти секундний тайм-аут, щоб уникнути ряду технічних проблем. Якщо після цього періоду часу не буде отримано жодного нового повідомлення, запит буде скасовано та надіслано повторно, але загальна концепція залишається незмінною.
Використовуючи тривале опитування, вам не потрібно спамити сервери Telegram, і ви все одно отримуєте нові повідомлення відразу! Чудово. Це те, що grammY робить за замовчуванням, коли ви запускаєте bot
.
Як працюють вебхуки?
Після цього жахливого досвіду (ціла ніч без морозива!) ви б більше воліли нікого не питати про морозиво взагалі. Хіба не було б круто, якби морозиво само прийшло до вас?
Налаштування вебхуку означає, що ви надасте Telegram URL-адресу, доступну з Інтернету. Щоразу, коли вашому боту надсилається нове повідомлення, Telegram, а не ви, бере на себе ініціативу та надсилає запит із обʼєктом оновлення на ваш сервер. Гарно, а?
Ви вирішуєте піти в кафе з морозивом востаннє. Ви кажете своєму другові за прилавком, де ви живете. Він обіцяє особисто завітати до вашої домівки, коли приїде нове морозиво, щоб воно не розтало на пошті. Класний хлопець.
______________ _____________
| | | |
| | | |
| | | |
| | *обидва чекаєте* | |
| | | |
| Telegram | | Бот |
| | | |
| | | |
| | --- привіт, нове повідомлення ---> | |
| | <--- дякую, друже --- | |
|____________| |___________|
Порівняння
Головна перевага тривалого опитування у порівнянні з вебхуками полягає в тому, що воно простіше. Вам не потрібен домен або загальнодоступна URL-адреса. Вам не потрібно возитися з налаштуванням SSL-сертифікатів, якщо ви запускаєте бота на VPS. Використовуйте bot
, і все запрацює, тому що подальше налаштування не потрібне. Під час навантаження ви повністю контролюєте кількість повідомлень, які можете обробити.
Місця, де тривале опитування добре працює, включають:
- Під час розробки на вашій локальній машині.
- На більшості серверів.
- На розміщених “серверних” екземплярах, тобто машинах, на яких ваш бот працює 24/7.
Головна перевага вебхуків у порівнянні з довгим опитуванням полягає в тому, що вони дешевші. Ви заощаджуєте масу зайвих запитів. Вам не потрібно постійно тримати мережеве зʼєднання відкритим. Ви можете використовувати служби, які автоматично зменшують масштаб вашої інфраструктури до нуля, коли запити не надходять. Якщо ви хочете, ви навіть можете здійснити виклик API під час відповіді на запит Telegram, хоча це має ряд недоліків. Перегляньте параметр конфігурації тут.
Місця, де вебхуки добре працюють, включають:
- На серверах із сертифікатами SSL.
- На розміщених “інтерфейсних” екземплярах, які масштабуються відповідно до їхнього навантаження.
- На безсерверних платформах, наприклад, у хмарних функціях або програмованих кордонних мережах.
Я все ще не знаю, що використовувати
Тоді використовуйте тривале опитування. Якщо у вас немає вагомої причини використовувати вебхуки, то зауважте, що тривале опитування не має серйозних недоліків, а також, згідно з нашим досвідом, ви витрачатимете набагато менше часу на вирішення проблем. Час від часу вебхуки можуть бути неприємними (дивіться нижче).
Що б ви не вибрали, якщо у вас виникнуть серйозні проблеми, перейти на інший тип розгортання не буде надто важко. З grammY вам доведеться змінити лише кілька рядків коду. Налаштування ваших проміжних обробників залишаться такими самими.
Як використовувати тривале опитування
Викликати
bot.start();
щоб запустити свого бота за допомогою дуже простої форми тривалого опитування. Він обробляє всі оновлення послідовно. Це робить вашого бота дуже легким для налагодження, а всю поведінку дуже передбачуваною, оскільки паралелізм не використовується.
Якщо ви хочете, щоб ваші повідомлення оброблялися grammY одночасно, або ви турбуєтеся про пропускну здатність, перегляньте розділ про плагін для конкурентності (runner).
Як використовувати вебхуки
Якщо ви хочете запустити grammY з вебхуками, вам потрібно інтегрувати бота у вебсервер. Тому ми очікуємо, що ви зможете запустити простий вебсервер з використанням обраного вами фреймворку.
Кожного бота grammY можна перетворити на проміжний обробник для багатьох серверних фреймворків, зокрема express
, koa
/oak
тощо. Ви можете імпортувати функцію webhook
(довідка API), щоб створити проміжний обробник для відповідного фреймворку.
import express from "express";
const app = express(); // чи будь-що, що ви використовуєте
app.use(express.json()); // перетворюємо тіло запиту JSON
// "express" також використовується за замовчуванням, якщо аргумент не задано.
app.use(webhookCallback(bot, "express"));
2
3
4
5
6
7
const express = require("express");
const app = express(); // чи будь-що, що ви використовуєте
app.use(express.json()); // перетворюємо тіло запиту JSON
// "express" також використовується за замовчуванням, якщо аргумент не задано.
app.use(webhookCallback(bot, "express"));
2
3
4
5
6
7
import { Application } from "https://deno.land/x/oak/mod.ts";
const app = new Application(); // чи будь-що, що ви використовуєте
// Обовʼязково вкажіть структуру, яку ви використовуєте.
app.use(webhookCallback(bot, "oak"));
2
3
4
5
6
Зауважте, що ви не повинні викликати
bot
при використанні вебхуків..start()
Обовʼязково прочитайте чудовий путівник Марвіна про все
Адаптери для серверних фреймворків
Щоб підтримувати багато різних фреймворків, grammY використовує концепцію адаптерів. Кожен адаптер відповідає за передачу вхідних і вихідних даних із фреймворку до grammY і навпаки. Другий параметр, переданий у webhook
(довідка API) визначає адаптер фреймворку, який використовується для звʼязку з фреймворком.
Через те, як працює цей підхід, нам зазвичай потрібен адаптер для кожного фреймворку, але, оскільки деякі фреймворки мають схожий інтерфейс, є адаптери, які працюють з кількома фреймворками. Нижче наведено таблицю з доступними на даний момент адаптерами, а також фреймворками, API або середовищами виконання, з якими вони працюють.
Адаптер | Фреймворк, API або середовище виконання |
---|---|
aws | AWS Lambda Functions |
aws | AWS Lambda Functions з async |
azure | Azure Functions |
bun | Bun |
cloudflare | Cloudflare Workers |
cloudflare | Cloudflare Module Workers |
express | Express, Google Cloud Functions |
fastify | Fastify |
hono | Hono |
http , https | Модулі Node.js http або https , Vercel |
koa | Koa |
next | Next.js |
nhttp | NHttp |
oak | Oak |
serve | Deno |
std | Deno , std , Deno , Fresh , Ultra , Rutt , Sift |
sveltekit | SvelteKit |
worktop | Worktop |
Відповідь вебхуку
Коли надходить запит вебхуку, ваш бот може викликати один метод у відповіді. Як перевагу, це позбавляє вашого бота від створення одного запиту HTTP на кожне оновлення. Однак у використанні цього механізму є ряд недоліків:
- Ви не зможете обробляти потенційні помилки відповідного виклику API. Це включає помилки обмеження кількості запитів, тому ви не можете бути впевнені, що ваш запит буде виконано.
- Що ще важливіше, ви також не матимете доступу до обʼєкта відповіді. Наприклад, виклик
send
не дасть вам доступу до повідомлення, яке ви надіслали.Message - Крім того, скасувати запит неможливо.
Abort
буде проігноровано.Signal - Зауважте також, що типи в grammY не відображають наслідків виконаного зворотного виклику вебхуку! Наприклад, вони вказують на те, що ви завжди отримуєте обʼєкт відповіді, тому ви несете відповідальність за те, щоб переконатися, що бот не зламалався, використовуючи цю незначну оптимізацію продуктивності.
Якщо ви хочете використовувати відповіді вебхуків, ви можете вказати опцію can
у опції client
вашого Bot
(довідка API). Передайте функцію, яка визначає, чи використовувати відповідь вебхуку для заданого запиту, визначеного методом.
const bot = new Bot("", {
client: {
// Приймаємо недолік відповідей вебхуку для введення статусу.
canUseWebhookReply: (method) => method === "sendChatAction",
},
});
2
3
4
5
6
Ось як відповіді вебхуків працюють під капотом.
______________ _____________
| | | |
| | | |
| | | |
| | *обидва чекаєте* | |
| | | |
| Telegram | | Бот |
| | | |
| | | |
| | --- привіт, нове повідомлення ---> | |
| | <--- добре, sendChatAction --- | |
|____________| |___________|
Своєчасне завершення запитів вебхуків
Ви можете ігнорувати решту цієї сторінки, якщо всі ваші проміжні обробники завершуються швидко, тобто протягом кількох секунд. Цей розділ насамперед призначений для людей, які хочуть виконувати передачу файлів у відповідь на повідомлення або інші операції, які потребують більше часу.
Коли Telegram надсилає оновлення з одного чату вашому боту, він чекатиме, поки ви завершите запит, перш ніж доставити наступне оновлення, яке належить цьому чату. Іншими словами, Telegram послідовно надсилатиме оновлення з одного чату, а оновлення з різних чатів надсилатимуться одночасно. Джерело цієї інформації тут.
Telegram намагається переконатися, що ваш бот отримує всі оновлення. Це означає, що якщо доставка оновлення для чату не вдасться, наступні оновлення будуть поставлені в чергу, доки перше оновлення не буде успішним.
Чому не завершувати запит вебхуку небезпечно
Telegram має тайм-аут для кожного оновлення, яке він надсилає на вебхук вашого бота. Якщо ви не завершите запит вебхуку досить швидко, Telegram повторно надішле оновлення, припускаючи, що воно не було доставлено. Унаслідок цього ваш бот може неочікувано обробити те саме оновлення кілька разів. Це означає, що він виконуватиме всю обробку оновлень, включаючи надсилання будь-яких відповідних повідомлень, кілька разів.
______________ _____________
| | | |
| | --- привіт, нове повідомленнЯ ---> | |
| | . | |
| | *обробка ботом* . | |
| | . | |
| Telegram | --- Я сказав нове повідомлення!!! ---> | Бот |
| | .. | |
| | *обробка ботом двічі* .. | |
| | .. | |
| | --- ПРИИИВВВІІІТ ---> | |
| | ... | |
| | *обробка ботом тричі* ... | |
|____________| ... |___________|
Ось чому grammY має власний коротший типовий тайм-аут всередині webhook
: 10 секунд. Якщо ваш проміжний обробник закінчиться раніше, функція webhook
автоматично відповість на вебхук. У такому випадку все добре. Однак, якщо ваш проміжний обробник не завершить роботу до закінчення часу очікування grammY, webhook
видасть помилку. Це означає, що ви можете обробити помилку в своєму серверному фреймворку. Якщо у вас немає такої обробки помилок, Telegram знову надішле те саме оновлення, але принаймні тепер ви матимете журнали помилок, які повідомлятимуть вам, що щось не так.
Коли Telegram надішле оновлення вашому боту вдруге, малоймовірно, що ви впораєтеся з ним швидше, ніж у перший раз. Тому, ймовірно знову мине тайм-аут, а Telegram знову надішле оновлення. Отже, ваш бот побачить оновлення не просто два рази, а кілька десятків разів, поки Telegram не припинить повторні спроби. Ви можете помітити, що ваш бот починає розсилати користувачам спам, намагаючись обробити всі ці оновлення, які фактично щоразу однакові.
Чому завчасне завершення запиту вебхуку також небезпечно
Ви можете налаштувати webhook
так, щоб не видавати помилку після тайм-ауту, а натомість завчасно завершувати запит вебхуку, навіть якщо ваш проміжний обробник все ще працює. Ви можете зробити це, передавши "return"
як третій аргумент webhook
замість стандартного значення "throw"
. Однак, незважаючи на те, що така поведінка має кілька допустимих випадків використання, таке рішення зазвичай викликає більше проблем, ніж вирішує.
Памʼятайте, що як тільки ви відповісте на запит вебхуку, Telegram надішле наступне оновлення для цього чату. Однак, оскільки старе оновлення все ще обробляється, два оновлення, які раніше оброблялися послідовно, раптово обробляються паралельно. Це може призвести до стану гонитви. Наприклад, плагін сесії неминуче вийде з ладу через конфлікт запису після читання. Це спричиняє втрату даних! Інші плагіни і навіть ваш власний проміжний обробник також можуть зламатися. Ступінь цього невідома та залежить від вашого бота.
Як вирішити цю проблему
Відповісти на це питання легше, ніж це зробити. Це ваша робота — переконатися, що ваші проміжні обробники завершують роботу досить швидко. Не використовуйте довготривалі проміжні обробники. Так, ми знаємо, що ви, можливо, хочете мати довгострокові завдання. Але стійте. Не робіть цього. Не у вашому проміжному обробнику.
Замість цього використовуйте чергу; існує багато систем обробки черг: від дуже простих до дуже складних. Замість того, щоб намагатися виконати всю роботу в невеликому вікні тайм-ауту вебхуку, просто додайте завдання до черги, яке буде оброблятися окремо, і дайте проміжному обробнику завершити роботу. Черга може використовувати стільки часу, скільки потрібно. Коли робота буде завершена, можна надіслати відповідь у чат. Це легко зробити, якщо ви використовуєте просту чергу, яка зберігає стан у памʼяті. Це може бути трохи складніше, якщо ви використовуєте відмовостійку зовнішню систему обробки черг, яка зберігає стан усіх завдань і може повторювати їх, навіть якщо ваш сервер раптово вимикається.
______________ _____________
| | | |
| | --- привіт, нове повідомлення ---> | |
| | <--- дякую, друже ---. | |
| | . | |
| | . | |
| Telegram | *черга боту працює* . | Бот |
| | . | |
| | . | |
| | <--- повідомлення з результатом --- | |
| | --- добре ---> | |
|____________| |___________|
Чому "return"
загалом гірше, ніж "throw"
Можливо, ви дивуєтеся, чому типовою дією webhook
є повідомлення про помилку, а не успішне завершення запиту. Цей вибір дизайну було зроблено з наступних причин.
Стан гонитви дуже важко відтворити, і він може виникати вкрай рідко або епізодично. Вирішення цієї проблеми полягає в тому, щоб в першу чергу переконатися, що ви не стикаєтеся з тайм-аутами. Але, якщо це все-таки трапляється, вам потрібно знати, що це відбувається, щоб ви могли дослідити і виправити проблему! З цієї причини вам потрібно, щоб помилка зʼявлялася у ваших журналах. Встановлення обробника тайм-ауту на "return"
, тобто приховування тайм-ауту та вдавання, ніби нічого не сталося, є протилежністю корисної поведінки.
Якщо ви це зробите, ви в певному сенсі використовуєте чергу Telegram для доставки оновлень через вебхук як чергу завдань. Це погана ідея з усіх причин, описаних вище. Те, що grammY може приховувати помилки, через які ви можете втратити свої дані, не означає, що ви маєте робити це. Цей параметр конфігурації не слід використовувати у випадках, коли виконання ваших проміжних обробників просто займає надто багато часу. Витратьте час, щоб правильно вирішити цю проблему, тоді в майбутньому ви та ваші користувачі будуть вам вдячні.