Масштабирование III: Надежность
Если вы убедились, что у вас есть правильная обработка ошибок для вашего бота, то вы готовы к работе. Все ошибки, которые должны произойти (неудачные вызовы API, неудачные сетевые запросы, неудачные запросы к базе данных, неудачные middleware и т.д) — будут обнаружены.
Вы должны убедиться, что всегда ожидаете все объекты Promise
, или, по крайней мере, вызываете catch
для них. Используйте правило линтинга, чтобы убедиться, что вы не сможете забыть об этом.
Правильное выключение
Для ботов, использующих long polling, есть еще один момент, который следует учитывать. Поскольку в какой-то момент вы снова собираетесь остановить своего бота во время работы, вам следует подумать о том, чтобы перехватить события SIGTERM
и SIGINT
и вызвать bot
(встроенный long polling) или остановить своего бота через его обработчик (grammY runner):
Простой Long Polling
import { Bot } from "grammy";
const bot = new Bot("");
// Остановка бота при завершении процесса Node.js
// вот-вот будет завершён
process.once("SIGINT", () => bot.stop());
process.once("SIGTERM", () => bot.stop());
await bot.start();
2
3
4
5
6
7
8
9
10
const { Bot } = require("grammy");
const bot = new Bot("");
// Остановка бота при завершении процесса Node.js
// вот-вот будет завершён
process.once("SIGINT", () => bot.stop());
process.once("SIGTERM", () => bot.stop());
await bot.start();
2
3
4
5
6
7
8
9
10
import { Bot } from "https://deno.land/x/grammy@v1.30.0/mod.ts";
const bot = new Bot("");
// Остановка бота при завершении процесса Deno
// вот-вот будет завершён
Deno.addSignalListener("SIGINT", () => bot.stop());
Deno.addSignalListener("SIGTERM", () => bot.stop());
await bot.start();
2
3
4
5
6
7
8
9
10
Использование grammY runner
import { Bot } from "grammy";
import { run } from "@grammyjs/runner";
const bot = new Bot("");
const runner = run(bot);
// Остановка бота при завершении процесса Node.js
// вот-вот будет завершён
const stopRunner = () => runner.isRunning() && runner.stop();
process.once("SIGINT", stopRunner);
process.once("SIGTERM", stopRunner);
2
3
4
5
6
7
8
9
10
11
12
const { Bot } = require("grammy");
const { run } = require("@grammyjs/runner");
const bot = new Bot("");
const runner = run(bot);
// Остановка бота при завершении процесса Node.js
// вот-вот будет завершён
const stopRunner = () => runner.isRunning() && runner.stop();
process.once("SIGINT", stopRunner);
process.once("SIGTERM", stopRunner);
2
3
4
5
6
7
8
9
10
11
12
import { Bot } from "https://deno.land/x/grammy@v1.30.0/mod.ts";
import { run } from "https://deno.land/x/grammy_runner@v2.0.3/mod.ts";
const bot = new Bot("");
const runner = run(bot);
// Остановка бота при завершении процесса Deno
// вот-вот будет завершён
const stopRunner = () => runner.isRunning() && runner.stop();
Deno.addSignalListener("SIGINT", stopRunner);
Deno.addSignalListener("SIGTERM", stopRunner);
2
3
4
5
6
7
8
9
10
11
12
Это практически все, что нужно для надежности, и теперь ваш бот никогда не будет падать.
Гарантии надежности
Что делать, если ваш бот обрабатывает финансовые операции и вам необходимо рассмотреть сценарий kill
когда процессор физически ломается или в центре обработки данных отключается электричество? Если по какой-то причине кто-то или что-то действительно жестко пресекает этот процесс, все становится немного сложнее.
По сути, боты не могут гарантировать точно однократное выполнение вашего middleware. Прочтите это обсуждение на GitHub, чтобы узнать больше о том, почему ваш бот может отправлять дублирующие сообщения (или вообще не отправлять их) в крайне редких случаях. В оставшейся части этого раздела мы подробно расскажем о том, как grammY ведет себя в этих необычных обстоятельствах, и о том, как действовать в таких ситуациях.
Вы просто хотите создать бота для Telegram? Пропустите остальную часть этой страницы
.
Вебхук
Если вы используете вебхуки, сервер Bot API будет повторно доставлять обновления вашему боту, если он вовремя не ответит OK
. Это практически полностью определяет поведение системы — если вам нужно предотвратить обработку дубликатов обновлений, вы должны создать свой собственный де-дубликатор, основанный на update
. grammY не делает этого за вас, но не стесняйтесь создавать PR, если считаете, что кто-то еще может извлечь из этого пользу.
Long Polling
Long Polling
или же Длительный опрос
более интересен. Long Polling, по сути, повторно запускает последнюю партию обновлений, которая была получена, но не смогла завершиться.
Обратите внимание, что если вы правильно остановите своего бота с помощью
bot
, то смещение обновления будет синхронизировано с серверами Telegram путем вызова.stop get
с правильным смещением, но без обработки данных обновления.Updates
Другими словами, вы никогда не потеряете ни одного обновления, однако может случиться так, что вы повторно обработаете до 100 обновлений, которые вы видели ранее. Поскольку вызовы send
не являются идемпотентными, пользователи могут получать дубликаты сообщений от вашего бота. Однако по крайней мере один раз обработка гарантирована.
grammY Runner
Если вы используете grammY runner в параллельном режиме следующий вызов get
потенциально может быть выполнен до того, как ваш middleware обработает первое обновление текущей партии. Таким образом, смещение обновления будет подтверждено преждевременно. Это плата за высокую параллельность, и, к сожалению, её невозможно избежать без снижения пропускной способности и скорости отклика. В результате, если ваш экземпляр будет выключен в нужный (неправильный) момент, может случиться так, что до 100 обновлений не смогут быть получены снова, потому что Telegram считает их подтвержденными. Это приводит к потере данных.
Если очень важно предотвратить это, используйте источники и поглотители библиотеки grammY runner для создания собственного конвейера обновлений, который сначала пропускает все обновления через очередь сообщений.
- По сути, вам придется создать поглотитель, который будет толкать в очередь, и запускать одного runner, который будет обслуживать только вашу очередь сообщений.
- Затем нужно создать источник, который снова будет тянуть из очереди сообщений. В результате вы запустите два разных экземпляра grammY runner.
Этот расплывчатый проект, описанный выше, был лишь наброском, но не реализован, насколько нам известно. Пожалуйста, свяжитесь с группой Telegram, если у вас есть какие-либо вопросы или если вы попробуете это сделать и сможете поделиться своими успехами.
С другой стороны, если ваш бот сильно загружен и запрос обновлений замедляется из-за автоматического ограничения нагрузки, возрастают шансы, что некоторые обновления будут получены повторно, что снова приведет к дублированию сообщений. Таким образом, цена полной параллельности заключается в том, что обработка не может быть гарантирована ни по крайней мере один раз, ни более одного раза