real-time web apps & .net. what are your options? ndc oslo 2016

163
Real-Time Web Apps & .NET What are Your Options? NDC { Oslo } 10-June-2016 @leggetter

Upload: phil-leggetter

Post on 09-Jan-2017

255 views

Category:

Software


0 download

TRANSCRIPT

Real-Time Web Apps & .NET

What are Your Options?

NDC { Oslo } 10-June-2016

@leggetter

PHIL @LEGGETTER

Head of Developer Relations

[email protected]

3 / 105

@leggetter

4 / 105

@leggetter

What we'll cover

1. Why Real-Time?

2. Common Real-Time Use Cases

3. What are your options?

How do you choose? inc. Communication Patterns

.NET examples

Pros & Cons

4. Q&A

5 / 105

@leggetter

When do we need Realtime?

6 / 105

@leggetter

WCaaS

7 / 105

@leggetter

WCaaS

Data: Is there a timely nature to the data?

7 / 105

@leggetter

8 / 105

@leggetter

User Experience: Is there a timely nature to theexperience?

8 / 105

@leggetter

Realtime is required when there's a Need orDemand for:

Up to date informationInteraction to maintain engagement (UX)

9 / 105

@leggetter

Common Real-Time Use Cases

10 / 105

@leggetter

Notifications & Activity Streams11 / 105

@leggetter

Data Visualizations12 / 105

@leggetter

13 / 105

Chat & Bots@leggetter

13 / 105

Chat & Bots@leggetter

14 / 105

@leggetter

Real-Time Location Tracking15 / 105

@leggetter

Multi-User Collaboration16 / 105

@leggetter

WebRTC Powered AV Chat

17 / 105

@leggetter

18 / 105

@leggetter

Users expect a real-time UX

18 / 105

@leggetter

Users expect a real-time UX

Without a real-time UX your app appearsbroken

18 / 105

@leggetter

You All Have Real-Time Data in Your Apps

Every Single Event

Data ChangesSystem InteractionsUser Interactions

19 / 105

@leggetter

Real-time Web Apps & .NET What are your options?

20 / 105

@leggetter

7 Factors to Consider

21 / 105

@leggetter

1. Should you keep on polling?

22 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users

23 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users2. Over 1 Hour, with a 10 second polling interval

23 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users2. Over 1 Hour, with a 10 second polling interval3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,000

23 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users2. Over 1 Hour, with a 10 second polling interval3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,0004. Poll requests per user/minute = (60 / 10) = 6

23 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users2. Over 1 Hour, with a 10 second polling interval3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,0004. Poll requests per user/minute = (60 / 10) = 65. Poll requests per user/hour = (6 * 60) = 360

23 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users2. Over 1 Hour, with a 10 second polling interval3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,0004. Poll requests per user/minute = (60 / 10) = 65. Poll requests per user/hour = (6 * 60) = 3606. Poll requests site wide per hour = (360 * 10,000) = 3,600,000

23 / 105

@leggetter

Polling Calculations

Scenario

1. Site average of 10,000 Users2. Over 1 Hour, with a 10 second polling interval3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,0004. Poll requests per user/minute = (60 / 10) = 65. Poll requests per user/hour = (6 * 60) = 3606. Poll requests site wide per hour = (360 * 10,000) = 3,600,000

With polling the site would need to handle 3.65 Million requests per hour

Or 50k HTTP requests + maintain 10k persistent connections?

23 / 105

@leggetter

Cache - clients keep pollingPush Proxy solutions

fanout.iostreamdata.io

Quick Win solutions

24 / 105

@leggetter

2. Use an existing solution

Don't reinvent the wheel

Unless you've a unique use case

25 / 105

@leggetter

Why use an existing solution?

Connection fallback/upgrade hacks still required

WebSocket: 91% of connections

HTTP fallback: 9% of connections

26 / 105

@leggetter

Why use an existing solution?

Connection fallback/upgrade hacks still required

WebSocket: 91% of connections

HTTP fallback: 9% of connections

