SlideShare ist ein Scribd-Unternehmen logo
1 von 77
Downloaden Sie, um offline zu lesen
Building real-time web
apps using WEBSOcKeTS
WITH PLAY!
andrew@42go.com
@connerdelights
How we
used to
do it
request
How we
used to
do it
request
response
How we
used to
do it
request
response
new message!
How we
used to
do it
request
response
new message!
The web has
changed
(quite a bit)
The dynamic web
needs real-time
communication
The dynamic web
needs real-time
communication
short polling
short polling
short polling
short polling
Resource intensive,slow, limited toone response
Chunked Responses
Chunked Responses
Hacky,
no error handling,half-duplex
Long Polling
Long Polling
Long Polling
Long Polling
well supported,still half-duplex,single req/resp
These do not handle high
bursts of messages
These do not handle high
bursts of messages
Stuck with the single
request → response
model
GET / HTTP/1.1
Host: www.google.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
28.0.1500.71 Safari/537.36
DNT: 1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: PREF=ID=e248d326d84eb3dc:FF=0:TM=1372622071:LM=1274622070:S=2bERIaHgSKRjWeC8; NID=47=CHAZZVkq40TcovIu-
FuXlU0pF2UPfqqSEhNqx8hqUKnZ7-s4uxGjtBEFK7kRtTSVEu4fzJ00vhB4OrLRxw8JfV5EuiKczEC2_EHkBqr1kNwn_NdZ73XRl2umFybXYoiVD_
HTTP/1.1 200 OK
Date: Tue, 23 Jul 2013 23:28:02 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=e248d326d84eb3dc:FF=1997f24999d1d9ef:FF=0:TM=1334622374:LM=1374622082:S=ynynJppwL64C6VMRU;
expires=Thu, 23-Jul-2015 23:28:02 GMT; path=/; domain=.google.com
Content-Encoding: gzip
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Transfer-Encoding: chunked
Inefficient
Over 1kb in headers
Can
Play! do
these?
Can
Play! do
these?
Yes!
Can
Play! do
these?
In fact, if
you’re
supporting
older
browsers,
consider
them!
Yes!
Websockets
Websockets
Full duplex,efficient, fast,only newer* browsers
How fast are
websockets?
our office
EC2
US West
How fast are
websockets?
our office
EC2
US West
Average 10ms ping
~13ms mean improvement
Full benchmarks:
http://eng.42go.com/
GET / HTTP/1.1
Host: www.google.com
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
28.0.1500.71 Safari/537.36
DNT: 1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: PREF=ID=e248d326d84eb3dc:FF=0:TM=1372622071:LM=1274622070:S=2bERIaHgSKRjWeC8; NID=47=CHAZZVkq40TcovIu-
FuXlU0pF2UPfqqSEhNqx8hqUKnZ7-s4uxGjtBEFK7kRtTSVEu4fzJ00vhB4OrLRxw8JfV5EuiKczEC2_EHkBqr1kNwn_NdZ73XRl2umFybXYoiVD_
HTTP/1.1 200 OK
Date: Tue, 23 Jul 2013 23:28:02 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=UTF-8
Set-Cookie: PREF=ID=e248d326d84eb3dc:FF=1997f24999d1d9ef:FF=0:TM=1334622374:LM=1374622082:S=ynynJppwL64C6VMRU;
expires=Thu, 23-Jul-2015 23:28:02 GMT; path=/; domain=.google.com
Content-Encoding: gzip
Server: gws
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Transfer-Encoding: chunked
Over 1kb in headers
How fast are
websockets?
Initial Websocket
transmission
Websocket
Client
HTTP
Client
How fast are
websockets?
EC2
US West
TCP Websocket
0.002ms 0.02ms
Websockets are
not magical :)
Full benchmarks:
http://eng.42go.com/
How fast are
websockets?
EC2
US West
TCP Websocket
0.002ms 0.02ms
Still quite fast
Full benchmarks:
http://eng.42go.com/
Websockets
deal with
streams of data
from the client
to the client
What are my messages?
Here’s Your messages!
You have a new notification
from the client
to the client
What are my messages?
Here’s Your messages!
You have a new notification
I visited my user profile page
I visited the home page
from the client
to the client
What are my messages?
Here’s Your messages!
You have a new notification
I visited my user profile page
I visited the home page
Here’s a Message for Frank
Got your message
from the client
to the client
What are my messages?
Here’s Your messages!
You have a new notification
I visited my user profile page
I visited the home page
Here’s a Message for Frank
Got your message
Code push, reconnect please!
from the client
to the client
How does
Play! handle
websockets?
How does
Play! handle
websockets?
let’s step back a bit...
val it = Seq(1,2,3,4,5).toIterator
while(it.hasNext) {
println(it.next())
}
Iterators
val it = Seq(1,2,3,4,5).toIterator
while(it.hasNext) {
println(it.next())
}
val list = Seq(1,2,3,4,5)
list.foreach(println)
Iterators
val list = Seq(1,2,3,4,5)
list.foreach(println)
Instead of imperatively traversing containers,
apply a function to elements in a container.
Iterators
val list = Seq(1,2,3,4,5)
list.foreach(println)
Container Function
Iterators
Iterators
val list = Seq(1,2,3,4,5)
list.foreach(println)
Container Function
Producer Consumer
Iteratee
(ie, the consumer)
Immutably consumes chunks from a Producer.
Think: Iterates over a stream*.
Iteratee
(ie, the consumer)
Immutably consumes chunks from a Producer.
Think: Iterates over a stream*.
Iteratees can actually do a bit more,
but we’ll save that for another day.
Enumerator
(ie, the PRODUCER)
Produces typed chunks.
Only produces when there is a CONSUMER.
Play! needs a producer and
consumer for a Websocket stream
val in = Iteratee.foreach[JsArray](println)
val in = Iteratee.foreach[JsArray](println)
Consumer
This iteratee will consume from an Enumerator
(the client), printing the input to the console
val in = Iteratee.foreach[JsArray](println)
val out = Enumerator("You connected!").andThen(Enumerator.eof)
Producer
This Enumerator will be consumed by an iteratee
(The client), sending a string then EOF
def index = WebSocket.using[JsArray] { request =>
val in = Iteratee.foreach[JsArray](println)
val out = Enumerator("You connected!").andThen(Enumerator.eof)
(in, out)
}
def index = WebSocket.using[JsArray] { request =>
val in = Iteratee.foreach[JsArray](println)
val out = Enumerator("You connected!").andThen(Enumerator.eof)
(in, out)
}
It works!
> var createSocket = function() {
var s = new WebSocket("ws://localhost:9000");
s.onopen = function() { s.send("['hey!']"); }
s.onclose = function(e) { console.log("closed!",e); }
s.onmessage = function(msg) { console.log("got: ", msg.data); }
return s;
}
undefined
> var socket = createSocket()
undefined
got: You connected!
closed!
CloseEvent {reason: "", code: 1005, wasClean: true, clipboardData:
def echo = WebSocket.using[JsArray] { request =>
val (out, channel) = Concurrent.broadcast[JsArray]
val in = Iteratee.foreach[JsArray](channel.push)
(in, out)
}
def echo = WebSocket.using[JsArray] { request =>
val (out, channel) = Concurrent.broadcast[JsArray]
val in = Iteratee.foreach[JsArray](channel.push)
(in, out)
}
feeds into
Channels let us
imperatively push data
into an enumerator
Channels
Channels...
how about a chat room?
super simple
^
Simplified version of the Play! Chat room
websocket sample
https://github.com/playframework/playframework/blob/master/samples/scala/websocket-chat
case class Join(username: String)
case class Quit(username: String)
case class Talk(username: String, text: String)
class ChatRoom extends Actor {
var members = Set.empty[String]
val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]
val chatBot = "Marvin"
 
def receive = ???
 
}
case class Join(username: String)
case class Quit(username: String)
case class Talk(username: String, text: String)
class ChatRoom extends Actor {
var members = Set.empty[String]
val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]
val chatBot = "Marvin"
 
