Skip to content

Commit f7eed6e

Browse files
[docs] Add haproxy cluster example (#2818)
1 parent 9888529 commit f7eed6e

File tree

10 files changed

+697
-0
lines changed

10 files changed

+697
-0
lines changed

examples/cluster-haproxy/README.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
# Socket.IO Chat with haproxy & redis
3+
4+
A simple chat demo for socket.io
5+
6+
## How to use
7+
8+
Install [Docker Compose](https://docs.docker.com/compose/install/), then:
9+
10+
```
11+
$ docker-compose up -d
12+
```
13+
14+
And then point your browser to `http://localhost:3000`.
15+
16+
This will start four Socket.IO nodes, behind a haproxy instance which will loadbalance the requests (using a cookie for sticky sessions, see [cookie](https://cbonte.github.io/haproxy-dconv/1.7/configuration.html#4.2-cookie)).
17+
18+
Each node connects to the redis backend, which will enable to broadcast to every client, no matter which node it is currently connected to.
19+
20+
```
21+
# you can kill a given node, the client should reconnect to another node
22+
$ docker-compose stop server-george
23+
```
24+
25+
## Features
26+
27+
- Multiple users can join a chat room by each entering a unique username
28+
on website load.
29+
- Users can type chat messages to the chat room.
30+
- A notification is sent to all users when a user joins or leaves
31+
the chatroom.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
2+
haproxy:
3+
build: ./haproxy
4+
links:
5+
- server-john
6+
- server-paul
7+
- server-george
8+
- server-ringo
9+
ports:
10+
- "3000:80"
11+
12+
server-john:
13+
build: ./server
14+
links:
15+
- redis
16+
expose:
17+
- "3000"
18+
environment:
19+
- NAME=John
20+
21+
server-paul:
22+
build: ./server
23+
links:
24+
- redis
25+
expose:
26+
- "3000"
27+
environment:
28+
- NAME=Paul
29+
30+
server-george:
31+
build: ./server
32+
links:
33+
- redis
34+
expose:
35+
- "3000"
36+
environment:
37+
- NAME=George
38+
39+
server-ringo:
40+
build: ./server
41+
links:
42+
- redis
43+
expose:
44+
- "3000"
45+
environment:
46+
- NAME=Ringo
47+
48+
redis:
49+
image: redis:alpine
50+
expose:
51+
- "6379"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM haproxy:1.7-alpine
2+
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Reference: http://blog.haproxy.com/2012/11/07/websockets-load-balancing-with-haproxy/
2+
3+
global
4+
daemon
5+
maxconn 4096
6+
nbproc 2
7+
8+
defaults
9+
mode http
10+
balance roundrobin
11+
option http-server-close
12+
timeout connect 5s
13+
timeout client 30s
14+
timeout client-fin 30s
15+
timeout server 30s
16+
timeout tunnel 1h
17+
default-server inter 1s rise 2 fall 1 on-marked-down shutdown-sessions
18+
option forwardfor
19+
20+
listen chat
21+
bind *:80
22+
default_backend nodes
23+
24+
backend nodes
25+
option httpchk HEAD /health
26+
http-check expect status 200
27+
cookie serverid insert
28+
server john server-john:3000 cookie john check
29+
server paul server-paul:3000 cookie paul check
30+
server george server-george:3000 cookie george check
31+
server ringo server-ringo:3000 cookie ringo check
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM mhart/alpine-node:6
2+
3+
# Create app directory
4+
RUN mkdir -p /usr/src/app
5+
WORKDIR /usr/src/app
6+
7+
# Install app dependencies
8+
COPY package.json /usr/src/app/
9+
RUN npm install
10+
11+
# Bundle app source
12+
COPY . /usr/src/app
13+
14+
EXPOSE 3000
15+
CMD [ "npm", "start" ]
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Setup basic express server
2+
var express = require('express');
3+
var app = express();
4+
var server = require('http').createServer(app);
5+
var io = require('socket.io')(server);
6+
var redis = require('socket.io-redis');
7+
var port = process.env.PORT || 3000;
8+
var serverName = process.env.NAME || 'Unknown';
9+
10+
io.adapter(redis({ host: 'redis', port: 6379 }));
11+
12+
server.listen(port, function () {
13+
console.log('Server listening at port %d', port);
14+
console.log('Hello, I\'m %s, how can I help?', serverName);
15+
});
16+
17+
// Routing
18+
app.use(express.static(__dirname + '/public'));
19+
20+
// Health check
21+
app.head('/health', function (req, res) {
22+
res.sendStatus(200);
23+
});
24+
25+
// Chatroom
26+
27+
var numUsers = 0;
28+
29+
io.on('connection', function (socket) {
30+
socket.emit('my-name-is', serverName);
31+
32+
var addedUser = false;
33+
34+
// when the client emits 'new message', this listens and executes
35+
socket.on('new message', function (data) {
36+
// we tell the client to execute 'new message'
37+
socket.broadcast.emit('new message', {
38+
username: socket.username,
39+
message: data
40+
});
41+
});
42+
43+
// when the client emits 'add user', this listens and executes
44+
socket.on('add user', function (username) {
45+
if (addedUser) return;
46+
47+
// we store the username in the socket session for this client
48+
socket.username = username;
49+
++numUsers;
50+
addedUser = true;
51+
socket.emit('login', {
52+
numUsers: numUsers
53+
});
54+
// echo globally (all clients) that a person has connected
55+
socket.broadcast.emit('user joined', {
56+
username: socket.username,
57+
numUsers: numUsers
58+
});
59+
});
60+
61+
// when the client emits 'typing', we broadcast it to others
62+
socket.on('typing', function () {
63+
socket.broadcast.emit('typing', {
64+
username: socket.username
65+
});
66+
});
67+
68+
// when the client emits 'stop typing', we broadcast it to others
69+
socket.on('stop typing', function () {
70+
socket.broadcast.emit('stop typing', {
71+
username: socket.username
72+
});
73+
});
74+
75+
// when the user disconnects.. perform this
76+
socket.on('disconnect', function () {
77+
if (addedUser) {
78+
--numUsers;
79+
80+
// echo globally that this client has left
81+
socket.broadcast.emit('user left', {
82+
username: socket.username,
83+
numUsers: numUsers
84+
});
85+
}
86+
});
87+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "socket.io-chat",
3+
"version": "0.0.0",
4+
"description": "A simple chat client using socket.io",
5+
"main": "index.js",
6+
"author": "Grant Timmerman",
7+
"private": true,
8+
"license": "BSD",
9+
"dependencies": {
10+
"express": "4.13.4",
11+
"socket.io": "^1.7.2",
12+
"socket.io-redis": "^3.0.0"
13+
},
14+
"scripts": {
15+
"start": "node index.js"
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>Socket.IO Chat Example</title>
6+
<link rel="stylesheet" href="style.css">
7+
</head>
8+
<body>
9+
<ul class="pages">
10+
<li class="chat page">
11+
<div class="chatArea">
12+
<ul class="messages"></ul>
13+
</div>
14+
<input class="inputMessage" placeholder="Type here..."/>
15+
</li>
16+
<li class="login page">
17+
<div class="form">
18+
<h3 class="title">What's your nickname?</h3>
19+
<input class="usernameInput" type="text" maxlength="14" />
20+
</div>
21+
</li>
22+
</ul>
23+
24+
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
25+
<script src="/socket.io/socket.io.js"></script>
26+
<script src="/main.js"></script>
27+
</body>
28+
</html>

0 commit comments

Comments
 (0)