🚀 [Кеширование API golos] Запуск множества многопоточных живых скриптов на одной ноде - решение для маштабируемости ботов голоса

Суть решаемой проблемы:


Решение:

В конце поста будет приведен простой код для node.js который позволит оптимизировать работу множества скриптов на одной ноде.


Активным пользователям моих ботов:
Просьба протестировать нового бота https://t.me/autoupvotebot - если ошибок найдено не будет, на эту версию будут переведены все боты , что обеспечит стабильную и комфортную для вас работу.

Update:
Уже обновлены:
https://t.me/golosrobot
https://t.me/hottabot

https://t.me/mapvotebot (бот в числе прочего голосующий за посты размещенные с клиента мапалы)

Для обновления введите в ботах команду /menu - вы увидите переименованную кнопку "Запуск" - нажимайте ее, это возобновит работу.

Некоторые боты будут переведены на новую версию уже сейчас.


В последнее время моими телеграм ботами для голосования пользуются все больше людей и естественно нагрузка возрасла в 10-ки раз по сравнению даже с прошлым месяцем. Я поднял дополнительные ноды, распределил нагрузки, добавил мощности серверам, но узкое место оказалось в самой пропускной способности нод.

Обратите внимание на скрин, там изредка мелькают строки белого цвета. Это череда новых блоков. Желтые же строки - это обращение к ноде.

Сейчас на сервере работают примерно 10 ботов, в каждом примерно 20 пользователей, это 200 практически одновременных запросов каждые 3 секунды 24/7. Такую нагрузку нода очень часто не выдерживает и боты пропускают транзакции или становятся на вынужденную паузу, а пользователи сердятся на несовершенство скриптов :)

Так как каждый пользователь бота "опрашивает" новые блоки с интервалом в 3 секунды, то сделать работу комфортной можно либо устанавливая по ноде на каждых 20-30 человек, что экономически абсурдно, либо пересмотреть принципы работы с опрашиванием блоков.

Решение я выбрал такое:

Вместо сотен отдельных сессий пользователей работает только один ОТДЕЛЬНЫЙ скрипт который опрашивает блоки. И сохраняет данные блока в базу данных Redis, все это происходит за доли миллисикунд, так как redis при правильной настройке не просто база, а мощный кеширующий инструмент.
Далее, каждый из пользователей бота обращается не к ноде, а уже к redis, что бы получить данные блока с операциями блокчейна. Таким образом, нагрузка должна упасть в 10-ки, а то и больше раз.

Разработчикам и владельцам нод

Суть метода простая, на сервере работает javascript который с интервалом в 3 секунды обновляет в базе данных содержимое свежего блока на golos.io
Далее вместо того, что бы подключаться всеми своими скриптами одновременно к одной ноде, вы берете данные о блоках из базы данных, а нода работает с минимальной нагрузкой, словно ее использует лишь один скрипт.

Вы можете использовать любую базу данных, как-то mongo, mysql, rethink и другие, для себя же я выбрал именно redis, для меня она проверенный инструмент способный держать нагрузку во время кэширования большого объема данных.

Весь код для постоянного сохранения текущего блока в redis уместился в несколько десятков строк

require('events').EventEmitter.prototype._maxListeners = 100000
const Promise = require("bluebird")
const _ = require('lodash')
const golos = require("golos-js")
const redis = require("redis")
const client = redis.createClient()
golos.config.set('websocket','ws://localhost:9090')
let trig = {existBlock:true}
const dynamicSnap = new Promise((resolve, reject) => {
    golos.api.getDynamicGlobalProperties((err, res) => {
        if (err) {console.log(err)}
        else {
            resolve(res)
        }
    })
})
const FIRSTBLOCK = n => n.head_block_number
const SENDBLOCK = currentblock => {
    golos.api.getBlock(currentblock, (err, result) => {
        if (err) {
            console.log(err) 
            }   else if (result === null){
                trig.existBlock = false
                }else {
        let JSONblock = JSON.stringify(result)
        client.hmset("GolosLastBlock", "data",JSONblock);
        console.log(JSONblock)
        trig.existBlock = true
            }
    })
}
const NEXTBLOCKS = firstblock => {
    let currentblock = firstblock
    setInterval(() => {
if(trig.existBlock){
currentblock++
}else{
console.warn(`Проблема блока ${currentblock}`)}
SENDBLOCK(currentblock)
}, 3000)
}
dynamicSnap
    .then(FIRSTBLOCK)
    .then(NEXTBLOCKS)
    .catch(e => console.log(e));
    
    

Далее вам достаточно брать данные блока из базы redis

client.hgetall("GolosLastBlock", function(err, LB) {
let LastBlockRedis = JSON.parse(LB.data)
            filterOperations(LastBlockRedis)
}

Где предполагается, что функция filterOperations обрабатывается вашим скриптом.


голосботыapitelegram
306
1765.122 GOLOS
0
В избранное
VIK
Обратная связь в телеграм чате @chain_cf
306
0

Зарегистрируйтесь, чтобы проголосовать за пост или написать комментарий

Авторы получают вознаграждение, когда пользователи голосуют за их посты. Голосующие читатели также получают вознаграждение за свои голоса.

Зарегистрироваться
Комментарии (15)
Сортировать по:
Сначала старые