Hi, I've made a telegram bot which does CPU-heavy things.
I wonder, Can I just use pm2 and scale the bot?
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.
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.
Still if you want to make a separate load balancer, you can use node-http-proxy.
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.