It's Time For Real-Time

Eric Terpstra

@eterps

A Solution For Adding

Real Time Functionality

To Your Existing Web Applications

Before We Get Started...

A Story

Warning: Following fictional anectode may contain absurd amounts of animated GIFs (pronounced: 'jiffs')

Once upon a time...

And one unsung lady

Super Serious Business Application

And all was good...

...for a time.

(image via: luke o’sullivan)

Then came the revolt.

Followed by salvation?

A New Alliance Was Forged

Through toil and torment...

...and neverending struggles

Victory

?

Then

Now

Sure, why not?

And then came JavaScript

And AJAX Happened

And users rejoiced...

...but were soon unsatisfied.

(again)

What about real-time notifications and live updates?

Time for another rewrite!

Get Ruby on Rails on the Phone!

Just Kidding :)

There has to be a better way...

Enter WebSockets!

Not the hero we deserve
 but the hero we need. 

WebSockets: Awesome, but hard.

And sometimes missing completely.

If only there were a library to cover up all the pain points of WebSockets and provide an easy-to-use cross-platform solution with intelligent fallbacks and helpful utilities...

for free.

Because budgets

         Planning Budget:   $12,490,000.32
      Development Budget:   $35
Software Licenses Budget:   $0

Hooray For Open-Source!

And so the work continued...

... but smarter this time.

And All Was Well Again.

And Now Back to Your Regularly Scheduled Presentation...

The Solution:

This Talk Is About:

  • A possible solution to a common problem
     
  • A look at some new features of Socket.IO 1.0
     
  • Foundational Knowledge

This Is Not:

  • A Deep Dive Into WebSockets
  • A Laundry List of Socket.IO Capabilities
  • Evangelism

Warning:

This solution makes heavy use of open source libraries. If you like creating solutions by yourself, from scratch, this is not for you.

Let's Take It For a Spin,

Shall We?

Socket.IO

 

  • Browser Fallbacks for WebSockets
    • Starts with long-poll, steps up to WS if available.
    • Flash based socket connection also an option.
  • Connect/Disconnect Logic
  • Events
  • Namespaces, Rooms
  • Logging
  • Binary Streams
  • And More...

Like jQuery For WebSockets

  • A Key-Value Store
  • A Pub-Sub Mechanism
  • And more... http://redis.io

important

Your Silent Partner

Socket.IO Emitter

  • Emits events TO Socket.IO Server FROM... somewhere else.
     
  • Implemented in YOUR FAVORITE LANGUAGE
     
  • Redis pub/sub under the hood.

The Bolt-On Accessory For Your App

A Diagram

  1. HTTP!
     
  2. Emitter
     
  3. Socket.IO

The Redis is Silent

HOWTO: Socket.IO

  • Install Node.js
     
  • Install Socket.IO
     
  • Install Socket.IO-Redis
     
npm install socket.io
npm install socket.io-redis

HOWTO: Redis

  • Install & Configure
    • http://redis.io/download
    • http://redis.io/topics/quickstart
  • Dependencies
    • Redis Client/Adaptor
      • http://redis.io/clients
    • Msgpack
      • http://msgpack.org/

HOWTO: Emitter

  • Off-the-shelf
    • ​PHP
    • Java
    • Ruby
    • Python
    • .NET
    • Go
    • Node.js
  • Roll-Your-Own
    • Implement functions for emit, broadcast, in/to, and of.
    • Publish data to Redis
       
  • Do Both
    • Hooray Open-Source!

HOWTO: Review

  • Setup Node and Redis
    • Presumably on a server
    • Create some space for a node app
  • Find/Create and "Emitter" Class
    • Integrate it into your app codebase
  • Install Dependencies
    • Redis connector/client
    • Msgpack

And now, some code.

The Socket.IO Server

var server = require('http').Server();
var io = require('socket.io')(server);
var ioredis = require('socket.io-redis');

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

server.listen(3000);

Not Kidding. That's It.

socket-io-server.js

Client-Side JavaScript

// Connect to Socket.IO
var socket = io('http://demoapp.dev:3000');


// Listen for the 'Thing::update' event 
socket.on('Thing::update', function(thing){
    
    // Parse the event data
    thing = JSON.parse(thing);

    // Update the data in the DOM
    ...
});
<script src="http://yoursocketserver.org/socket.io/socket.io.js"></script>

Include Client-Side Socket.IO Library

Write some JavaScript

Server Side / Emitter

