Real-time Web Application with Socket.IO, Node.js, and Redis
York Tsai, JSDC 2013
Who is York
VP Engineering @ EZTABLE
Never Live DEMO!!
I hope this work on my (and yours) computer ...
You may Have Heard About
- Forever Iframe
- XMLHttpRequest Long Polling
- Cometd
- Websocket
TL;DR
Socket.IO
- Cross browser (including IE 6)
- Real-time and bi-directional persistent connection (WebSocket)
- Very simple to use
- Javascript!
Start a Server
express = require("express")
app = express()
server = require("http").createServer(app)
io = require("socket.io").listen(server)
server.listen 80
Sending and Receiving Events
Server
io.sockets.on "connection", (socket) ->
socket.emit "event_X",
hello: "world"
socket.on "event_Y", (data) ->
# will get {my: "data"}
Client
socket = io.connect("http://localhost")
socket.on "event_X", (data) ->
# data is {hello: "world"}
socket.emit "event_Y",
my: "data"
Select targets for your event
Broadcasting
io.sockets.on "connection", (socket) ->
socket.broadcast.emit "myevent" "a new user connected"
Rooms
Join / Leave
socket.join('room')
socket.leave('room')
Emitting
io.sockets.in('room').emit('event_name', data)
Single Target
clients = []
io.sockets.on('connection', (socket) ->
clients.push(socket.id)
)
io.sockets.socket(clients[0]).emit "for_first_one",
msg: "You are the first!"
Namespacing
Server
io.of("/my_app")
.on("connection", (socket) ->
# event handlers
)
Client
myAppClient = io.connect("http://localhost/my_app")
Get old data for new connections?
Data Persistence
- Need a persistence layer.
- Get old data from the persistence layer whenever a new connection established
Integration
I have component X,Y,Z want to send/receive data from user's browser
Pub/Sub or Message Queue
Messaging system usually supports both
Pub/Sub
- Super-fast in-memory data structure server
- Pub/Sub
- Rich client libraries
- Zero install and easy to operate
Only 3 Commands
- SUBSCRIBE channel
- UNSUBSCRIBE channel
- PUBLISH channel message
In addition...
You can connect multiple node.js server to one redis channel
Scalability!
Two Connections per Server
Redis-backed Pub/Sub
- Two clients required: one for pub, one for sub
- Use one channel, add metadata to your message
Redis-backed Pub/Sub
redisSubClient.on "ready", ->
redisSubClient.subscribe redisChannel
redisSubClient.on "message", (channel, data) ->
data = JSON.parse(data)
ioEvent = data.event
ioData = data.data
io.sockets.in(data.channel)
.emit(ioEvent, ioData)
Frequently Asked Questions
SSL Server
privateKey = fs.readFileSync("my_key").toString()
certificate = fs.readFileSync("my_crt").toString()
ca = fs.readFileSync("intermediate.crt").toString()
app = express.createServer({
key: privateKey
cert: certificate
ca: ca
})
SSL Client
io.connect('https://localhost', {secure: true})
Cross domain?
Yes! (If use WebSocket or JSONP)
Authentication
- Cookie & Session
- Cookie does not cross domain, use SSL + Access Token instead
Load Balancing?
Yes! (If use Nginx >= 1.3.13)
Sample Nginx Configuration
server {
listen 80;
server_name my.server;
location / {
proxy_pass nodeCluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Load Balancing (ELB)
- ELB's HTTP(S) proxy does not understand websocket requests
- Use TCP/SSL, instead of HTTP/HTTPS
- No session stickyness
- No X-Forwarded-For header
Scalability
- Single thread
- 2500~3500 connections per process
- As many processes as you want
Configuration
Some features do not work by default
Configurations
- store (default: MemoryStore, single process only)
- transports (default: websocket, htmlfile, xhr-polling, jsonp-polling)
- authorization (default: false)
Configurations - transports
io.configure "production", ->
io.enable "browser client etag"
io.set "log level", 1
io.set "transports", ["websocket", "flashsocket", "htmlfile", "xhr-polling", "jsonp-polling"]
NODE_ENV=production node app.js
Put your name here and win $1,000 EZTABLE VIP Voucher!
Hint: socket.emit "prize", "your name"
Never trust data from frontend!
Culture
- Flexible working hours
- No PM, Product Owner MUST code/design
- Data-driven customer development
- Hot girls!
Every Month you got
- 2~3 study groups
- 1 work away day
- 1 extra holiday
- 1 hack day