Node-telegram-bot-api: Is it scalable?

Created on 31 Oct 2016  路  10Comments  路  Source: yagop/node-telegram-bot-api

Hi, I've made a telegram bot which does CPU-heavy things.
I wonder, Can I just use pm2 and scale the bot?

docs question

Most helpful comment

@ohenepee You can't make all bots to listen on the same port but you can make load balancer to listen on port 8443 and use proxy to redirect requests so you can start your bots on any port you want as long as your load balancer is on the right port.

However it's usually overkill and only rational if you want to have the same bot on different servers. If that isn't your case then just use nodeJs cluster (Too many requests error can be safely ignored):

Also note that nodeJs clusters are just seperate processes and do not share memory.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  const token = 'Your Token';
  const options = {
    webHook: {
      port: 8443,
      key: `${__dirname}/PRIVATE.key`,  // Path to file with PEM private key
      cert: `${__dirname}/PUBLIC.pem`  // Path to file with PEM certificate
    }
  };

  const bot = new TelegramBot(token, options);
  bot.setWebHook(`https://${YOUR_SERVER_IP}:8443/bot${token}`, {
    certificate: options.webHook.cert,
  });

  console.log(`Worker ${process.pid} started`);
}

Still if you want to make a separate load balancer, you can use node-http-proxy.

const https     = require('https');
const httpProxy = require('http-proxy');
const fs        = require('fs');
const _         = require('lodash');

const proxy = httpProxy.createServer();
const certification = {
  key: fs.readFileSync(KEYPATH),
  cert: fs.readFileSync(CERTPATH)
};

const servers = ['127.0.0.1:3000', '127.0.0.2:3001'];

function pickOneServer() {
  return _.sample(servers);
}

https.createServer(certification, (req, res) => {
  // check req.url just to be sure that it is a bot request
  const chosenServer = pickOneServer(botKey);
  proxy.web(req, res, {
    target: `https://${chosenServer}`,
    secure: false
  });
}).listen(8443);

This is a very simple load balancer. Note that I did not change https to http because node-telegram-bot-api still expects secure connection even though it does not run on the ports such as 443 or 8443 anymore.

Also you need to do some changes in your bot settings.

  const options = {
    webHook: {
      port: 3000, // or any other port you want
      key: `${__dirname}/PRIVATE.key`,  // Path to file with PEM private key
      cert: `${__dirname}/PUBLIC.pem`  // Path to file with PEM certificate
    }
  };

  const loadbalancerAddress = 'LOAD_BALANCER_IP:8443';
  const bot = new TelegramBot(token, options);
  bot.setWebHook(`https://${loadbalancerAddress}/bot${token}`, {
    certificate: options.webHook.cert,
  });

All 10 comments

If you are using polling, it is not scalable at all! This is because polling uses offsets to retrieve new updates. If your application is scaled to multiple instances, each of these instance will use its own and separate offset. Therefore, there exists the chance that different application instances will process the same update.

However, when using webhooks it is scalable. How? You would bot.setWebhook(url), where url points to a load balancer, such as Nginx, that'd re-route the request to an application instance, depending on set strategy.

@GochoMugo Thanks but I'm a little confused. I don't want some kind of complex load balancer, I just want a simple nodeJs middle-ware to reroute requests randomly.

I don't know how to do it or where to start, Can you give me some tips or some kind of library which would make things easier?

IMHO, it is better to use an already-established load balancer, rather than re-invent the wheel.

However, you could use node-http-proxy to proxy requests to different application instances. PM2 could help you handle clustering.

I tried to use Nginx but I gave up after a few hours of trying to implement it. So I personally suggest node-http-proxy which is more flexible and familiar.

@omidh28 If you managed to implement your said architecture, could you share with the structural code? i want to run multiple bots with node-http-proxy via the webhook API, but I don't know where to start? As I don't understand how to get the bots listening on the same port (8443 [non-root])

Re-open to ensure this is documented in our help information.

@ohenepee You can't make all bots to listen on the same port but you can make load balancer to listen on port 8443 and use proxy to redirect requests so you can start your bots on any port you want as long as your load balancer is on the right port.

However it's usually overkill and only rational if you want to have the same bot on different servers. If that isn't your case then just use nodeJs cluster (Too many requests error can be safely ignored):

Also note that nodeJs clusters are just seperate processes and do not share memory.

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  console.log(`Master ${process.pid} is running`);

  // Fork workers.
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`worker ${worker.process.pid} died`);
  });
} else {
  const token = 'Your Token';
  const options = {
    webHook: {
      port: 8443,
      key: `${__dirname}/PRIVATE.key`,  // Path to file with PEM private key
      cert: `${__dirname}/PUBLIC.pem`  // Path to file with PEM certificate
    }
  };

  const bot = new TelegramBot(token, options);
  bot.setWebHook(`https://${YOUR_SERVER_IP}:8443/bot${token}`, {
    certificate: options.webHook.cert,
  });

  console.log(`Worker ${process.pid} started`);
}

Still if you want to make a separate load balancer, you can use node-http-proxy.

const https     = require('https');
const httpProxy = require('http-proxy');
const fs        = require('fs');
const _         = require('lodash');

const proxy = httpProxy.createServer();
const certification = {
  key: fs.readFileSync(KEYPATH),
  cert: fs.readFileSync(CERTPATH)
};

const servers = ['127.0.0.1:3000', '127.0.0.2:3001'];

function pickOneServer() {
  return _.sample(servers);
}

https.createServer(certification, (req, res) => {
  // check req.url just to be sure that it is a bot request
  const chosenServer = pickOneServer(botKey);
  proxy.web(req, res, {
    target: `https://${chosenServer}`,
    secure: false
  });
}).listen(8443);

This is a very simple load balancer. Note that I did not change https to http because node-telegram-bot-api still expects secure connection even though it does not run on the ports such as 443 or 8443 anymore.

Also you need to do some changes in your bot settings.

  const options = {
    webHook: {
      port: 3000, // or any other port you want
      key: `${__dirname}/PRIVATE.key`,  // Path to file with PEM private key
      cert: `${__dirname}/PUBLIC.pem`  // Path to file with PEM certificate
    }
  };

  const loadbalancerAddress = 'LOAD_BALANCER_IP:8443';
  const bot = new TelegramBot(token, options);
  bot.setWebHook(`https://${loadbalancerAddress}/bot${token}`, {
    certificate: options.webHook.cert,
  });

It's helpful thank you.

You can also using pm2 to simply handle your clustering, then you only need to set webhook for your telegram bot like the one @omidh28 does.

 const bot = new TelegramBot(token);
 bot.setWebHook(`https://${loadbalancerAddress}/bot${token}`, {
    certificate: options.webHook.cert,
  });

Very helpful issue. Thanks

Pls share if have working code for that, i am totally confused with nodeJS and multiple bot to listen for single class.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Kasra-S picture Kasra-S  路  3Comments

lenny76 picture lenny76  路  3Comments

Niquolas picture Niquolas  路  4Comments

mbrammer picture mbrammer  路  3Comments

Panthro picture Panthro  路  3Comments