-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathslides_node_cluster.html
More file actions
318 lines (300 loc) · 13.2 KB
/
slides_node_cluster.html
File metadata and controls
318 lines (300 loc) · 13.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slides for
Scaling Node — Ruby on Rails Guides
</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" data-turbo-track="reload">
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print">
<link rel="stylesheet" type="text/css" href="stylesheets/highlight.css" data-turbo-track="reload">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="stylesheets/reset.css">
<link rel="stylesheet" href="stylesheets/reveal.css">
<link rel="stylesheet" href="stylesheets/myslide.css" id="theme">
<link rel="stylesheet" href="stylesheets/code.css">
<script src="javascripts/clipboard.js" data-turbo-track="reload"></script>
<script src="javascripts/slides.js" data-turbo-track="reload"></script>
</head>
<body>
<div class="reveal">
<!-- Any section element inside of this container is displayed as a slide -->
<div class="slides">
<section>
<h1>Scaling Node</h1><p>After working through this guide you should be able to</p>
<ul>
<li>set up cluster.js to run several node instances</li>
</ul>
<p><small>Slides - use arrow keys to navigate, esc to return to page view, f for fullscreen</small></p>
</section>
<section><a class='slide_break' href='node_cluster.html#slide-0'>▻</a>
<h2 id="node-and-the-command-line"><a class="anchorlink" href="#node-and-the-command-line"><span>1</span> Node and the Command Line</a></h2></section>
<section><a class='slide_break' href='node_cluster.html#slide-1'>▻</a>
<h2 id="arguments"><a class="anchorlink" href="#arguments"><span>2</span> Arguments</a></h2><p>The array <code>process.argv</code> contains information about how the program
was started:</p><div class="interstitial code">
<pre><code class="highlight plaintext">node fs.js file.txt
</code></pre>
<button class="clipboard-button" data-clipboard-text="node fs.js file.txt
">Copy</button>
</div>
<p>The contents of the array might be:</p><div class="interstitial code">
<pre><code class="highlight plaintext">['/Users/b/.nvm/versions/node/v5.9.0/bin/node', '/Users/b/code/fs.js', 'file.txt' ]
</code></pre>
<button class="clipboard-button" data-clipboard-text="['/Users/b/.nvm/versions/node/v5.9.0/bin/node', '/Users/b/code/fs.js', 'file.txt' ]
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_cluster.html#slide-2'>▻</a>
<h2 id="standard-input-and-standard-output-channels"><a class="anchorlink" href="#standard-input-and-standard-output-channels"><span>3</span> Standard Input and Standard Output channels</a></h2><p>are streams in node:</p><div class="interstitial code">
<pre><code class="highlight javascript"><span class="nx">process</span><span class="p">.</span><span class="nx">stdout</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hello World</span><span class="se">\n</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">process</span><span class="p">.</span><span class="nx">stderr</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="dl">"</span><span class="s2">you're doing it wrong</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">this</span><span class="dl">"</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="nx">that</span><span class="p">);</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="process.stdout.write("Hello World\n");
process.stderr.write("you're doing it wrong");
console.log("this", true, 42, that);
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight shell"><span class="nb">node </span>app.js <span class="o">></span> out.txt 2> err.txt
</code></pre>
<button class="clipboard-button" data-clipboard-text="node app.js > out.txt 2> err.txt
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_cluster.html#slide-3'>▻</a>
<h2 id="environment-variables"><a class="anchorlink" href="#environment-variables"><span>4</span> environment variables</a></h2></section>
<section><a class='slide_break' href='node_cluster.html#slide-4'>▻</a>
<h2 id="process-id"><a class="anchorlink" href="#process-id"><span>5</span> process id</a></h2><div class="interstitial code">
<pre><code class="highlight javascript"><span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="s2">`process </span><span class="p">${</span><span class="nx">process</span><span class="p">.</span><span class="nx">pid</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="console.log(`process ${process.pid}`)
">Copy</button>
</div>
<p>Or find it on the commandline:</p><div class="interstitial code">
<pre><code class="highlight shell"><span class="nv">$ </span>ps
PID TTY TIME CMD
71422 ttys000 0:00.05 bash
20877 ttys002 0:00.11 <span class="nb">node </span>app.js
</code></pre>
<button class="clipboard-button" data-clipboard-text="$ ps
PID TTY TIME CMD
71422 ttys000 0:00.05 bash
20877 ttys002 0:00.11 node app.js
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_cluster.html#slide-5'>▻</a>
<h2 id="sending-and-reacting-to-signals"><a class="anchorlink" href="#sending-and-reacting-to-signals"><span>6</span> Sending and reacting to signals</a></h2><div class="interstitial code">
<pre><code class="highlight javascript"><span class="nx">process</span><span class="p">.</span><span class="nf">on</span><span class="p">(</span> <span class="dl">"</span><span class="s2">SIGINT</span><span class="dl">"</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="s2">`stopping`</span><span class="p">);</span>
<span class="nx">process</span><span class="p">.</span><span class="nf">exit</span><span class="p">();</span>
<span class="p">});</span>
</code></pre>
<button class="clipboard-button" data-clipboard-text="process.on( "SIGINT", function() {
console.log(`stopping`);
process.exit();
});
">Copy</button>
</div>
<p>Send SIGINT by pressing CTRL-C
Send SIGINT with <code>kill 20877</code></p><p>SIGTERM is used by docker, always handle it!</p><div class="interstitial code">
<pre><code class="highlight plaintext">process.on( "SIGTERM", function() {
console.log(`stopping`);
process.exit();
});
</code></pre>
<button class="clipboard-button" data-clipboard-text="process.on( "SIGTERM", function() {
console.log(`stopping`);
process.exit();
});
">Copy</button>
</div>
<p>SIGHUB can be used for less serious signals, for example
to get the app to dump some statistics, or reload a configuration file:</p><div class="interstitial code">
<pre><code class="highlight plaintext">let count = 1;
process.on( "SIGHUP", function() {
console.log(`count = ${count}`);
});
</code></pre>
<button class="clipboard-button" data-clipboard-text="let count = 1;
process.on( "SIGHUP", function() {
console.log(`count = ${count}`);
});
">Copy</button>
</div>
<div class="interstitial code">
<pre><code class="highlight plaintext">kill –HUP 20877
</code></pre>
<button class="clipboard-button" data-clipboard-text="kill –HUP 20877
">Copy</button>
</div>
</section>
<section><a class='slide_break' href='node_cluster.html#slide-6'>▻</a>
<h2 id="scaling-node-with-cluster-js"><a class="anchorlink" href="#scaling-node-with-cluster-js"><span>7</span> Scaling Node with cluster.js</a></h2><p>V8 has a default memory limit of ~1.5GB
If your server has more memory
this might be unsatisfactory!
You can increase this by starting node with the option
<code>--max-old-space-size</code>.</p><p>The Eventloop uses 1 core
If your server has 64 cores
this might be unsatisfactory!
To get around this limitation use cluster.js</p><div class="interstitial code">
<pre><code class="highlight plaintext">const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
console.log('working!');
}
</code></pre>
<button class="clipboard-button" data-clipboard-text="const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
console.log('working!');
}
">Copy</button>
</div>
<p>sockets are shared between slave processes:</p><div class="interstitial code">
<pre><code class="highlight plaintext">if (cluster.isMaster) {
// ...
} else {
// Workers can share any
// TCP connections
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
}
</code></pre>
<button class="clipboard-button" data-clipboard-text="if (cluster.isMaster) {
// ...
} else {
// Workers can share any
// TCP connections
http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
}
">Copy</button>
</div>
<p>the master process is in charge:</p><div class="interstitial code">
<pre><code class="highlight plaintext">if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`${worker.process.pid} died`);
});
}
</code></pre>
<button class="clipboard-button" data-clipboard-text="if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`${worker.process.pid} died`);
});
}
">Copy</button>
</div>
<p>they do not share the same memory space
= objects are local to each instance of the app.
You cannot maintain state in the application code!
You can send messages:</p><div class="interstitial code">
<pre><code class="highlight plaintext">const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
let worker = cluster.fork();
worker.send('do something!');
}
} else {
process.on('message', (msg) => {
process.send('nope.');
});
}
</code></pre>
<button class="clipboard-button" data-clipboard-text="const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
let worker = cluster.fork();
worker.send('do something!');
}
} else {
process.on('message', (msg) => {
process.send('nope.');
});
}
">Copy</button>
</div>
<p>Where do we keep the state of the app? The answers
are the same as for PHP, Rails, etc:</p>
<ul>
<li>in a Database (full featured)</li>
<li>in a key value store like Memcached, Redis (faster, minimal features)</li>
<li>by sending messages</li>
</ul>
</section>
<section><a class='slide_break' href='node_cluster.html#slide-7'>▻</a>
<h2 id="see-also"><a class="anchorlink" href="#see-also"><span>8</span> See Also</a></h2>
<ul>
<li><a href="https://nodejs.org/api/cluster.html">cluster.js documentation</a></li>
<li><a href="https://pm2.io/doc/en/runtime/overview/">pm2 - process manager for Node.js</a></li>
</ul>
</div></section>
</div>
</div>
<!-- End slides. -->
<!-- Required JS files. -->
<script src="javascripts/reveal.js"></script>
<script src="javascripts/search.js"></script>
<script src="javascripts/markdown.js"></script>
<script>
// Also available as an ES module, see:
// https://revealjs.com/initialization/
Reveal.initialize({
controls: false,
progress: true,
center: false,
hash: true,
// The "normal" size of the presentation, aspect ratio will
// be preserved when the presentation is scaled to fit different
// resolutions. Can be specified using percentage units.
width: 1000,
height: 600,
disableLayout: false,
// Factor of the display size that should remain empty around
// the content
margin: 0.05,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 10.0,
keyboard: {
27: () => {
// do something custom when ESC is pressed
var new_url = window.location.pathname.replace('slides_', '') + window.location.hash.replace('/','slide-');
window.location = new_url;
},
191: 'toggleHelp',
13: 'next', // go to the next slide when the ENTER key is pressed
},
// Learn about plugins: https://revealjs.com/plugins/
plugins: [ RevealSearch, RevealMarkdown ]
});
</script>
</body>
</html>