Skip to content

Commit 3dfc332

Browse files
authored
Merge pull request #3 from crussell52/next
Merge v0.2.0 code to master
2 parents 15da7b9 + fbbd227 commit 3dfc332

File tree

12 files changed

+1059
-401
lines changed

12 files changed

+1059
-401
lines changed

README.md

Lines changed: 103 additions & 280 deletions
Large diffs are not rendered by default.

docs/ADVANCED.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Advanced Usage
2+
3+
For the most obvious use cases, you probably don't need this stuff. But these are the things you might find useful
4+
when things get... _interesting_.
5+
6+
## Custom Encoding and Decoding
7+
8+
## Talking to Processes Outside of Node
9+
10+
## Throttling Messages

docs/API.md

Lines changed: 369 additions & 0 deletions
Large diffs are not rendered by default.

docs/USAGE.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Usage
2+
3+
General knowledge for the simplest use cases.
4+
5+
## A Note on Safety
6+
7+
Unix socket files exist on the file system. This library does not provide any special handling of their
8+
creation; it leaves that up to the expert: the [NodeJs net module](https://nodejs.org/api/net.html). In fact,
9+
that page has a section dedicated to Node's [IPC support](https://nodejs.org/api/net.html#net_ipc_support)
10+
that you should probably read, if you are not already famliiar with it.
11+
12+
Because they are files, they are subject to permissions. Make sure you understand how those permissions work
13+
for sockets on your target OS. Use appropriate caution to not expose your application's messages to unintended
14+
audiences **or expose your application to messages from unintended clients!**.
15+
16+
Socket files are very commonly used. You could use this library to tap into any socket file that your process has
17+
access to! That could be _very_ interesting... but it could also be hazardous. In particular, the `Server` will
18+
try to use _any_ socket file you tell it to -- even if that socket file is normally used by another service. Now, it
19+
can't "hijack" a socket file that another server is actively using, but if you occupy it, the other service may fail
20+
to start or its client's may think you are their server and start sending unexpected data!
21+
22+
The details of how socket files work and the traps that might lurk in the shadows are **far** beyond the scope of this
23+
module's documentation. Like any good module, `socket-ipc` tries to hide this complexity from you and get you up
24+
and running fast. But if this is your first time stepping into this territory, it might still be worth the effort to
25+
learn a bit about them.
26+
27+
## Automatic Retry
28+
29+
## Automatic Reconnect
30+
31+
## Working with client ids
32+
33+
## Event Timing
34+
35+
The [API Docs](/docs/API.md) provide tips on minimizing errors by reacting to particular events. The natural question
36+
is, "Why _minimize_ instead of _eliminate_?"
37+
38+
The simple(ish) answer is because sockets may disconnect at _any moment_, but events listeners are only executed at a
39+
very specific phase of the node [event loop](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/). In
40+
other words, it is _always_ possible that the socket may be made unavailable (ended or closed) _before_ attached
41+
listeners have a chance to react to it.
42+
43+
This module tries to strike a balance between usability and giving you the ability to quickly respond to sockets
44+
becoming unavailable.
45+
46+
For convenience, `socket-ipc` events will listen for key socket events and either emit more specific events or simply
47+
_echo_ the original event. For example, whenever a `socket` emits an `error` event `socket-ipc` will handle it by
48+
repeating it as its own `error` event. Because of the way the Node event loop works, that means the socket-ipc `error`
49+
event may (will?) be "emitted" at least one event loop iteration later than the original socket `error` event. For many
50+
cases these _repeats_ should be good enough. However, this is application dependent.
51+
52+
To allow high-throughput applications respond just a _little bit_ faster when needed, we expose the actual `net.Socket`
53+
instance upon connection. We have also sprinkled "hints" in our API docs about when these applications may want to
54+
attach listeners directly to the socket event.
55+
56+
Our "official" recommendation is that you listen to the `socket-ipc` events unless the timing is causing specific
57+
problems in your application. If you suspect timing side effects, _try_ listening directly to the socket and see if that
58+
makes a _worth-while_ difference. But remember, even listening directly the socket does not guarantee that your listener
59+
will be able to respond "fast enough" to avoid errors -- sometimes the updated socket state just hasn't made its way
60+
into userland Node.

examples/resilience/client.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,13 @@ client.on('disconnect', () => console.log('disconnected from server'));
2222
client.on('reconnect', () => console.log('reconnected to server'));
2323
client.on('message', (message, topic) => console.log(`Heard: [${topic}]`, message));
2424
client.connect();
25+
26+
27+
function shutdown(reason) {
28+
// Stop all processing and let node naturally exit.
29+
console.log('shutting down: ', reason);
30+
client.close();
31+
}
32+
33+
process.on('SIGTERM', () => shutdown('sigterm'));
34+
process.on('SIGINT', () => shutdown('sigint'));

examples/resilience/server.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,17 @@ const {Server} = require('../../src/index');
1919

2020
const SOCKET_FILE = undefined;
2121

22-
22+
let server;
2323
let newServerTimeout;
24+
let serverCloseTimeout;
2425
let nextServerNum = 1;
26+
let shuttingDown = false;
2527
function createServer() {
2628
clearTimeout(newServerTimeout);
2729
const serverNum = nextServerNum++;
2830
console.log(`creating server (${serverNum})`);
2931

30-
const server = new Server({socketFile: SOCKET_FILE});
32+
server = new Server({socketFile: SOCKET_FILE});
3133
server.on('connectionClose', (clientId) => console.log(`Client (${clientId}) disconnected`));
3234
server.on('listening', () => console.log(`server (${serverNum}) is listening`));
3335
server.on('connection', (clientId) => {
@@ -39,12 +41,21 @@ function createServer() {
3941
// After the first client connects, start a time to shut down the server.
4042
// This will show what happens to the client(s) when a server disappears.
4143
server.once('connection', () => {
44+
if (shuttingDown) {
45+
return;
46+
}
47+
4248
console.log(`Server (${serverNum}) will shut down in 10 second.`);
43-
setTimeout(() => server.close(), 10000);
49+
clearTimeout(serverCloseTimeout);
50+
serverCloseTimeout = setTimeout(() => server.close(), 10000);
4451
});
4552

4653
// When the server closes, auto-spawn a new one after a second.
4754
server.on('close', () => {
55+
if (shuttingDown) {
56+
return;
57+
}
58+
4859
console.log(`server (${serverNum}) closed. Starting a replacement server in 5 second.`);
4960
newServerTimeout = setTimeout(() => createServer(), 5000);
5061
});
@@ -54,3 +65,17 @@ function createServer() {
5465
}
5566

5667
createServer();
68+
69+
70+
71+
function shutdown(reason) {
72+
// Stop all processing and let node naturally exit.
73+
console.log('shutting down: ', reason);
74+
shuttingDown = true;
75+
clearTimeout(newServerTimeout);
76+
clearTimeout(serverCloseTimeout);
77+
server.close();
78+
}
79+
80+
process.on('SIGTERM', () => shutdown('sigterm'));
81+
process.on('SIGINT', () => shutdown('sigint'));

examples/spam/client.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Provides a simple client which makes a lot of noise about the server state.
3+
*
4+
* Run one or more copies of in conjunction with `server.js` in the same directory to
5+
* see how the client deals with an unreliable server.
6+
*
7+
* Hint: Don't forget to define the SOCKET_FILE before running!
8+
*
9+
* @author Chris Russell <[email protected]>
10+
* @copyright Chris Russell 2018
11+
* @license MIT
12+
*/
13+
14+
const {Client} = require('../../src/index');
15+
16+
const SOCKET_FILE = undefined;
17+
18+
19+
let spamInterval;
20+
function spam() {
21+
spamInterval = setInterval(() => {
22+
for (let i = 0; i < 300; i++) {
23+
client.send('hello', i + ':' + data[Math.floor(Math.random() * Math.floor(999))]);
24+
}
25+
}, 5);
26+
}
27+
28+
29+
function generate_random_data1(size){
30+
let chars = 'abcdefghijklmnopqrstuvwxyz'.split('');
31+
let len = chars.length;
32+
let random_data = [];
33+
34+
while (size--) {
35+
random_data.push(chars[Math.random()*len | 0]);
36+
}
37+
38+
return random_data.join('');
39+
}
40+
41+
let data = [];
42+
for (let i = 0; i < 1000; i++) {
43+
data.push(generate_random_data1(32));
44+
}
45+
46+
47+
const client = new Client({socketFile: SOCKET_FILE});
48+
client.on('connect', (socket) => {
49+
socket.on('close', () => {
50+
console.log('Server went away (close).');
51+
clearInterval(spamInterval);
52+
});
53+
54+
socket.on('end', () => {
55+
console.log('Server went away.');
56+
clearInterval(spamInterval);
57+
});
58+
59+
console.log('connected to server');
60+
spam()
61+
});
62+
63+
client.on('error', (e) => {
64+
console.log(e);
65+
});
66+
67+
client.connect();
68+
69+
function shutdown(reason) {
70+
// Stop all processing and let node naturally exit.
71+
console.log('shutting down: ', reason);
72+
clearInterval(spamInterval);
73+
client.close();
74+
}
75+
76+
process.on('SIGTERM', () => shutdown('sigterm'));
77+
process.on('SIGINT', () => shutdown('sigint'));

examples/spam/server.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Provides a simple client which makes a lot of noise about the server state.
3+
*
4+
* Run one or more copies of in conjunction with `server.js` in the same directory to
5+
* see how the client deals with an unreliable server.
6+
*
7+
* Hint: Don't forget to define the SOCKET_FILE before running!
8+
*
9+
* @author Chris Russell <[email protected]>
10+
* @copyright Chris Russell 2018
11+
* @license MIT
12+
*/
13+
14+
const {Server} = require('../../src/index');
15+
16+
const SOCKET_FILE = undefined;
17+
18+
const server = new Server({socketFile: SOCKET_FILE});
19+
server.on('listen', () => {
20+
console.log('listening');
21+
});
22+
23+
24+
let msgCount = 0;
25+
let clientCount = 0;
26+
server.on('connection', (id, socket) => {
27+
clientCount++;
28+
socket.on('close', () => clientCount--);
29+
});
30+
server.on('message', (message) => msgCount++);
31+
let statusInterval = setInterval(() => {
32+
console.log(`~${msgCount} messages in last 1s from ${clientCount} clients.`);
33+
msgCount = 0;
34+
}, 1000);
35+
server.listen();
36+
37+
38+
function shutdown(reason) {
39+
// Stop all processing and let node naturally exit.
40+
console.log('shutting down: ', reason);
41+
clearInterval(statusInterval);
42+
server.close();
43+
}
44+
45+
process.on('SIGTERM', () => shutdown('sigterm'));
46+
process.on('SIGINT', () => shutdown('sigint'));

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@crussell52/socket-ipc",
3-
"version": "0.1.2",
3+
"version": "0.2.0",
44
"description": "An event-driven IPC implementation using unix file sockets.",
55
"keywords": ["sockets", "ipc", "unix"],
66
"homepage": "https://crussell52.github.io/node-socket-ipc/",

0 commit comments

Comments
 (0)