SocketStream

176

description

A talk given at Node.js Cambridge about SocketStream, a realtime framework for single page apps. https://socketstream.com

Transcript of SocketStream

Page 1: SocketStream
Page 2: SocketStream

A web framework for single page apps

Page 3: SocketStream

Created byOwen Barnes

@temporalwave !!

(my boss at a former company)

Page 4: SocketStream

About me

Paul Jensen @paulbjensen

!!

(I’m the new lead developer)

Page 5: SocketStream

Where to begin?

Page 6: SocketStream

Why Single Page Apps?

Page 7: SocketStream

With a traditional web app, the user has to refresh the

page to see new information

Page 8: SocketStream

Time

Server

Client

Page 9: SocketStream

Time

Server

Client

GET/football/live HTTP/1.1

Page 10: SocketStream

Time

Server

Client

HTTP/1.1 200 OK

Page 11: SocketStream

Time

Server

Client

20 seconds later…

Page 12: SocketStream

Time

Server

Client

I wonder what the latest score is… Let’s reload the page

Page 13: SocketStream

Time

Server

Client

GET/football/live HTTP/1.1

Page 14: SocketStream

Time

Server

Client

HTTP/1.1 304 Not Modified

Page 15: SocketStream

Time

Server

Client

Page 16: SocketStream

Time

Server

Client

The user had to press F5 to get at any new information

Page 17: SocketStream

Time

Server

Client

Even though there was no new information,

the server still had to serve the HTTP request

Page 18: SocketStream

This is not a fun experience

Page 19: SocketStream

How do we make this better?

Page 20: SocketStream

Time

Server

Client

Page 21: SocketStream

We could use AJAX to update the page

Page 22: SocketStream

We’d save the user having to press the F5 key

Page 23: SocketStream

What else can we do?

Page 24: SocketStream

Time

Server

Client

Page 25: SocketStream

Optimise the response

Page 26: SocketStream

GZIP the response data, and …

Page 27: SocketStream

Avoid sending data we already have on the client

Page 28: SocketStream

We could also separate the HTML

from the data

Page 29: SocketStream

Reuse the HTML on the client

Page 30: SocketStream

…and use the server to provide you with just

data

Page 31: SocketStream

And the web site becomes a client

Server

Web App Native App API User

Page 32: SocketStream

The server is just an API

Page 33: SocketStream

A beautiful separation of concerns

Page 34: SocketStream

Overview

• The server becomes a REST API serving JSON

• HTML compilation is done on the client

• As a result, less processing & bandwidth is consumed by the server

Page 35: SocketStream

Why Realtime?

Page 36: SocketStream

Time

Server

Client

Page 37: SocketStream

Polling the server every [n] seconds for new data is redundant

Page 38: SocketStream

There has to be a better way

Page 39: SocketStream

What if the server could send its client(s) new

data as soon as it came about?

Page 40: SocketStream

We can, thanks to WebSockets

Page 41: SocketStream

WebSockets allows data to be sent both ways

Page 42: SocketStream

Time

Server

Client

Page 43: SocketStream

Time

Server

Client

Goal

Page 44: SocketStream

Time

Server

Client

Goal

The server sends a message to the client that an action has

occurred

Page 45: SocketStream

We eliminate the need to poll the server for

new data

Page 46: SocketStream

Overview

• We can replace AJAX polling with WebSockets, and provide a better user experience as a result

• We also remove the need to make redundant polling requests back to the server.

• We use WebSockets for sending/receiving JSON

Page 47: SocketStream

Single Page Apps +

The Realtime Web

Page 48: SocketStream

There are many ways to build this kind of

app

Page 49: SocketStream

You could build it mostly from scratch, and use Express + Socket.io

Page 50: SocketStream

Or alternatively, you could use a web

framework like Meteor or Firebase

Page 51: SocketStream

SocketStream is somewhere in-between

these 2 approaches

Page 52: SocketStream

It provides tools to help with building realtime

single page apps...

Page 53: SocketStream

... Whilst trying not to restrict what

technologies you can use with your app

