Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/cluster-haproxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

# Socket.IO Chat with haproxy & redis

A simple chat demo for socket.io

## How to use

Install [Docker Compose](https://docs.docker.com/compose/install/), then:

```
$ docker-compose up -d
```

And then point your browser to `http://localhost:3000`.

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)).

Each node connects to the redis backend, which will enable to broadcast to every client, no matter which node it is currently connected to.

```
# you can kill a given node, the client should reconnect to another node
$ docker-compose stop server-george
```

## Features

- Multiple users can join a chat room by each entering a unique username
on website load.
- Users can type chat messages to the chat room.
- A notification is sent to all users when a user joins or leaves
the chatroom.
51 changes: 51 additions & 0 deletions examples/cluster-haproxy/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

haproxy:
build: ./haproxy
links:
- server-john
- server-paul
- server-george
- server-ringo
ports:
- "3000:80"

server-john:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=John

server-paul:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Paul

server-george:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=George

server-ringo:
build: ./server
links:
- redis
expose:
- "3000"
environment:
- NAME=Ringo

redis:
image: redis:alpine
expose:
- "6379"
2 changes: 2 additions & 0 deletions examples/cluster-haproxy/haproxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FROM haproxy:1.7-alpine
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
31 changes: 31 additions & 0 deletions examples/cluster-haproxy/haproxy/haproxy.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Reference: http://blog.haproxy.com/2012/11/07/websockets-load-balancing-with-haproxy/

global
daemon
maxconn 4096
nbproc 2

defaults
mode http
balance roundrobin
option http-server-close
timeout connect 5s
timeout client 30s
timeout client-fin 30s
timeout server 30s
timeout tunnel 1h
default-server inter 1s rise 2 fall 1 on-marked-down shutdown-sessions
option forwardfor

listen chat
bind *:80
default_backend nodes

backend nodes
option httpchk HEAD /health
http-check expect status 200
cookie serverid insert
server john server-john:3000 cookie john check
server paul server-paul:3000 cookie paul check
server george server-george:3000 cookie george check
server ringo server-ringo:3000 cookie ringo check
15 changes: 15 additions & 0 deletions examples/cluster-haproxy/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM mhart/alpine-node:6

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install

# Bundle app source
COPY . /usr/src/app

EXPOSE 3000
CMD [ "npm", "start" ]
87 changes: 87 additions & 0 deletions examples/cluster-haproxy/server/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
var port = process.env.PORT || 3000;
var serverName = process.env.NAME || 'Unknown';

io.adapter(redis({ host: 'redis', port: 6379 }));

server.listen(port, function () {
console.log('Server listening at port %d', port);
console.log('Hello, I\'m %s, how can I help?', serverName);
});

// Routing
app.use(express.static(__dirname + '/public'));

// Health check
app.head('/health', function (req, res) {
res.sendStatus(200);
});

// Chatroom

var numUsers = 0;

io.on('connection', function (socket) {
socket.emit('my-name-is', serverName);

var addedUser = false;

// when the client emits 'new message', this listens and executes
socket.on('new message', function (data) {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
message: data
});
});

// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
if (addedUser) return;

// we store the username in the socket session for this client
socket.username = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
});

// when the client emits 'typing', we broadcast it to others
socket.on('typing', function () {
socket.broadcast.emit('typing', {
username: socket.username
});
});

// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function () {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});

// when the user disconnects.. perform this
socket.on('disconnect', function () {
if (addedUser) {
--numUsers;

// echo globally that this client has left
socket.broadcast.emit('user left', {
username: socket.username,
numUsers: numUsers
});
}
});
});
17 changes: 17 additions & 0 deletions examples/cluster-haproxy/server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"description": "A simple chat client using socket.io",
"main": "index.js",
"author": "Grant Timmerman",
"private": true,
"license": "BSD",
"dependencies": {
"express": "4.13.4",
"socket.io": "^1.7.2",
"socket.io-redis": "^3.0.0"
},
"scripts": {
"start": "node index.js"
}
}
28 changes: 28 additions & 0 deletions examples/cluster-haproxy/server/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Socket.IO Chat Example</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul class="pages">
<li class="chat page">
<div class="chatArea">
<ul class="messages"></ul>
</div>
<input class="inputMessage" placeholder="Type here..."/>
</li>
<li class="login page">
<div class="form">
<h3 class="title">What's your nickname?</h3>
<input class="usernameInput" type="text" maxlength="14" />
</div>
</li>
</ul>

<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/main.js"></script>
</body>
</html>
Loading