Если вы используете JS приложения которые подразумевают подробный анализ блокчейна голоса в реальном времени - этот пост для вас
Исходный код 📦 GitHub/vikxx/bots
За последний месяц значительно прибавилось новых пользователей у ботов. К ним подключились разнообразные инициативы в том числе топовые держатели "силы голоса", в связи с этим я решил сделать максимальный упор на надежность работы сервисов, чтобы "каждый голос был услышан" :)
Задача и ее решение
Ранее я уже писал о том, что для решения низкой пропускной способности API нод я использую redis:
🚀 [Кеширование API golos]
Блоки в реальном времени собирает и кеширует один единственный скрипт, а все остальные боты и приложения берут данные о последнем блоке из кеша, таким образом удается запустить несколько сотен независимых сессий ботов при этом не создавать никакой нагрузки на ноду голоса. Этот способ решил вопрос масштабируемости нод, однако сам процесс кеширования блоков оказался не идеальным и время от времени в силу разных технических причин случались пропуски. Но еще более назойливой проблемой был рост задержки при длительной постоянной работе и если случался рестарт - терялись все блоки в диапазоне задержки.
Чтобы решить эти две основные проблемы, пропуск блоков и рост задержки, пришлось поломать голову над сменой алгоритма. В итоге получился скрипт: Сноб, который не пропускает блоки даже в случае простоя и не терпит опозданий :)
BlockSnobbery.js - в реальном времени получает блоки и транслирует их в redis кеш.
Практически неограниченное количество скриптов могут подключаться к redis стриму и выполнять свои задачи не создавая нагрузки на ноды голоса.
В скрипт встроен учет блоков для минимизации задержки и исключения пропусков блоков.
Даже в случае простоя и рестарта скрипта после запуска будут отработаны все пропущенные на время простоя блоки в ускоренном режиме.
💽 Установка
Если не установлен - установите Redis: Quickstart
mkdir blocksnobbery
cd blocksnobbery
wget https://raw.githubusercontent.com/vikxx/bots/master/BlockSnobbery/BlockSnobbery.js
npm install redis
npm install golos-js
*
🔌 Запуск
node BlockSnobbery.js
Запуск с параметрами
Запуск и синхронизация с указанного блока
node blocksnobbery.js 1234565
Запуск и синхронизация с самого свежего блока в блокчейн
node blocksnobbery.js now
Запуск и синхронизация с последнего обработанного скриптом блока.
node blocksnobbery.js
Для постоянной работы рекомендуется использовать pm2
npm install pm2 -g
pm2 start BlockSnobbery.js
pm2 stop BlockSnobbery
- Остановка
pm2 restart BlockSnobbery
- Рестарт
pm2 logs BlockSnobbery
- Лог в реальном времени
*npm install steem
для использования со steemit, требуется изменить адрес ноды в файле BlockSnobbery.js
📡 Использованиe
После установки скрипта вы можете получать блоки в любых других приложениях с помощью SUB Redis, в том числе с других серверов или внешними запросами к базе.
Вам нужно только запустить скрипт в фоне. Далее вы можете подключиться к стриму любым удобным образом и получать поток данных с блокчейна.
Пример получения блоков на JS
// При каждом обновлении блока вызывается функция
sub.on("message", (channel, message) => {
// Переменная message всегда содержит актуальные операции из блока!
});
sub.subscribe("golosblocks");
Принципы работы и исходный код
Исходный код 📦 GitHub/vikxx/bots
В основе работы с блокчейном голоса лежит интервал блоков. Самый популярный способ получения актуальных данных - это установка 3-х секундного интервала роста номера блока для запроса его содержимого. Но к сожалению этот очевидный и простой механизм может работать только в неосуществимых идеальных условиях, без сбоев как самой сети голоса, так и вашего "железа" вплоть до тривиальных проблем с интернет каналом. Как следствие - рассинхронизация данных, пробелы, задержка и другие ошибки.
В качестве решения проблемы с задержками и пропусками - я выбрал динамическую смену интервала в зависимости от актуальности и состояния последовательности блоков.
В динамическом интервале используются два API запроса
С помощью первого мы узнаем номер самого недавнего блока на голосе head_block_number
*
С помощью второго мы получаем все операции из блока.
Каждый раз получая операции из блока скрипт сохраняет его номер как последний обработанный блок (lastblock), далее применяя сложную систему таймеров скрипт получает следующий блок. Если последовательность номеров блоков (так называемой "высоты") нарушена задержками в ПО, канале связи или по любым другим причинам - BlockSnobbery будет выстраивать правильную последовательность, чтобы обрабатывать блоки строго один за другим.
Кроме коррекции последовательности, алгоритм заложено так же и коррекция задержки. Если актуальный временной штамп и временной штамп последней операции в блоке больше 3 секунд (или любого указанного значения), включается форсированный режим перебора блоков по 40ms (можно уменьшить на мощном железе)
👨💻 Мониторинг
Запустив скрипт вы можете следить за ходом работы в консоли.
В первой колонке номер обрабатываемого блока
Во второй, в данном состоянии, отображена надпись
[1 Sequence ✔]
- это нормальная последовательность, где 1 означает, что мы обрабатываемый самый свежий блок.Третья колонка
[⌛️ Age of last TX 0.736s]
- это возраст последней транзакции в блоке. Вычислен он по принципу - серверное время минус временной штамп транзакции.Четвертая колонка
[🔴 Interval ~ < 3s ✂️ 68ms]
сообщает о том, какой используется интервал и насколько он оптимизирован. Скрипт запускает таймер на 3 секунды для получения следующего блока, но поскольку до запуска таймера происходит взаимодействие с API - появляется задержка и чтобы соблюдать необходимый интервал в 3 секунды - отрезается рассчитанное время выполнения запросов от следующего таймера.Пятая колонка вида
[📝 Operations streamed: 11]
отображает сколько операций было в блоке и сообщает, что они были успешно отправлены в redis стример для работы с PUB/SUB
Примеры поведения в стрессовых ситуациях
😈 В качесте тестов создавалась имитация разных проблем и неблагоприятных условий
Искусственное условие - "тормознутость" скрипта
Представим, что скрипт тормозит и долго обрабатывает блоки. Так долго, что растет разрыв между последним обработанным блоком и последним блоком в блокчейн. Разрыв устраняется автоматически когда начинает превышать указанный лимит. В примере максимально-допустимая задержка 7 секунд.
Обратите внимание на значение Age of last TX
Когда возраст транзакции превышает допустимый лимит в 6 секунд, во второй колонке отображается режим [2 Overtake 🏃]
, а в четвертой активируется высокая скорость интервала, что позволяет быстро "догонять" номер последнего блока соблюдая последовательность снова возвращаясь на режим, где возраст транзакции меньше 6 секунд.
Искусственное условие - тормознутость ноды или спешка скрипта
Противоположный первому примеру случай.
В логах [🛠 Outrunning] 🚑 Return to sequence 1B
Когда скрипт пытается запросить еще не существующий блок - алгоритм корректирует его данные о последовательности и возвращает на правильную последовательность
Искусственное условие - Рэндомные торомза или спешка
В случае если скрипт бросает из стороны в стороны, последовательность не ломается, задержка не превышает указанный лимит устранение проблем работает.
Искусственное условие - Подсовываем якобы пустые блоки
Включим режим очень плохой ноды и попробуем обмануть скрипт якобы пустым блоком
Пустой блок будет проверен дважды.
На скрине видно, как блок 10109746 пришел на обработку будучи якобы пустым. Включился doublecheck и блок оказался с операциями, которые могли быть пропущены не будь такой проверки.
Искусственное условие - Остановка скрипта, ребут сервера, аварийный рестарт
Предположим у вас что-то поломалось и скрипт остановился.
Или вы захотели внести изменения в код и вам нужно перезапустить скрипт - поскольку запоминается последний обработанный блок, в случае старта после паузы - начало работы начнется с последнего сохраненного блока с ускоренным интервалом пока очередь не дойдет до самого свежего блока
На изображении видно как в укоренном режиме обработались 10 блоков, которые были сгенерированы в блокчейн во время простоя скрипта.
Это может быть удобно для тех, кто будет использовать своих ботов не на сервере - а на обычных пк, которые нет смысла держать включенными 24\7. Вы просто запускаете скрипт когда вам угодно и ждете синхронизации.
Примеры параметров запуска
Запуск и синхронизация с указанного блока
Например
node blocksnobbery.js 10000000
Скрипт начнет работать с блоками начиная с 10000001 в ускоренном режиме
Запуск и синхронизация с самого свежего блока в блокчейн
node blocksnobbery.js now
Запуск и синхронизация с последнего обработанного скриптом блока.
node blocksnobbery.js
Этот вариант подразумевает работу без пропусков блоков, даже если вы останавливаете скрипт на время. Таким образом его можно использовать периодически запуская на пк.
📡 Внешнее подключение
Все тесты проводились на сервере без ноды голоса, а скрипт подключался к блокчейну через мою паблик ноду wss://api.golos.cf то есть были созданы условия обычного пользователя без спец. софта блокчейна.
Кроме того, можно подписываться на стрим Redis с другого сервера или транслировать в браузер.
На изображении пример, где BlockSnobbery.js запущен на сервере 1 (справа)
А на сервере 2 (слева) показан результат подключения к redis сервера 1.
📌 NB*
head_block_number
VS last_irreversible_block
Есть важный нюанс во время работы со "свежайшим" блоком head_block_number
. Да, вы получаете самые актуальные действия, но транзакции в таком блоке еще не прошли раунд подписей делегатских нод и еще могут измениться или вовсе исчезнуть.
Если вы хотите работать только с необратимыми транзакциями - вас следует выбрать last_irreversible_block
. Но возраст последнего необратимого блока - около минуты и если ваше приложение подразумевает моментальную реакцию - это будет неудобно.
Следует выбрать для себя приоритет - моментальная реакция на самую недавнюю операцию в блоке. Или работа только с необратимыми операциями. Например если ваше приложение принимает оплату в токенах блокчейна и вам нужно зачислять оплату - логично использовать вариант с необратимыми блоками. Чтобы минимизировать задержку для клиента - можно комбинировать оба способа, например моментально подтверждать факт перевода, но зачисление совершать только тогда, когда блок станет необратимым.
📚 Читайте также:
golos.cf/reg.html и golos.cf/steem - создание аккаунтов в стим и голос без верификации и ограничений
Установка ноды Golos с public API
Отчет делегата @vik
Обновление списка ботов и сервисов для голоса
Чатбот для рассылки бонусов подписчикам
Деградант и GolosChain
а нельзя просто к ноде подключиться, чтоб она через вебсокеты оповещала о новых блоках? тогда пляски с таймаутами не понадобятся. а пропущенные блоки добывать обычными вызовами api, причём можно сразу пачкой, а не по одному
Было бы удобно. Тем более это и есть принцип и смысл работы вебсокетов. Но нода стима/голоса так не хотят работать.
У блокчейна #bitshares тем не менее есть такая возможность - subscribe на транзакции и другие события в блокчейн, но в блоговом ПО стима и голоса увы это не реализовали
Но можно попробовать
set_block_applied_callback
UP
Все получилось и без таймеров :) Спасибо за пинок в нужную сторону.
Вот пример:
я бы сделал так - общий крон для всех ботов, который получает блок, парсит его, и по критериям дергает нужных ботов.
плюс в том что
Правда если не кривить душой, у меня у самого в коде пока все черз то самое место. И это я так собираюсь только сделать=)))
В некотором роде это именно оно.
Один скрипт создает канал в redis cо стримом блоков.
К нему-то и можно подключить скрипт, который будет дергать того или иного бота в зависимости от операции.
Удобство этого метода еще и в том, что стрим блоков можно сделать публичным и доступным для подключения
Еще это можно подружить с GraphQL
с кэшем да, я про кучу ботов которые делают после одно и тоже)
Но опять же, этов се придется перелопатить, что тоже "непросто"
@vik, Поздравляю!
Ваш пост был упомянут в моем хит-параде в следующей категории:
Я ничего не поняла. "Криптобабушкам" - то, что делать? Что устанавливать, куда подключаться, что вводить? или расслабиться?
Просто второй день все глючит...нет ответов, обновлений ленты и пр.
Ваш пост поддержали следующие Инвесторы Сообщества "Добрый кит":
yefet, litrbooh, t3ran13, strecoza, lyudmila, neo, fetta, andrvik, eduard, trionyx, larissa, dreamer, dimarss, vik, forbon21, shuler, genyakuc, brovaryleaks, vadbars, rusalka, dany2323, nefer, borodaus, semasping, voltash, snaryaga, asuleymanov, bystree, exan, boltyn, elena-cantabile, gapel, on1x, newodin, vika-teplo, borisss, nims55, aiparnyuk, myhardmoney, talia, graff0x, ogion, sareon, kertar, dimas102, lengalenga, lokkie, tannedd, now, igrinov, ssleeperr, zhenek, mirumir, abloud, wedge, tatdt, ksantoprotein, gbot, roman1973, bounty-compaing, vsebudethorosho, sansey, izbushka, mifilin, katherina, alexko
Поэтому я тоже проголосовал за него!
dobryj.kit теперь стал Делегатом! Ваш голос важен для всего сообщества!!!
Поддержите нас на странице https://golos.io/~witnesses, вот так:
Ура! Надеемся на безглючную работу ботов :)
Привет!
Мне очень нравится ваша активность на Голосе , посты интересные и очень актуальные, поэтому я предложением поучаствовать в нашей баунти кампании.
Наш проект Crypto.tickets https://crypto.tickets/index.en.html, возможно, о котором вы уже слышали. Сегодня у нас начало ICO!
За посты и репосты получаете стейки ;);)
О проекте можно узнать тут https://golos.id/@crypto.tickets / @crypto.tickets
Страница баунти https://bitcointalk.org/index.php?topic=2164768.msg21681052#msg21681052
Напишите в ответном письме ваше решение)