Support/Community

26 / 105

@leggetter

Why use an existing solution?

Connection fallback/upgrade hacks still required

WebSocket: 91% of connections

HTTP fallback: 9% of connections

Support/Community

Maintenance

26 / 105

@leggetter

Why use an existing solution?

Connection fallback/upgrade hacks still required

WebSocket: 91% of connections

HTTP fallback: 9% of connections

Support/Community

Maintenance

Future features

26 / 105

@leggetter

Why use an existing solution?

Connection fallback/upgrade hacks still required

WebSocket: 91% of connections

HTTP fallback: 9% of connections

Support/Community

Maintenance

Future features

Scaling

26 / 105

@leggetter

27 / 105

@leggetter

j.mp/realtime-tech-guide

27 / 105

@leggetter

3. Use languages you're comfortablewith

28 / 105

@leggetter

3. Use languages you're comfortablewith

28 / 105

@leggetter

Solutions by language

PHP: Ratchet, dNode-php

Java: Netty, Jetty

JavaScript (Node.JS): Faye, Socket.IO (Engine.IO), Primus.io

.NET (C#): SignalR, XSockets

Python: Lots of options built on Tornado

Ruby: em-websocket, Faye

Language agnostic: most hosted services

29 / 105

@leggetter

4. Client Device Support?

30 / 105

@leggetter

Client Device Support

Only some have a selection of client libraries

31 / 105

@leggetter

Client Device Support

Only some have a selection of client libraries

Supported connection transports

31 / 105

@leggetter

Client Device Support

Only some have a selection of client librariesSupported connection transportsHow much data are you sending?

31 / 105

@leggetter

Client Device Support

Only some have a selection of client librariesSupported connection transportsHow much data are you sending?SSL required on 3/4G networks

31 / 105

@leggetter

5. Application/Solution Communication Patterns

32 / 105

@leggetter

5. Application/Solution Communication Patterns

How does the client/server & client/client communicate

32 / 105

@leggetter

Simple Messaging33 / 105

@leggetter

0:00 34 / 105

@leggetter

Internet ^5 Machine

0:00 34 / 105

@leggetter

Internet ^5 Machine

Simple Messaging

// client

var ws = new WebSocket('wss://localhost/');

35 / 105

@leggetter

Simple Messaging

// client

var ws = new WebSocket('wss://localhost/');

ws.onmessage = function(evt) { var data = JSON.parse(evt.data);

35 / 105

@leggetter

Simple Messaging

// client

var ws = new WebSocket('wss://localhost/');

ws.onmessage = function(evt) {

var data = JSON.parse(evt.data);

// ^5

performHighFive();

};

35 / 105

@leggetter

Simple Messaging

// client

var ws = new WebSocket('wss://localhost/');

ws.onmessage = function(evt) {

var data = JSON.parse(evt.data);

// ^5

performHighFive();

};

// server

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

35 / 105

@leggetter

Simple Messaging

// client

var ws = new WebSocket('wss://localhost/');

ws.onmessage = function(evt) {

var data = JSON.parse(evt.data);

// ^5

performHighFive();

};

// server

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

socket.send(JSON.stringify({action: 'high-5'}));

});

35 / 105

@leggetter

Simple Messaging

using Nexmo.Api;

// SMS

var results = SMS.Send(new SMS.SMSRequest {

from = "15555551212", to = "17775551212",

text = "this is a test"

});

// Voice

var result = Voice.TextToSpeech(new Voice.TextToSpeechCallCommand {

to = "17775551212",

from = "15555551212", text = "Hello from Nexmo"

});

36 / 105

@leggetter

PubSub37 / 105

@leggetter

38 / 105

@leggetter

PubSub

// client

var client = new Faye.Client('http://localhost:8000/faye');

39 / 105

@leggetter

PubSub

// client

var client = new Faye.Client('http://localhost:8000/faye');

