SlideShare a Scribd company logo
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/

More Related Content

ODP
Using Websockets in Play !
PDF
WebSockets wiith Scala and Play! Framework
PPTX
Web sockets in Java
PPTX
Asynchronous Web Programming with HTML5 WebSockets and Java
PDF
WebSockets with Spring 4
PDF
Building Next Generation Real-Time Web Applications using Websockets
KEY
Pushing the web — WebSockets
KEY
The HTML5 WebSocket API
Using Websockets in Play !
WebSockets wiith Scala and Play! Framework
Web sockets in Java
Asynchronous Web Programming with HTML5 WebSockets and Java
WebSockets with Spring 4
Building Next Generation Real-Time Web Applications using Websockets
Pushing the web — WebSockets
The HTML5 WebSocket API

What's hot (20)

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

Similar to Using Websockets with Play! (20)

PPTX
Understanding Akka WebSockets A Comprehensive Guide
PPTX
Understanding Akka WebSockets A Comprehensive Guide.pptx
PPTX
Real time websites and mobile apps with SignalR
PDF
Real-Time with Flowdock
PPTX
Doppio: Breaking the Browser Language Barrier
PPT
PPT
Get Real: Adventures in realtime web apps
PDF
From polling to real time: Scala, Akka, and Websockets from scratch
PPTX
WebSockets in JEE 7
PPTX
Enhancing Mobile User Experience with WebSocket
PDF
Building and Scaling a WebSockets Pubsub System
PDF
Comet with node.js and V8
PDF
Getting Started with WebSocket and Server-Sent Events using Java by Arun Gupta
PPTX
Client server chat application
PPTX
Realtime web experience with signalR
PDF
Getting started with Websocket and Server-sent Events using Java - Arun Gupta
PPT
Leveraging zeromq for node.js
PDF
Getting Started with WebSocket and Server-Sent Events in Java
PPTX
Realtime web experience with signal r
PDF
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
Understanding Akka WebSockets A Comprehensive Guide
Understanding Akka WebSockets A Comprehensive Guide.pptx
Real time websites and mobile apps with SignalR
Real-Time with Flowdock
Doppio: Breaking the Browser Language Barrier
Get Real: Adventures in realtime web apps
From polling to real time: Scala, Akka, and Websockets from scratch
WebSockets in JEE 7
Enhancing Mobile User Experience with WebSocket
Building and Scaling a WebSockets Pubsub System
Comet with node.js and V8
Getting Started with WebSocket and Server-Sent Events using Java by Arun Gupta
Client server chat application
Realtime web experience with signalR
Getting started with Websocket and Server-sent Events using Java - Arun Gupta
Leveraging zeromq for node.js
Getting Started with WebSocket and Server-Sent Events in Java
Realtime web experience with signal r
Real-Time Web Apps & .NET. What Are Your Options? NDC Oslo 2016
Ad

Recently uploaded (20)

PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Approach and Philosophy of On baking technology
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Electronic commerce courselecture one. Pdf
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
cuic standard and advanced reporting.pdf
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Encapsulation theory and applications.pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Encapsulation_ Review paper, used for researhc scholars
PPT
Teaching material agriculture food technology
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
Understanding_Digital_Forensics_Presentation.pptx
Spectral efficient network and resource selection model in 5G networks
Approach and Philosophy of On baking technology
NewMind AI Weekly Chronicles - August'25 Week I
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Digital-Transformation-Roadmap-for-Companies.pptx
Advanced methodologies resolving dimensionality complications for autism neur...
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Electronic commerce courselecture one. Pdf
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Diabetes mellitus diagnosis method based random forest with bat algorithm
cuic standard and advanced reporting.pdf
Network Security Unit 5.pdf for BCA BBA.
Encapsulation theory and applications.pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Encapsulation_ Review paper, used for researhc scholars
Teaching material agriculture food technology
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Understanding_Digital_Forensics_Presentation.pptx

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 */)