SocketStream

Post on 10-May-2015

1.356 views 0 download

Tags:

description

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

Transcript of SocketStream

A web framework for single page apps

Created byOwen Barnes

@temporalwave !!

(my boss at a former company)

About me

Paul Jensen @paulbjensen

!!

(I’m the new lead developer)

Where to begin?

Why Single Page Apps?

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

page to see new information

Time

Server

Client

Time

Server

Client

GET/football/live HTTP/1.1

Time

Server

Client

HTTP/1.1 200 OK

Time

Server

Client

20 seconds later…

Time

Server

Client

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

Time

Server

Client

GET/football/live HTTP/1.1

Time

Server

Client

HTTP/1.1 304 Not Modified

Time

Server

Client

Time

Server

Client

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

Time

Server

Client

Even though there was no new information,

the server still had to serve the HTTP request

This is not a fun experience

How do we make this better?

Time

Server

Client

We could use AJAX to update the page

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

What else can we do?

Time

Server

Client

Optimise the response

GZIP the response data, and …

Avoid sending data we already have on the client

We could also separate the HTML

from the data

Reuse the HTML on the client

…and use the server to provide you with just

data

And the web site becomes a client

Server

Web App Native App API User

The server is just an API

A beautiful separation of concerns

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

Why Realtime?

Time

Server

Client

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

There has to be a better way

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

data as soon as it came about?

We can, thanks to WebSockets

WebSockets allows data to be sent both ways

Time

Server

Client

Time

Server

Client

Goal

Time

Server

Client

Goal

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

occurred

We eliminate the need to poll the server for

new data

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

Single Page Apps +

The Realtime Web

There are many ways to build this kind of

app

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

Or alternatively, you could use a web

framework like Meteor or Firebase

SocketStream is somewhere in-between

these 2 approaches

It provides tools to help with building realtime

single page apps...

... Whilst trying not to restrict what

technologies you can use with your app

For example, we don't provide an ORM.

Instead, you choose the database & the ORM

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.

What we focus on instead are these

things:

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

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

SocketStream

Getting started

npm install -g socketstream ! socketstream new my_app

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

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

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

Client code is organised into 5 sub-folders

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

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

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'); });

// 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'); });

// 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'); });

// 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'); });

// 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'); });

// 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'); });

// 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'); });

SocketStream uses Browserify to handle

requiring JS files

Browserify allows us to use a Node.js style of

requiring JS files

// 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'); ! }); !});

// 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'); ! }); !});

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

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

HTML, CSS, and JavaScript

SocketStream allows developers to use these code preprocessors in

their apps

Adding a preprocessor is simple

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

For Javascript

• SS-COFFEE - supports using CoffeeScript

• SS-GORILLA - supports using GorillaScript

For CSS

• SS-STYLUS - supports using Stylus

• SS-LESS - supports using Less

For HTML Views

• SS-JADE - supports using Jade

For HTML Templating

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

• SS-COFFEEKUP - supports using CoffeeKup

Setting a Templating engine

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

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

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

changes to HTML/CSS/JS...

... is not a fun experience

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

occur

In the case of CSS, SocketStream will apply

the changes without reloading the page

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

Client-side HTML templates are made

available to the browser via the ss.tmpl object

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

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

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

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

is inefficient

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

JS and 1 CSS file

This saves bytes being transferred, as well as

reducing the number of HTTP requests you make

Also, you can tell SocketStream to load

these files from a CDN

Setting a Templating engine

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

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

Web Workers are handy for intensive client-side

JS operations

SocketStream provides support for using Web

Workers in your app

First, create a folder

Next, put your web worker files in that folder

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

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

SocketStream uses Connect middleware to support HTTP features

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

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

SocketStream provides a way to load custom middleware into the

connect stack

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

This allows you to use all of the connect

middleware out there today, i.e. EveryAuth

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

We use connect’s session middleware, so authentication

can be done with either EveryAuth, PassportJS, or you

can roll your own.

We also recommend using connect-redis

Both HTTP and WebSocket interfaces

can get/set the session data

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); });

Via WebSockets

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

RPC is a common pattern for clients

requesting data from the server

SocketStream provides a way to construct RPC

APIs with flexibility

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

PubSub is a great pattern for Single Page

Apps

SocketStream handles this in various ways:

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

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 )

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)

3 - Sending to users

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

Server

4 - Sending to a browser tab

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

Server

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

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

request responders

Request Response is basically a WebSocket

message handler

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

byte matters

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

WebSockets are not immortal…

They are mangled by mobile networks…

Blocked by firewalls…

Routed to dead ends by proxy servers

And severed by train tunnels

Also, browser support for WebSockets isn’t

guaranteed

You need a transport strategy

Originally, SocketStream used Socket.io

But Socket.io asserted that if a browser

supported WebSockets, then it would work

They learned from this, by building Engine.io

I created the transport wrapper for Engine.io in

SocketStream for Bechtel & Dashku

And designed it to reconnect the client

when severed

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

core.

SocketStream let’s you use this, alongside

SockJS

…and that is SocketStream in a nutshell. Whew!

Let’s look at some SocketStream apps in

the wild

Hollowhollowdocumentary.com

Vmuxvmux.co

Dashkudashku.com

SocketStream plugins

SS-BACKBONE

SS-ANGULAR

SS-CUCUMBER

Tips for deploying SocketStream in

production

1 - Check your server’s ulimit configuration

(This can bite you hard)

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

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

HTTPS helps to improve the stability of WebSocket

connections, especially on mobile devices

But Node’s HTTPS implementation is

noticeably slower than using HAProxy or Nginx

Where is SocketStream going next?

We’re in the process of getting SocketStream’s

test coverage up

We’re also trying to close some ancient

bugs

We also need better documentation

We’re giving the web site an overhaul

And we want to document how

SocketStream’s internals function, to help build 0.4

but what about 0.4?

…0.4 is starting to look like these:

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

Thank You