client.subscribe('/leggetter-updates', function(data) {

39 / 105

@leggetter

PubSub

// client

var client = new Faye.Client('http://localhost:8000/faye');

client.subscribe('/leggetter-updates', function(data) {

console.log(data.text);});

39 / 105

@leggetter

PubSub

// client

var client = new Faye.Client('http://localhost:8000/faye');

client.subscribe('/leggetter-updates', function(data) {

console.log(data.text);});

client.subscribe('/leggetter-dm-notifications', function(data) { console.log(data.count);});

39 / 105

@leggetter

PubSub

// client

var client = new Faye.Client('http://localhost:8000/faye');

client.subscribe('/leggetter-updates', function(data) {

console.log(data.text);});

client.subscribe('/leggetter-dm-notifications', function(data) { console.log(data.count);});

// server

server.publish('/leggetter-updates', {text: 'Hello DevWeek!'});

39 / 105

@leggetter

PubSub

// client

var client = new Faye.Client('http://localhost:8000/faye');

client.subscribe('/leggetter-updates', function(data) {

console.log(data.text);});

client.subscribe('/leggetter-dm-notifications', function(data) { console.log(data.count);});

// server

server.publish('/leggetter-updates', {text: 'Hello DevWeek!'});

server.publish('/leggetter-dm-notifications', {count: 2});

39 / 105

@leggetter

Evented PubSub40 / 105

@leggetter

Evented PubSub

// client

var updates = io('/leggetter-updates');

41 / 105

@leggetter

Evented PubSub

// client

var updates = io('/leggetter-updates');updates.on('created', function (data) { // Add activity to UI});

41 / 105

@leggetter

Evented PubSub

// client

var updates = io('/leggetter-updates');updates.on('created', function (data) { // Add activity to UI});updates.on('updated', function(data) { // Update activity});updates.on('deleted', function(data) { // Remove activity});

41 / 105

@leggetter

Evented PubSub

// client

var updates = io('/leggetter-updates');updates.on('created', function (data) { // Add activity to UI});updates.on('updated', function(data) { // Update activity});updates.on('deleted', function(data) { // Remove activity});

// server

var io = require('socket.io')();var updates = io.of('/leggetter-updates');

41 / 105

@leggetter

Evented PubSub

// client

var updates = io('/leggetter-updates');updates.on('created', function (data) { // Add activity to UI});updates.on('updated', function(data) { // Update activity});updates.on('deleted', function(data) { // Remove activity});

// server

var io = require('socket.io')();var updates = io.of('/leggetter-updates');updates.emit('created', {text: 'PubSub Rocks!', id: 1});

41 / 105

@leggetter

Evented PubSub

// client

var updates = io('/leggetter-updates');updates.on('created', function (data) { // Add activity to UI});updates.on('updated', function(data) { // Update activity});updates.on('deleted', function(data) { // Remove activity});

// server

var io = require('socket.io')();var updates = io.of('/leggetter-updates');updates.emit('created', {text: 'PubSub Rocks!', id: 1});updates.emit('updated', {text: 'Evented PubSub Rocks!', id: 1});updates.emit('deleted', {id: 1});

41 / 105

@leggetter

PubSub vs. Evented PubSub

42 / 105

@leggetter

43 / 105

@leggetter

44 / 105

@leggetter

PubSub

client.subscribe('devexp-channel', function(data) { if(data.eventType === 'chat-message') { addMessage(data.message); }

45 / 105

@leggetter

PubSub

client.subscribe('devexp-channel', function(data) { if(data.eventType === 'chat-message') { addMessage(data.message); } else if(data.eventType === 'channel-purposed-changed') { updateRoomTitle(data.purpose); } else if(/* and so on */) { }})

45 / 105

@leggetter

PubSub

client.subscribe('devexp-channel', function(data) { if(data.eventType === 'chat-message') { addMessage(data.message); } else if(data.eventType === 'channel-purposed-changed') { updateRoomTitle(data.purpose); } else if(/* and so on */) { }})

Evented PubSub

var devexp = io('/devexp-channel');devexp.on('chat-message', addMessage);devexp.on('channel-purposed-changed', updateChannelPurpose);

45 / 105

@leggetter

PubSub

client.subscribe('devexp-channel', function(data) { if(data.eventType === 'chat-message') { addMessage(data.message);

}

else if(data.eventType === 'channel-purposed-changed') { updateRoomTitle(data.purpose);

}

else if(/* and so on */) { }

})

Evented PubSub

var devexp = io('/devexp-channel');devexp.on('chat-message', addMessage);

devexp.on('channel-purposed-changed', updateChannelPurpose);

devexp.on('current-topic-changed', updateChannelTopic);

devexp.on('user-online', userOnline);

devexp.on('user-offline', userOffline);

45 / 105

@leggetter

DataSync46 / 105

@leggetter

47 / 105

@leggetter

Data Sync

// client

var ref = new Firebase("https://app.firebaseio.com/doc1/lines");

48 / 105

@leggetter

Data Sync

// client

var ref = new Firebase("https://app.firebaseio.com/doc1/lines");

ref.on('child_added', function(childSnapshot, prevChildKey) { // code to handle new child.});

48 / 105

@leggetter

Data Sync

// client

var ref = new Firebase("https://app.firebaseio.com/doc1/lines");

ref.on('child_added', function(childSnapshot, prevChildKey) { // code to handle new child.});

ref.on('child_changed', function(childSnapshot, prevChildKey) { // code to handle child data changes.});

48 / 105

@leggetter

Data Sync

// client

var ref = new Firebase("https://app.firebaseio.com/doc1/lines");

ref.on('child_added', function(childSnapshot, prevChildKey) { // code to handle new child.});

ref.on('child_changed', function(childSnapshot, prevChildKey) { // code to handle child data changes.});

ref.on('child_removed', function(oldChildSnapshot) { // code to handle child removal.});

48 / 105

@leggetter

Data Sync

// client

var ref = new Firebase("https://app.firebaseio.com/doc1/lines");

ref.on('child_added', function(childSnapshot, prevChildKey) { // code to handle new child.});

ref.on('child_changed', function(childSnapshot, prevChildKey) { // code to handle child data changes.});

ref.on('child_removed', function(oldChildSnapshot) { // code to handle child removal.});

ref.push({ 'editor_id': 'leggetter', 'text': 'Nexmo Rocks!' });

48 / 105

@leggetter

Data Sync

// client

var ref = new Firebase("https://app.firebaseio.com/doc1/lines");

ref.on('child_added', function(childSnapshot, prevChildKey) { // code to handle new child.});

ref.on('child_changed', function(childSnapshot, prevChildKey) { // code to handle child data changes.});

ref.on('child_removed', function(oldChildSnapshot) { // code to handle child removal.});

ref.push({ 'editor_id': 'leggetter', 'text': 'Nexmo Rocks!' });

Framework handles updates to other clients

48 / 105

@leggetter

RMI (aka RPC)49 / 105

@leggetter

50 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

51 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

chat.client.broadcastMessage = function (name, message) { // handle message

};

51 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

chat.client.broadcastMessage = function (name, message) { // handle message

};

chat.server.send( 'me', 'hello world' );

51 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

chat.client.broadcastMessage = function (name, message) { // handle message

};

chat.server.send( 'me', 'hello world' );

$.connection.hub.start(); // async

51 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

chat.client.broadcastMessage = function (name, message) {

// handle message

};

chat.server.send( 'me', 'hello world' );

$.connection.hub.start(); // async

// server

public class ChatHub : Hub

{

51 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

chat.client.broadcastMessage = function (name, message) {

// handle message

};

chat.server.send( 'me', 'hello world' );

$.connection.hub.start(); // async

// server

public class ChatHub : Hub

{

public void Send(string name, string message)

{

51 / 105

@leggetter

RMI

// client

var chat = $.connection.chatHub;

chat.client.broadcastMessage = function (name, message) {

// handle message

};

chat.server.send( 'me', 'hello world' );

$.connection.hub.start(); // async

// server

public class ChatHub : Hub

{

public void Send(string name, string message)

{

// Call the broadcastMessage method to update clients.

Clients.All.broadcastMessage(name, message);

}

}

51 / 105

@leggetter

52 / 105

@leggetter

53 / 105

@leggetter

54 / 105

@leggetter

55 / 105

@leggetter

56 / 105

@leggetter

6. Deployment & ArchitectureConsiderations

57 / 105

@leggetter

Codehttps://github.com/leggetter/realtime-dotnet-examplesShort link: http://j.mp/rt-dotnet-ex

58 / 105

@leggetter

Self Hosted (Tightly Coupled) 59 / 105

@leggetter

.NET Self-Hosted Real-Time options

SignalRXSockets

60 / 105

@leggetter

61 / 105

@leggetter

Self-Hosted Demo 1: ASP.NET + SignalR (Tightly Coupled)61 / 105

@leggetter

What we'll look at:

PM> Install-Package Microsoft.AspNet.SignalR

Scripts\jquery.signalR*.js

App_Start\SignalRStartup.cs

Controllers\HomeController.cs

Hubs\ChatHub.cs

Views\Home\SignalR.cshtml

Script\chat\SignalRChat.js

62 / 105

@leggetter

Pros

.NET

Simple integration

MS Supported

jQuery Dependency

Cons

Tightly coupled

RMI only

Self-Scaling

Scaling (realtime + HTTP)

Self-Hosted Demo 1: Pro & Cons

63 / 105

@leggetter

64 / 105

@leggetter

Self-Hosted Demo 2: ASP.NET + XSockets (Tightly Coupled)64 / 105

@leggetter

What we'll look at:

PM> Install-Package XSockets

App_Start\XSocketsStartup.cs

Controllers\HomeController.cs

XSockets\ChatController.cs

Views\Home\XSockets.cshtml

Scripts\XSockets.latest.js

Script\chat\XSocketsChat.cs

65 / 105

@leggetter

Pros

.NET

Simple integration

Communication patterns

PubSub/Evented

RMI

Licensed

Cons

Tightly coupled

Self-Scaling

Scaling (realtime + HTTP)

Licensed

Self-Hosted Demo 2: Pro & Cons

66 / 105

@leggetter

Self-Hosted: .NET + Message Queue (Loosely Coupled)67 / 105

@leggetter

68 / 105

@leggetter

Pros

.NETMaps well to PubSubLoosely coupledCould use another runtime

Cons

How does it fit with RMI/SignalR?Multiple componentsSelf-scalingQueue routing questionsIn: HTTP. Out: WebSocket

Self-Hosted: .NET + Message Queue - Pro &

Cons

69 / 105

@leggetter

If SignalR or XSockets aren't a good fityou may have to look at a non-.NET solution

70 / 105

@leggetter

.NET Hosted Real-Time options

AblyFirebaseFanoutPubNubPusherRealtime.coSyncano

71 / 105

@leggetter

72 / 105

@leggetter

Hosted Demo: PubNub72 / 105

@leggetter

What we'll look at:

PM> Install-Package Pubnub

Controllers\ChatController.cs

Views\Home\PubNub.cshtml

Script\chat\PubNub.js

PubNub Real-Time Analytics

73 / 105

@leggetter

Pros

Simple & powerfulInstantly scalableManaged & dedicatedDirect integration. No overhead.

Cons

3rd party relianceDifficult to influence functionality

Hosted - Pros & Cons

74 / 105

@leggetter

Why use a hosted service?Scenario

1. Site average of 10,000 Users

75 / 105

@leggetter

Why use a hosted service?Scenario

1. Site average of 10,000 Users2. Over 1 Hour, no polling

75 / 105

@leggetter

Why use a hosted service?Scenario

1. Site average of 10,000 Users2. Over 1 Hour, no polling3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,000

75 / 105

@leggetter

Why use a hosted service?Scenario

1. Site average of 10,000 Users2. Over 1 Hour, no polling3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,0004. That's it! Total: 50,000

75 / 105

@leggetter

Why use a hosted service?Scenario

1. Site average of 10,000 Users2. Over 1 Hour, no polling3. Requests from pages load + HTML, CSS, JS, Images for 10k users = 50,0004. That's it! Total: 50,000

Your servers handle 50k requests per hour instead of 3.6M

You offload the polling or persistent connections to the service

75 / 105

@leggetter

7. Self-Hosted v Hosted

"Build vs. Buy"

76 / 105

@leggetter

Build vs. Buy - Costs

baremetrics.com/calculator

77 / 105

@leggetter

How do you choose?

7 Realtime Framework Considerations

1. Should you keep on polling?

2. Use an Existing Solution

3. Use a language you're comfortable with

4. Do you need multiple client device support?

5. Simple Messaging, PubSub/Evented, RMI or DataSync

6. Architectural considerations

7. Hosted v Self-Hosted (Build vs. Buy)

78 / 105

@leggetter

You need Real-Time!

There are lots of options.

Make the choice that's right for you.

I hope this helps!

79 / 105

@leggetter

Resources

Real-time Tech Guidegithub.com/leggetter/realtime-dotnet-examplesTools, Tips and Techniques for Developing Real-time AppsNexmo

80 / 105

@leggetter

Real-time Web Apps & .NET What are your options?

Questions?

PHIL @LEGGETTER

Head of Developer Relations

81 / 105

@leggetter

Future (If Time)

82 / 105

@leggetter

Network Infrastructure & Protocols

Reliability

Speed

Beyond HTTP

HTTP2

83 / 105

@leggetter

Bayeux

DDP

dNode

EPCP

GRIP

gRPC

MQTT

Pusher Protocol

STOMP

SignalR Protocol

WAMP (Web App Messaging Protocol)

XMPP (various)

Communication Pattern ProtocolStandardisation

84 / 105

@leggetter

85 / 105

@leggetter

Firebase

GitHub

Iron.io

MailChimp

MailJet

PagerDuty

Nexmo

SendGrid

Real-Time APIs

86 / 105

@leggetter

87 / 105

@leggetter

More "Things"!

88 / 105

@leggetter

The Physical Web89 / 105

@leggetter

IoT, Apps & Developers

90 / 105

@leggetter

A thing can be anything

91 / 105

@leggetter

A thing can be anything

SensorsAppliancesVehiclesSmart PhonesDevices (Arduino, Electric Imp, Raspberry Pi etc.)

91 / 105

@leggetter

A thing can be anything

SensorsAppliancesVehiclesSmart PhonesDevices (Arduino, Electric Imp, Raspberry Pi etc.)ServersBrowsersApps: Native, Web, running anywhere

91 / 105

@leggetter

The Majority of code we'll write will still befor "Apps"

ConfiguringMonitoringInteractingApp Logic

92 / 105

@leggetter

Real-Time Use Case Evolution

Notifications & SignallingActivity StreamsData Viz & PollsChatCollaborationMultiplayer Games

93 / 105

@leggetter

Notifications/Activity Streams -> Actions

94 / 105

@leggetterThe end of apps as we know it - Intercom

Subscriptions

95 / 105

@leggetter

Personalised Event Streams

96 / 105

@leggetter

Unified UIs

97 / 105

@leggetter

Chat & Bots for Everything and the rise of the .ai domain 98 / 105

@leggetter

600M MAUs10M integrationsapp-within-an-app modeltaxi, order food, tickets, games etc.

WeChat

99 / 105

@leggetter

Chat Integrations100 / 105

@leggetter

SiriGoogle Now

Microso� CortanaFacebook M

Chat "Virtual Assistants"

101 / 105

@leggetter

102 / 105

@leggetter

Chat has evolved. Chat is now a platform!

103 / 105

@leggetter

Multi-Device Experiences

104 / 105

@leggetter

Ben Foxall - A conceptual future for the multi-device web (FutureJS 2014)

105 / 105

@leggetter