Page 54: SocketStream

For example, we don't provide an ORM.

Instead, you choose the database & the ORM

Page 55: SocketStream

Also, we don't mandate using a specific client-side framework

!

You can use BackBone, Angular, Ember, or something else, that is

entirely your choice.

Page 56: SocketStream

What we focus on instead are these

things:

Page 57: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 58: SocketStream

I'll run through each of these, 1-by-1. But first, let's look at how to use

SocketStream

Page 59: SocketStream

Getting started

npm install -g socketstream ! socketstream new my_app

Page 60: SocketStream

Getting started

Success! Created app 'my_app' with: ! ✓ Basic chat demo (-m for minimal install) ✓ Javascript example code (-c if you prefer CoffeeScript) ✓ Plain HTML for views (-j if you prefer Jade) Next, run the following commands: ! cd my_app [sudo] npm install ! To start your app: ! node app.js

Page 61: SocketStream

Here's what the initial file/folder structure looks like

Page 62: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 63: SocketStream

Client code is organised into 5 sub-folders

Page 64: SocketStream

Client side code organisation• CODE stores client side JavaScript files and libraries

• CSS stores CSS files

• STATIC stores public files like images, font files, and other assets

• TEMPLATES stores HTML templates for the single page app to render on the client

• VIEWS stores HTML files that are rendered from the server for the initial page

Page 65: SocketStream

Those sub-folders have sub-folders, but are optional

Page 66: SocketStream

This is how we load them

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 67: SocketStream

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 68: SocketStream

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 69: SocketStream

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 70: SocketStream

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 71: SocketStream

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 72: SocketStream

// My SocketStream 0.3 app !var http = require('http'), ss = require('socketstream'); !// Define a single-page client called 'main' ss.client.define('main', { view: 'app.html', css: ['libs/reset.css', 'app.styl'], code: ['libs/jquery.min.js', 'app'], tmpl: '*' }); !// Serve this client on the root URL ss.http.route('/', function(req, res){ res.serveClient('main'); });

Page 73: SocketStream

SocketStream uses Browserify to handle

requiring JS files

Page 74: SocketStream

Browserify allows us to use a Node.js style of

requiring JS files

Page 75: SocketStream

// This file automatically gets called first by SocketStream and must always exist !// Make 'ss' available to all modules and the browser console window.ss = require('socketstream'); !ss.server.on('disconnect', function(){ console.log('Connection down :-('); }); !ss.server.on('reconnect', function(){ console.log('Connection back up :-)'); }); !ss.server.on('ready', function(){ ! // Wait for the DOM to finish loading jQuery(function(){ // Load app require('/app'); ! }); !});

Page 76: SocketStream

// This file automatically gets called first by SocketStream and must always exist !// Make 'ss' available to all modules and the browser console window.ss = require('socketstream'); !ss.server.on('disconnect', function(){ console.log('Connection down :-('); }); !ss.server.on('reconnect', function(){ console.log('Connection back up :-)'); }); !ss.server.on('ready', function(){ ! // Wait for the DOM to finish loading jQuery(function(){ // Load app require('/app'); ! }); !});

Page 77: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 78: SocketStream

Over the years, developers have come up with new languages to generate

HTML, CSS, and JavaScript

Page 79: SocketStream

SocketStream allows developers to use these code preprocessors in

their apps

Page 80: SocketStream

Adding a preprocessor is simple

// Code Formatters ss.client.formatters.add(require('ss-stylus'));

Page 81: SocketStream

For Javascript

• SS-COFFEE - supports using CoffeeScript

• SS-GORILLA - supports using GorillaScript

Page 82: SocketStream

For CSS

• SS-STYLUS - supports using Stylus

• SS-LESS - supports using Less

Page 83: SocketStream

For HTML Views

• SS-JADE - supports using Jade

Page 84: SocketStream

For HTML Templating

• SS-HOGAN - supports using Twitter's Hogan.js

• SS-COFFEEKUP - supports using CoffeeKup

Page 85: SocketStream

Setting a Templating engine