def receive = ???
 
} channel
output
lets us push data
into the enumerator
case class Join(username: String)
case class Quit(username: String)
case class Talk(username: String, text: String)
class ChatRoom extends Actor {
var members = Set.empty[String]
val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]
val chatBot = "Marvin"
 
def receive = {
case Join(username) => {
members = members + username
broadcastMessage(chatBot, s"$username has joined")
sender ! chatEnumerator
}
case Quit(username) =>
case Talk(username, text) =>
}
 
def broadcastMessage(user: String, text: String): Unit = ???
}
case class Join(username: String)
case class Quit(username: String)
case class Talk(username: String, text: String)
class ChatRoom extends Actor {
var members = Set.empty[String]
val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]
val chatBot = "Marvin"
 
def receive = {
case Join(username) => {
members = members + username
broadcastMessage(chatBot, s"$username has joined")
sender ! chatEnumerator
}
case Quit(username) =>
case Talk(username, text) =>
}
 
def broadcastMessage(user: String, text: String): Unit = ???
}
send back a reference
to the chatroom’s enumerator
case class Join(username: String)
case class Quit(username: String)
case class Talk(username: String, text: String)
class ChatRoom extends Actor {
var members = Set.empty[String]
val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue]
val chatBot = "Marvin"
 
def receive = {
case Join(username) => {
members = members + username
broadcastMessage(chatBot, s"$username has joined")
sender ! chatEnumerator
}
case Quit(username) => {
broadcastMessage(chatBot, s"$username has left")
members = members - username
}
case Talk(username, text) => broadcastMessage(username, text)
}
 