public function updateThing(thingId):Void
{
    // Create an instance of your Emitter
    Emitter myEmitter = new SocketIO\Emitter(redisConnObj);

    // Create some data to push to connected clients
    Thing thisThing = ORM.Thing.get(thingId);
    thisThing.name = "Make more moneys"
    thisThing.importance = 9999;

    // Data must be string or binary
    String eventData = thisThing.toJSON();  
    
    // Name the event
    String updateEventName = "Thing::update";

    // Emit the event
    myEmitter.emit(updateEventName, eventData);
}

your-app-update.class.module.java.py.rb

Quick Review

"Emit" and event + data from your app using your Emitter class/module

Socket.IO (and Redis) does stuff. 

You do nothing

Listen for event & data in the browser with your own JS code.

myEmitter.emit( "AwesomeEvent", importantDatas );
socket.on( 'AwesomeEvent', function(importantData){} );

Let's See That Demo Again

Live Updates: Strategies

  • Send a changeset
    A record changed. Here's the new data.
     
  • Send a flag to do some action
    You wanted to be notified about this. Do something.
     
  • Sync client-side models
    Use a lib (or fancy code) to keep front/backend models in sync
     
  • Use namespaces/rooms

And Another Demo

Rooms!

Requires some additional setup...

var server = require('http').Server();
var io = require('socket.io')(server);
var ioredis = require('socket.io-redis');

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

io.on('connection', function(socket){

    socket.on('AlertToggle::join', function(){
        socket.join('ROOM::ExtraImportantUpdates');
    });

    socket.on('AlertToggle::leave', function(){
        socket.leave('ROOM::ExtraImportantUpdates');
    });

});

server.listen(3000);

socket-io-server.js

Rooms! (Browser)

$(document).on('click', '.alert-toggle', function(e){

    if(!gettingAlerts) {

        // Send signal to join the room
        socket.emit('AlertToggle::join');
        $(this).text('Stop Alerts');

        
    } else {

        // Send signal to leave the room
        socket.emit('AlertToggle::leave');
        $(this).text('Get Alerts');
    }

    gettingAlerts != gettingAlerts;

});

Rooms! (App/Emitter)

public void function deleteThing() {

    ...

    if ( thingToDelete.importance > 100 )
    {
        String updateRoom = "ROOM::ExtraImportantUpdates";
        String deleteEventName = "Thing::delete::important";

        myEmitter.in( updateRoom ).emit( deleteEventName, thingToDelete );
    }

    ...
}
socket.on('Thing::delete::important', function(thing){
    var thing = JSON.parse(thing);
    $('#myModal').modal('show');
    $('#modal-important-record').text(thing.name);

});

App Server Code

Browser Code - ONLY RUNS IF IN ROOM

Namespaces Are a Thing, Too.

From http://socket.io/docs/rooms-and-namespaces/

Socket.IO allows you to “namespace” your sockets, which essentially means assigning different endpoints or paths.

var nsp = io.of('/my-namespace');
nsp.on('connection', function(socket){
  console.log('someone connected'):
});
nsp.emit('hi', 'everyone!');
var socket = io('http://yoursocketserver.com/my-namespace');

Socket.IO Server Code

Browser/Client Code

Another Demo!

You've got Node and Redis now. Why not use them?

Real-Time Tracking

Browser/Client Code

$(document).on('click', function(){
    socket.emit('document::clicked');
});

Socket.IO Server (socket-io-server.js)

var redis = require('redis');
var redisClient = redis.createClient();
...
io.on('connection', function(socket){
    socket.on('document::clicked', function(){
        redisClient.incr('analytics::counter', function(err,reply){
            redisClient.get('analytics::counter',function(err,reply){
                io.emit('Analytics::counter',reply);
            });
        });
    });
});

Other Things to Ponder

  • Running the Node Process
    Normal Linux stuff. Or PM2 module.
     
  • Scaling
    Scaling solution also uses Redis adapter. Convenient!
     
  • Security
    Share session data. Use passwords. More things.

Risks

  • Shoot-Self-In-Foot Risk
  • Open Source  (Support)
  • Just hit 1.0
  • Windows

Still, better than:

  • Long/Short Polling
  • Platform Specific RT Solution
  • Server Sent Events (SSE)
  • Nothing

What is Socket.IO, Really?

  • Engine.IO

    • WebSocket Transport, low-level client & parser

  • Socket.IO
    • Fallback Transports
    • Marshall/API for other modules & functionality
  • Adapters (registry of rooms, users, namespaces)
    • In-Memory
    • ​Redis
  • Clients
  • Parsers

Resources

https://slides.com/eterps/time-for-real-time

These Slides

Thanks!

@eterps

Questions?