// Use server-side compiled Hogan (Mustache) templates. Others engines available ss.client.templateEngine.use(require('ss-hogan'));

Page 86: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 87: SocketStream

Having to press F5 to reload the page in order to view

changes to HTML/CSS/JS...

Page 88: SocketStream

... is not a fun experience

Page 89: SocketStream

In development mode, SocketStream will watch the client files for changes, and reload the page when they

occur

Page 90: SocketStream

In the case of CSS, SocketStream will apply

the changes without reloading the page

Page 91: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 92: SocketStream

Client-side HTML templates are made

available to the browser via the ss.tmpl object

Page 93: SocketStream
Page 94: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 95: SocketStream

When you're building a single page app, you'll

have a lot of JS files, and maybe a few CSS files

Page 96: SocketStream

But serving a HTML page with lots of these files can take time, and

is inefficient

Page 97: SocketStream

SocketStream provides a way to concatenate, minify, and GZip these files into 1

JS and 1 CSS file

Page 98: SocketStream

This saves bytes being transferred, as well as

reducing the number of HTTP requests you make

Page 99: SocketStream

Also, you can tell SocketStream to load

these files from a CDN

Page 100: SocketStream

Setting a Templating engine

// Minimize and pack assets if you type: SS_ENV=production node app.js if (ss.env === 'production') ss.client.packAssets();

Page 101: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 102: SocketStream

Web Workers are handy for intensive client-side

JS operations

Page 103: SocketStream

SocketStream provides support for using Web

Workers in your app

Page 104: SocketStream

First, create a folder

Page 105: SocketStream

Next, put your web worker files in that folder

Page 106: SocketStream

Then, load the worker in a client code file, and enjoy

Page 107: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 108: SocketStream

SocketStream uses Connect middleware to support HTTP features

Page 109: SocketStream

SocketStream uses the following middleware by default:

• compress - for GZipping assets

• cookieParser - for handling user tracking

• favicon - for serving a favicon.ico file

• session - for handling sessions

• static - for serving static assets

Page 110: SocketStream

SocketStream uses the following middleware by default:

• compress middleware is loaded first, before all other middleware

• static middleware is loaded last, after all other middleware

Page 111: SocketStream

SocketStream provides a way to load custom middleware into the

connect stack

Page 112: SocketStream

ss.http.middleware.prepend() ss.http.middleware.append()

Page 113: SocketStream

This allows you to use all of the connect

middleware out there today, i.e. EveryAuth

Page 114: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 115: SocketStream

We use connect’s session middleware, so authentication

can be done with either EveryAuth, PassportJS, or you

can roll your own.

Page 116: SocketStream

We also recommend using connect-redis

Page 117: SocketStream

Both HTTP and WebSocket interfaces

can get/set the session data

Page 118: SocketStream

Via HTTP

// app.js ss.http.router.on('/updateSession', function(req, res) { req.session.myVar = 4321; res.end('req.session.myVar has been updated to', req.session.myVar); });

Page 119: SocketStream

Via WebSockets

Page 120: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 121: SocketStream

RPC is a common pattern for clients

requesting data from the server

Page 122: SocketStream

SocketStream provides a way to construct RPC

APIs with flexibility

Page 123: SocketStream
Page 124: SocketStream
Page 125: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 126: SocketStream

PubSub is a great pattern for Single Page

Apps

Page 127: SocketStream

SocketStream handles this in various ways:

Page 128: SocketStream

1 - Publishing to everyone viewing the app right now

ss.publish.all('newMessage', message); // Broadcast the message to everyone

Server

// Listen out for newMessage events coming from the server ss.event.on('newMessage', function(message) { // do something with the message });

Client

Page 129: SocketStream

2 - Sending to private channels

// in a /server/rpc file after calling req.use('session') middleware !req.session.channel.subscribe('disney') !req.session.channel.unsubscribe('kids') !req.session.channel.reset() // unsubscribes the session from every channel !req.session.channel.list() // shows what channels are subscribed to

Server (subscribe/unsubscribe the session )

Page 130: SocketStream

2 - Sending to private channels