def broadcastMessage(user: String, text: String): Unit = ???
}
case class Join(username: String)
case class Quit(username: String)
case class Talk(username: String, text: String)
class ChatRoom extends Actor {
...
 
def broadcastMessage(user: String, text: String): Unit = {
val msg = Json.obj("user" -> JsString(user),
"message" -> JsString(text),
"members" -> JsArray(members.toList.map(JsString)))
chatChannel.push(msg)
}
}
object Application extends Controller {
lazy val chatroomActor = Akka.system.actorOf(Props[ChatRoom])
 
def chat(username: String) = WebSocket.async[JsValue] { request =>
(chatroomActor ? Join(username)) map {
// grab the Enumerator from ChatRoom:
case out: Enumerator[JsValue] =>
val in = Iteratee.foreach[JsValue] { event =>
chatroomActor ! Talk(username, (event  "text").as[String])
}.mapDone { _ =>
chatroomActor ! Quit(username)
}
(in, out)
}
}
}
This can be modified
to be a lightweight
pub-sub system by
creating many
channels
Play!
Lessons learned
use debugging tools!
Lessons learned
use debugging tools!
Lessons learned
weird things can happen when you’re
depending on a long-lived socket
Lessons learned
weird things can happen when you’re
depending on a long-lived socket
clients lie voodoo when
packet loss is high
disconnects
happen
Lessons learned
babysit connections in your application
ping / pong
Lessons learned
your clients need to auto-reconnect
(we’ll be open sourcing our
reconnecting WS library soon!)
Lessons learned
be careful about thread safety
val in = Iteratee.foreach[JsArray](/* handler */)
Lessons learned
make sure this guy is fast
val in = Iteratee.foreach[JsArray](/* handler */)
http://caniuse.com/websockets
http://caniuse.com/websockets
Find me:
andrew@42go.com
@connerdelights
Questions?
http://eng.42go.com/

Weitere ähnliche Inhalte

Was ist angesagt?

HTML5 vs Silverlight
HTML5 vs SilverlightHTML5 vs Silverlight
HTML5 vs Silverlight
Matt Casto
 
0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services
Ilya Grigorik
 
Asterisk, HTML5 and NodeJS; a world of endless possibilities
Asterisk, HTML5 and NodeJS; a world of endless possibilitiesAsterisk, HTML5 and NodeJS; a world of endless possibilities
Asterisk, HTML5 and NodeJS; a world of endless possibilities
Dan Jenkins
 
Websockets in Node.js - Making them reliable and scalable
Websockets in Node.js - Making them reliable and scalableWebsockets in Node.js - Making them reliable and scalable
Websockets in Node.js - Making them reliable and scalable
Gareth Marland
 
Tornado in Depth
Tornado in DepthTornado in Depth
Tornado in Depth
Òscar Vilaplana
 

Was ist angesagt? (20)

Dancing with websocket
Dancing with websocketDancing with websocket
Dancing with websocket
 
Intro to WebSockets
Intro to WebSocketsIntro to WebSockets
Intro to WebSockets
 
