Advertisement
Guest User

Untitled

a guest
Apr 8th, 2020
406
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const os = require('os');
  2. const cluster = require('cluster');
  3. const EventEmitter = require('events');
  4. const net = require('net');
  5. const http = require('http');
  6.  
  7. module.exports = class Cluster extends EventEmitter {
  8.     constructor(config = {}) {
  9.         super();
  10.  
  11.         this.config = {
  12.             public_ip: null,
  13.             master_address: 'localhost',
  14.             master_port: 2000,
  15.             name: config.name || 'Cluster',
  16.             nb_workers: config.nb_workers || os.cpus().length,
  17.             ipCheckHost: 'google.com',
  18.             ipCheckPort: 80
  19.         };
  20.  
  21.         this.workers_names = {
  22.             A: 'Alpha', B: 'Bravo', C: 'Charlie', D: 'Delta',
  23.             E: 'Echo', F: 'Foxtrot', G: 'Golf', H: 'Hotel',
  24.             I: 'India', J: 'Juliet', K: 'Kilo', L: 'Lima',
  25.             M: 'Mike', N: 'November', O: 'Oscar', P: 'Papa',
  26.             Q: 'Quebec', R: 'Romeo', S: 'Sierra', T: 'Tango',
  27.             U: 'Uniform', V: 'Victor', W: 'Whiskey', X: 'X-Ray',
  28.             Y: 'Yankee', Z: 'Zulu'
  29.         };
  30.  
  31.         this.clusters_names = {
  32.             A: 'Acosta', B: 'Belknap', C: 'Churchill', D: 'Douglas',
  33.             E: 'Eisenhower', F: 'Foch', G: 'Gration', H: 'Hongzhang'
  34.         };
  35.  
  36.         this.startedAt = new Date();
  37.         this.id = this.getRandomId();
  38.         this.name = this.getRandomName('clusters');
  39.         this.ready = false;
  40.         this.workers = [];
  41.  
  42.         this.on('ping', () => {
  43.             this.emit('pong');
  44.         });
  45.  
  46.         this.on('ready', address => {
  47.             this.notifyMaster('spawning', {
  48.                 address: address,
  49.                 workers: this.getWorkersMetadata(),
  50.                 cluster_id: this.id,
  51.                 type: this.config.name,
  52.                 name: this.name,
  53.                 uptime: this.startedAt
  54.             }).then(() => this.ready = true)
  55.             .catch(error => console.log(error));
  56.         });
  57.     }
  58.  
  59.     getPublicIp() {
  60.         return new Promise((resolve, reject) => {
  61.             let client = net.connect({
  62.                 port: this.config.ipCheckPort,
  63.                 host: this.config.ipCheckHost
  64.             }, () => {
  65.                 this.public_ip = client.localAddress;
  66.                 resolve(client.localAddress);
  67.             });
  68.         });
  69.     }
  70.  
  71.     getRandomId() {
  72.         return Math.random().toString(36).substr(2, 9);
  73.     }
  74.  
  75.     getRandomName(type) {
  76.         let key;
  77.         let count = 0;
  78.  
  79.         for (let item in this[type + '_names'])
  80.             if (Math.random() < 1 / ++count)
  81.                 key = item;
  82.  
  83.         let name = this[type + '_names'][key];
  84.         delete this[type + '_names'][key];
  85.  
  86.         return name;
  87.     };
  88.  
  89.     notifyMaster(event, payload) {
  90.         return new Promise((resolve, reject) => {
  91.             let data = JSON.stringify(payload);
  92.  
  93.             let request = http.request({
  94.                 hostname: this.config.master_address,
  95.                 port: this.config.master_port,
  96.                 path: '/' + event,
  97.                 method: 'POST',
  98.                 headers: {
  99.                     'Content-Type': 'application/json'
  100.                 }
  101.             }, res => {
  102.                 res.on('error', error => reject(error));
  103.  
  104.                 res.on('data', chunk => {
  105.                     console.log('Notification response:', chunk.toString());
  106.                 });
  107.  
  108.                 res.on('end', () => {
  109.                     console.log('No more data in response.');
  110.                     resolve();
  111.                 });
  112.             });
  113.  
  114.             request.on('error', error =>  console.error(error));
  115.             request.write(data);
  116.             request.end();
  117.         })
  118.     }
  119.  
  120.     getWorkersMetadata() {
  121.         return this.workers.map(worker => {
  122.             return {
  123.                 name: worker.name,
  124.                 pid: worker.process.pid,
  125.                 id: worker.id,
  126.                 job_count: worker.job_count
  127.             }
  128.         });
  129.     }
  130.  
  131.     start() {
  132.         return new Promise((resolve, reject) => {
  133.             if (cluster.isMaster) {
  134.                 this.getPublicIp().then(address => {
  135.  
  136.                     for (let i = 0; i < this.config.nb_workers; ++i) {
  137.                         let worker = cluster.fork();
  138.                         worker.name = this.getRandomName('workers');
  139.  
  140.                         worker.on('online', () => {
  141.                             console.log(worker.job_count)
  142.                             if (i == this.config.nb_workers - 1)
  143.                                 this.emit('ready', address);
  144.  
  145.                             console.log(`[${this.config.name}#${this.name}][${worker.name}#${worker.process.pid}] worker spawned`);
  146.                         });
  147.  
  148.                         this.workers.push(worker);
  149.                     }
  150.  
  151.                     process.stdin.resume();
  152.                     process.on('exit', () => this.exit);
  153.                     process.on('SIGINT', () => {
  154.                         this.exit();
  155.                     });
  156.                     // process.on('SIGUSR1', () => this.exit);
  157.                     // process.on('SIGUSR2', () => this.exit);
  158.                     process.on('uncaughtException', () => this.exit);
  159.                 });
  160.             } else {
  161.                 resolve();
  162.             }
  163.         })
  164.     }
  165.  
  166.     exit() {
  167.         console.log('closed');
  168.  
  169.         this.notifyMaster('closed', {
  170.             cluster_id: this.id,
  171.             type: this.config.name,
  172.             name: this.name
  173.         })
  174.         .catch(error => console.log(error));
  175.  
  176.         setTimeout(() => {
  177.             process.exit()
  178.         }, 1000);
  179.     }
  180. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement