Skip to content

Commit 3ba5e76

Browse files
committed
added clsuter module
0 parents  commit 3ba5e76

14 files changed

+1358
-0
lines changed

.eslintrc.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["eslint:recommended", "aenondynamics"]
3+
}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/*
2+
.tmp/*
3+
.hidden/*

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
language: node_js
2+
node_js:
3+
- "8"
4+
- "node"

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
### 1.0.0 ###
2+
Initial Public Release

CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Contribution
2+
------------
3+
4+
http-magic is OpenSource and managed on [GitHub](https://github.com/) - if you like, you're welcome to contribute!
5+
To simplify the release and quality control process, please follow these remarks:
6+
7+
### Notices ###
8+
* Related software packages are updated by the maintainer
9+
* If you form a concept of **larger project changes**, please [discuss]() them with the contributors **before** implementing

LICENSE.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
The MIT License
2+
3+
Copyright (c) 2018 Andi Dittrich
4+
5+
Permission is hereby granted, free of charge, to any person
6+
obtaining a copy of this software and associated documentation
7+
files (the "Software"), to deal in the Software without
8+
restriction, including without limitation the rights to use,
9+
copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the
11+
Software is furnished to do so, subject to the following
12+
conditions:
13+
14+
The above copyright notice and this permission notice shall be
15+
included in all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24+
OTHER DEALINGS IN THE SOFTWARE.

RAEDME.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
http-magic
2+
=========================================
3+
Summary
4+
5+
![Screenshot](assets/screenshot.png)
6+
7+
## Features ##
8+
9+
10+
## Install ##
11+
12+
```bash
13+
$ npm install http-magic --save
14+
$ yarn add http-magic
15+
```
16+
17+
## Usage ##
18+
19+
## Examples ##
20+
21+
## License ##
22+
http-magic is OpenSource and licensed under the Terms of [The MIT License (X11)](http://opensource.org/licenses/MIT) - your're welcome to contribute

http-magic.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const _cluster = require('./lib/cluster');
2+
const _webserver = require('./lib/m');
3+
4+
_cluster.init(_webserver);

lib/cluster.js

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
const _cluster = require('cluster');
2+
const _os = require('os');
3+
const _logger = require('logging-facility').getLogger('cluster');
4+
const _workerShutdownTimeout = 5000;
5+
const _numDefaultWorkers = _os.cpus().length * 2;
6+
7+
function startWorker(){
8+
// create a new child process
9+
// note: this method RELOADS the entire js file/module! - it is NOT a classical process.fork() !
10+
// this allows you to hot-reload your application by restarting the workers
11+
return _cluster.fork();
12+
}
13+
14+
// worker gracefull shutdown
15+
function stopWorker(worker, cb){
16+
// set kill timeout
17+
const killTimeout = setTimeout(() => {
18+
worker.kill();
19+
}, _workerShutdownTimeout);
20+
21+
// trigger disconnect
22+
worker.disconnect();
23+
24+
// wait for exit + disconnect
25+
worker.on('disconnect', () => {
26+
// disable kill timer
27+
clearTimeout(killTimeout);
28+
29+
cb(null);
30+
});
31+
}
32+
33+
// hot-restart the cluster without downtime (restart workers in a row)
34+
function hotReload(cb){
35+
36+
// get currently active workers
37+
const restartList = Object.keys(_cluster.workers);
38+
39+
// counter of processed workers
40+
let numRestartedWorkers = restartList.length;
41+
42+
// restart each worker
43+
restartList.map((workerID) => {
44+
// start new child process
45+
const newWorker = startWorker();
46+
47+
// remove old worker when the new one is online!
48+
newWorker.once('listening', () => {
49+
50+
// get worker by id
51+
const worker = _cluster.workers[workerID];
52+
53+
stopWorker(worker, () => {
54+
// decrement job finish counter
55+
numRestartedWorkers--;
56+
57+
// all workers restarted ?
58+
if (numRestartedWorkers<=0){
59+
cb(null, true);
60+
}
61+
});
62+
})
63+
});
64+
}
65+
66+
// cluster startup
67+
function initializeCluster(){
68+
69+
// flag to indicate hot-reloading is active
70+
let reloadInProgress = false;
71+
72+
// num workers set via env ?
73+
let numWorkers = parseInt(process.env.workers + '');
74+
75+
// limit worker threads
76+
if (isNaN(numWorkers) || numWorkers < 2 || numWorkers > 50){
77+
numWorkers = _numDefaultWorkers;
78+
}
79+
80+
// show num workers
81+
_logger.info(`master process ${process.pid} online`);
82+
_logger.info(`starting ${numWorkers} workers (default=${_numDefaultWorkers})`);
83+
84+
// gracefull cluster shutdown
85+
process.on('SIGTERM', () => {
86+
_logger.info(`graceful shutdown requested by SIGTERM`);
87+
_cluster.disconnect(() => {
88+
_logger.info(`workers disconnected`);
89+
process.exit(0);
90+
});
91+
});
92+
93+
// reload event
94+
process.on('SIGHUP', () => {
95+
_logger.info('hot-reloading requested by SIGHUP');
96+
97+
// reload active ?
98+
if (reloadInProgress === true){
99+
_logger.warn('process reload already initialized');
100+
return;
101+
}
102+
103+
// set reload flag
104+
reloadInProgress = true;
105+
106+
// trigger reload
107+
hotReload(() => {
108+
_logger.info('hot-reloading FINISHED');
109+
reloadInProgress = false;
110+
});
111+
});
112+
113+
// observer workers (all instances)
114+
_cluster.on('exit', (worker, code, signal) => {
115+
// gracefull shutdown/disconnect ?
116+
if (worker.exitedAfterDisconnect === true) {
117+
_logger.info(`worker ${worker.process.pid} disconnnected`);
118+
return;
119+
}
120+
121+
// log unexpected behaviour
122+
_logger.warn(`worker ${worker.process.pid} died - ${code}/${signal} - restarting..`);
123+
124+
// restart worker
125+
startWorker()
126+
});
127+
128+
// observe worker listening events (networking active)
129+
_cluster.on('listening', (worker, address) => {
130+
_logger.info(`worker ${worker.process.pid} is now connected to ${address.address}:${address.port}`);
131+
});
132+
133+
// spawn n workers
134+
for (let i = 0; i<numWorkers ; i++){
135+
startWorker();
136+
}
137+
}
138+
139+
module.exports = {
140+
init: function(application){
141+
// is master process ?
142+
if (_cluster.isMaster){
143+
initializeCluster();
144+
145+
// new child process startup
146+
}else{
147+
application.start();
148+
}
149+
}
150+
}

lib/m.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
console.log("init module");
2+
const net = require('net');
3+
4+
module.exports = {
5+
start: function(){
6+
const server = net.createServer((socket) => {
7+
// connections never end
8+
});
9+
10+
server.listen(8000);
11+
}
12+
}

0 commit comments

Comments
 (0)