HTML5 vs Silverlight
HTML5 vs SilverlightHTML5 vs Silverlight
HTML5 vs Silverlight
 
Websockets and SockJS, Real time chatting
Websockets and SockJS, Real time chattingWebsockets and SockJS, Real time chatting
Websockets and SockJS, Real time chatting
 
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
Handling 10k requests per second with Symfony and Varnish - SymfonyCon Berlin...
 
Real time web (Orbited) at BCNE3
Real time web (Orbited) at BCNE3Real time web (Orbited) at BCNE3
Real time web (Orbited) at BCNE3
 
Implementing Comet using PHP
Implementing Comet using PHPImplementing Comet using PHP
Implementing Comet using PHP
 
Solving anything in VCL
Solving anything in VCLSolving anything in VCL
Solving anything in VCL
 
Websockets on the JVM: Atmosphere to the rescue!
Websockets on the JVM: Atmosphere to the rescue!Websockets on the JVM: Atmosphere to the rescue!
Websockets on the JVM: Atmosphere to the rescue!
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...
 
GWT Web Socket and data serialization
GWT Web Socket and data serializationGWT Web Socket and data serialization
GWT Web Socket and data serialization
 
Reverse ajax in 2014
Reverse ajax in 2014Reverse ajax in 2014
Reverse ajax in 2014
 
0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services0-60 with Goliath: Building High Performance Ruby Web-Services
0-60 with Goliath: Building High Performance Ruby Web-Services
 
About Node.js
About Node.jsAbout Node.js
About Node.js
 
Asterisk, HTML5 and NodeJS; a world of endless possibilities
Asterisk, HTML5 and NodeJS; a world of endless possibilitiesAsterisk, HTML5 and NodeJS; a world of endless possibilities
Asterisk, HTML5 and NodeJS; a world of endless possibilities
 
Beyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisBeyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic Analysis
 
Websockets in Node.js - Making them reliable and scalable
Websockets in Node.js - Making them reliable and scalableWebsockets in Node.js - Making them reliable and scalable
Websockets in Node.js - Making them reliable and scalable
 
Going Live! with Comet
Going Live! with CometGoing Live! with Comet
Going Live! with Comet
 
Time for Comet?
Time for Comet?Time for Comet?
Time for Comet?
 
Tornado in Depth
Tornado in DepthTornado in Depth
Tornado in Depth
 

Ähnlich wie Using Websockets with Play!

Pushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax WPushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax W
rajivmordani
 
Interactive web. O rly?
Interactive web. O rly?Interactive web. O rly?
Interactive web. O rly?
timbc
 
Realtime Communication Techniques with PHP
Realtime Communication Techniques with PHPRealtime Communication Techniques with PHP
Realtime Communication Techniques with PHP
WaterSpout
 
Web Sockets are not Just for Web Browsers
Web Sockets are not Just for Web BrowsersWeb Sockets are not Just for Web Browsers
Web Sockets are not Just for Web Browsers
cjmyers
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...
Tom Croucher
 

Ähnlich wie Using Websockets with Play! (20)

WebSockets in JEE 7
WebSockets in JEE 7WebSockets in JEE 7
WebSockets in JEE 7
 
Get Real: Adventures in realtime web apps
Get Real: Adventures in realtime web appsGet Real: Adventures in realtime web apps
Get Real: Adventures in realtime web apps
 
08 ajax
08 ajax08 ajax
08 ajax
 
Designing for Distributed Systems with Reactor and Reactive Streams
Designing for Distributed Systems with Reactor and Reactive StreamsDesigning for Distributed Systems with Reactor and Reactive Streams
Designing for Distributed Systems with Reactor and Reactive Streams
 
Building interactivity with websockets
Building interactivity with websocketsBuilding interactivity with websockets
Building interactivity with websockets
 
Message in a Bottle
Message in a BottleMessage in a Bottle
Message in a Bottle
 
Pushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax WPushing Datatothe Browserwith Comet Ajax W
Pushing Datatothe Browserwith Comet Ajax W
 
Interactive web. O rly?
Interactive web. O rly?Interactive web. O rly?
Interactive web. O rly?
 
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
 