// in a /server/rpc file ss.publish.channel('disney', 'chatMessage', {from: 'jerry', message: 'Has anyone seen Tom?'});

Server (publish to channel)

// in a /client/code file ss.event.on('chatMessage', function(msg, channelName){ console.log('The following message was sent to the ' + channelName + ' channel:', msg); });

Client (receive channel message)

Page 131: SocketStream

3 - Sending to users

// in a /server/rpc file ss.publish.user('fred', 'specialOffer', 'Here is a special offer just for you!');

Server

Page 132: SocketStream

4 - Sending to a browser tab

// in a /server/rpc file ss.publish.socketId('254987654324567', 'justForMe', 'Just for one tab');

Server

Page 133: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 134: SocketStream

On top of RPC and PubSub, SocketStream provides you with a way to create custom

request responders

Page 135: SocketStream

Request Response is basically a WebSocket

message handler

Page 136: SocketStream

It allows you to write message handling for games, where every

byte matters

Page 137: SocketStream

Building RPC APIs

Live ReloadWebSocket Management

Minifying CSS/JS for production use

Client-side code organisation

Building PubSub APIs

HTML / CSS / JS code preprocessing

Session Management Building custom APIs on top of WS

HTML Templates Web Workers Connect middleware compatibility

Page 138: SocketStream

WebSockets are not immortal…

Page 139: SocketStream

They are mangled by mobile networks…

Page 140: SocketStream

Blocked by firewalls…

Page 141: SocketStream

Routed to dead ends by proxy servers

Page 142: SocketStream

And severed by train tunnels

Page 143: SocketStream

Also, browser support for WebSockets isn’t

guaranteed

Page 144: SocketStream

You need a transport strategy

Page 145: SocketStream

Originally, SocketStream used Socket.io

Page 146: SocketStream

But Socket.io asserted that if a browser

supported WebSockets, then it would work

Page 147: SocketStream

They learned from this, by building Engine.io

Page 148: SocketStream

I created the transport wrapper for Engine.io in

SocketStream for Bechtel & Dashku

Page 149: SocketStream

And designed it to reconnect the client

when severed

Page 150: SocketStream

Months later, it made it’s way into SocketStream’s

core.

Page 151: SocketStream

SocketStream let’s you use this, alongside

SockJS

Page 152: SocketStream

…and that is SocketStream in a nutshell. Whew!

Page 153: SocketStream

Let’s look at some SocketStream apps in

the wild

Page 154: SocketStream

Hollowhollowdocumentary.com

Page 155: SocketStream

Vmuxvmux.co

Page 156: SocketStream

Dashkudashku.com

Page 157: SocketStream

SocketStream plugins

Page 158: SocketStream

SS-BACKBONE

Page 159: SocketStream

SS-ANGULAR

Page 160: SocketStream

SS-CUCUMBER

Page 161: SocketStream

Tips for deploying SocketStream in

production

Page 162: SocketStream

1 - Check your server’s ulimit configuration

(This can bite you hard)

Page 163: SocketStream

I learned this when Dashku went #1 on Hacker News in 45min

Page 164: SocketStream

2 - Use HTTPS, but handle it at the load balancer level rather than at the app level

Page 165: SocketStream

HTTPS helps to improve the stability of WebSocket

connections, especially on mobile devices

Page 166: SocketStream

But Node’s HTTPS implementation is

noticeably slower than using HAProxy or Nginx

Page 167: SocketStream

Where is SocketStream going next?

Page 168: SocketStream

We’re in the process of getting SocketStream’s

test coverage up

Page 169: SocketStream

We’re also trying to close some ancient

bugs

Page 170: SocketStream

We also need better documentation

Page 171: SocketStream

We’re giving the web site an overhaul

Page 172: SocketStream

And we want to document how

SocketStream’s internals function, to help build 0.4

Page 173: SocketStream

but what about 0.4?

Page 174: SocketStream

…0.4 is starting to look like these:

Page 175: SocketStream

I promise you all, it’s coming in June 2014

Page 176: SocketStream

Thank You