Enhancing Mobile User Experience with WebSocket
Enhancing Mobile User Experience with WebSocketEnhancing Mobile User Experience with WebSocket
Enhancing Mobile User Experience with WebSocket
 
Realtime Communication Techniques with PHP
Realtime Communication Techniques with PHPRealtime Communication Techniques with PHP
Realtime Communication Techniques with PHP
 
Android dev 3
Android dev 3Android dev 3
Android dev 3
 
Comet from JavaOne 2008
Comet from JavaOne 2008Comet from JavaOne 2008
Comet from JavaOne 2008
 
Tornado web
Tornado webTornado web
Tornado web
 
Web Sockets are not Just for Web Browsers
Web Sockets are not Just for Web BrowsersWeb Sockets are not Just for Web Browsers
Web Sockets are not Just for Web Browsers
 
Vert.x for Microservices Architecture
Vert.x for Microservices ArchitectureVert.x for Microservices Architecture
Vert.x for Microservices Architecture
 
Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code
Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of codeSetup ephemeral password for TURN, Learn RTC in less than 200 Lines of code
Setup ephemeral password for TURN, Learn RTC in less than 200 Lines of code
 
Opa hackathon
Opa hackathonOpa hackathon
Opa hackathon
 
A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...A language for the Internet: Why JavaScript and Node.js is right for Internet...
A language for the Internet: Why JavaScript and Node.js is right for Internet...
 
Message Queuing on a Large Scale: IMVUs stateful real-time message queue for ...
Message Queuing on a Large Scale: IMVUs stateful real-time message queue for ...Message Queuing on a Large Scale: IMVUs stateful real-time message queue for ...
Message Queuing on a Large Scale: IMVUs stateful real-time message queue for ...
 

Kürzlich hochgeladen

Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Kürzlich hochgeladen (20)

presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 

Using Websockets with Play!

  • 1. Building real-time web apps using WEBSOcKeTS WITH PLAY! andrew@42go.com @connerdelights
  • 2. How we used to do it request
  • 3. How we used to do it request response
  • 4. How we used to do it request response new message!
  • 5. How we used to do it request response new message!
  • 7. The dynamic web needs real-time communication
  • 8. The dynamic web needs real-time communication
  • 12. short polling Resource intensive,slow, limited toone response
  • 14. Chunked Responses Hacky, no error handling,half-duplex
  • 18. Long Polling well supported,still half-duplex,single req/resp
  • 19. These do not handle high bursts of messages
  • 20. These do not handle high bursts of messages Stuck with the single request → response model
  • 21. GET / HTTP/1.1 Host: www.google.com Connection: keep-alive Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ 28.0.1500.71 Safari/537.36 DNT: 1 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Cookie: PREF=ID=e248d326d84eb3dc:FF=0:TM=1372622071:LM=1274622070:S=2bERIaHgSKRjWeC8; NID=47=CHAZZVkq40TcovIu- FuXlU0pF2UPfqqSEhNqx8hqUKnZ7-s4uxGjtBEFK7kRtTSVEu4fzJ00vhB4OrLRxw8JfV5EuiKczEC2_EHkBqr1kNwn_NdZ73XRl2umFybXYoiVD_ HTTP/1.1 200 OK Date: Tue, 23 Jul 2013 23:28:02 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=UTF-8 Set-Cookie: PREF=ID=e248d326d84eb3dc:FF=1997f24999d1d9ef:FF=0:TM=1334622374:LM=1374622082:S=ynynJppwL64C6VMRU; expires=Thu, 23-Jul-2015 23:28:02 GMT; path=/; domain=.google.com Content-Encoding: gzip Server: gws X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN Transfer-Encoding: chunked Inefficient Over 1kb in headers
  • 24. Can Play! do these? In fact, if you’re supporting older browsers, consider them! Yes!
  • 27. How fast are websockets? our office EC2 US West
  • 28. How fast are websockets? our office EC2 US West Average 10ms ping ~13ms mean improvement Full benchmarks: http://eng.42go.com/
  • 29. GET / HTTP/1.1 Host: www.google.com Connection: keep-alive Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ 28.0.1500.71 Safari/537.36 DNT: 1 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Cookie: PREF=ID=e248d326d84eb3dc:FF=0:TM=1372622071:LM=1274622070:S=2bERIaHgSKRjWeC8; NID=47=CHAZZVkq40TcovIu- FuXlU0pF2UPfqqSEhNqx8hqUKnZ7-s4uxGjtBEFK7kRtTSVEu4fzJ00vhB4OrLRxw8JfV5EuiKczEC2_EHkBqr1kNwn_NdZ73XRl2umFybXYoiVD_ HTTP/1.1 200 OK Date: Tue, 23 Jul 2013 23:28:02 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=UTF-8 Set-Cookie: PREF=ID=e248d326d84eb3dc:FF=1997f24999d1d9ef:FF=0:TM=1334622374:LM=1374622082:S=ynynJppwL64C6VMRU; expires=Thu, 23-Jul-2015 23:28:02 GMT; path=/; domain=.google.com Content-Encoding: gzip Server: gws X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN Transfer-Encoding: chunked Over 1kb in headers
  • 30. How fast are websockets? Initial Websocket transmission Websocket Client HTTP Client
  • 31. How fast are websockets? EC2 US West TCP Websocket 0.002ms 0.02ms Websockets are not magical :) Full benchmarks: http://eng.42go.com/
  • 32. How fast are websockets? EC2 US West TCP Websocket 0.002ms 0.02ms Still quite fast Full benchmarks: http://eng.42go.com/
  • 33. Websockets deal with streams of data from the client to the client
  • 34. What are my messages? Here’s Your messages! You have a new notification from the client to the client
  • 35. What are my messages? Here’s Your messages! You have a new notification I visited my user profile page I visited the home page from the client to the client
  • 36. What are my messages? Here’s Your messages! You have a new notification I visited my user profile page I visited the home page Here’s a Message for Frank Got your message from the client to the client
  • 37. What are my messages? Here’s Your messages! You have a new notification I visited my user profile page I visited the home page Here’s a Message for Frank Got your message Code push, reconnect please! from the client to the client
  • 40. val it = Seq(1,2,3,4,5).toIterator while(it.hasNext) { println(it.next()) } Iterators
  • 41. val it = Seq(1,2,3,4,5).toIterator while(it.hasNext) { println(it.next()) } val list = Seq(1,2,3,4,5) list.foreach(println) Iterators
  • 42. val list = Seq(1,2,3,4,5) list.foreach(println) Instead of imperatively traversing containers, apply a function to elements in a container. Iterators
  • 43. val list = Seq(1,2,3,4,5) list.foreach(println) Container Function Iterators
  • 44. Iterators val list = Seq(1,2,3,4,5) list.foreach(println) Container Function Producer Consumer
  • 45. Iteratee (ie, the consumer) Immutably consumes chunks from a Producer. Think: Iterates over a stream*.
  • 46. Iteratee (ie, the consumer) Immutably consumes chunks from a Producer. Think: Iterates over a stream*. Iteratees can actually do a bit more, but we’ll save that for another day.
  • 47. Enumerator (ie, the PRODUCER) Produces typed chunks. Only produces when there is a CONSUMER.
  • 48. Play! needs a producer and consumer for a Websocket stream
  • 49. val in = Iteratee.foreach[JsArray](println)
  • 50. val in = Iteratee.foreach[JsArray](println) Consumer This iteratee will consume from an Enumerator (the client), printing the input to the console
  • 51. val in = Iteratee.foreach[JsArray](println) val out = Enumerator("You connected!").andThen(Enumerator.eof) Producer This Enumerator will be consumed by an iteratee (The client), sending a string then EOF
  • 52. def index = WebSocket.using[JsArray] { request => val in = Iteratee.foreach[JsArray](println) val out = Enumerator("You connected!").andThen(Enumerator.eof) (in, out) }
  • 53. def index = WebSocket.using[JsArray] { request => val in = Iteratee.foreach[JsArray](println) val out = Enumerator("You connected!").andThen(Enumerator.eof) (in, out) } It works! > var createSocket = function() { var s = new WebSocket("ws://localhost:9000"); s.onopen = function() { s.send("['hey!']"); } s.onclose = function(e) { console.log("closed!",e); } s.onmessage = function(msg) { console.log("got: ", msg.data); } return s; } undefined > var socket = createSocket() undefined got: You connected! closed! CloseEvent {reason: "", code: 1005, wasClean: true, clipboardData:
  • 54. def echo = WebSocket.using[JsArray] { request => val (out, channel) = Concurrent.broadcast[JsArray] val in = Iteratee.foreach[JsArray](channel.push) (in, out) }
  • 55. def echo = WebSocket.using[JsArray] { request => val (out, channel) = Concurrent.broadcast[JsArray] val in = Iteratee.foreach[JsArray](channel.push) (in, out) } feeds into Channels let us imperatively push data into an enumerator
  • 57. Channels... how about a chat room? super simple ^
  • 58. Simplified version of the Play! Chat room websocket sample https://github.com/playframework/playframework/blob/master/samples/scala/websocket-chat
  • 59. case class Join(username: String) case class Quit(username: String) case class Talk(username: String, text: String) class ChatRoom extends Actor { var members = Set.empty[String] val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue] val chatBot = "Marvin"   def receive = ???   }
  • 60. case class Join(username: String) case class Quit(username: String) case class Talk(username: String, text: String) class ChatRoom extends Actor { var members = Set.empty[String] val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue] val chatBot = "Marvin"   def receive = ???   } channel output lets us push data into the enumerator
  • 61. case class Join(username: String) case class Quit(username: String) case class Talk(username: String, text: String) class ChatRoom extends Actor { var members = Set.empty[String] val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue] val chatBot = "Marvin"   def receive = { case Join(username) => { members = members + username broadcastMessage(chatBot, s"$username has joined") sender ! chatEnumerator } case Quit(username) => case Talk(username, text) => }   def broadcastMessage(user: String, text: String): Unit = ??? }
  • 62. case class Join(username: String) case class Quit(username: String) case class Talk(username: String, text: String) class ChatRoom extends Actor { var members = Set.empty[String] val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue] val chatBot = "Marvin"   def receive = { case Join(username) => { members = members + username broadcastMessage(chatBot, s"$username has joined") sender ! chatEnumerator } case Quit(username) => case Talk(username, text) => }   def broadcastMessage(user: String, text: String): Unit = ??? } send back a reference to the chatroom’s enumerator
  • 63. case class Join(username: String) case class Quit(username: String) case class Talk(username: String, text: String) class ChatRoom extends Actor { var members = Set.empty[String] val (chatEnumerator, chatChannel) = Concurrent.broadcast[JsValue] val chatBot = "Marvin"   def receive = { case Join(username) => { members = members + username broadcastMessage(chatBot, s"$username has joined") sender ! chatEnumerator } case Quit(username) => { broadcastMessage(chatBot, s"$username has left") members = members - username } case Talk(username, text) => broadcastMessage(username, text) }   def broadcastMessage(user: String, text: String): Unit = ??? }
  • 64. case class Join(username: String) case class Quit(username: String) case class Talk(username: String, text: String) class ChatRoom extends Actor { ...   def broadcastMessage(user: String, text: String): Unit = { val msg = Json.obj("user" -> JsString(user), "message" -> JsString(text), "members" -> JsArray(members.toList.map(JsString))) chatChannel.push(msg) } }
  • 65. object Application extends Controller { lazy val chatroomActor = Akka.system.actorOf(Props[ChatRoom])   def chat(username: String) = WebSocket.async[JsValue] { request => (chatroomActor ? Join(username)) map { // grab the Enumerator from ChatRoom: case out: Enumerator[JsValue] => val in = Iteratee.foreach[JsValue] { event => chatroomActor ! Talk(username, (event "text").as[String]) }.mapDone { _ => chatroomActor ! Quit(username) } (in, out) } } }
  • 66. This can be modified to be a lightweight pub-sub system by creating many channels Play!
  • 69. Lessons learned weird things can happen when you’re depending on a long-lived socket
  • 70. Lessons learned weird things can happen when you’re depending on a long-lived socket clients lie voodoo when packet loss is high disconnects happen
  • 71. Lessons learned babysit connections in your application ping / pong
  • 72. Lessons learned your clients need to auto-reconnect (we’ll be open sourcing our reconnecting WS library soon!)
  • 73. Lessons learned be careful about thread safety val in = Iteratee.foreach[JsArray](/* handler */)
  • 74. Lessons learned make sure this guy is fast val in = Iteratee.foreach[JsArray](/* handler */)