Streamdata.io
Efficiently Turn APIs into Real-time Experiences
In ADSL, first useful piece of data in 60ms (HTTPS) In 4G / LTE, first useful piece of data in 300 ms (450 ms in 3G)
static content
small size objects
small # of objects
dynamic content
larger size objects
more objects
Matthew Yohe [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0) or GFDL (http://www.gnu.org/copyleft/fdl.html)], via Wikimedia Commons
more objects
even more larger objects
Web page:
~ 2 Mb
~ 38 TCP connections
source: http://httparchive.org
.css
.png, .jpg
.html, .js
By Kriplozoik CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)], via Wikimedia Commons
#home {
left: 0px;
width: 46px;
background: url('img_navsprites.gif') 0 0;
}
#prev {
left: 63px;
width: 43px;
background: url('img_navsprites.gif') -47px 0;
}
#next {
left: 129px;
width: 43px;
background: url('img_navsprites.gif') -91px 0;
}
<img src="data:image/png;base64,iVBORw0KGgoAAA
...E2AAA7JN+/OU8bK5CYII="
alt="html_inline_alpesjug.png"
title="html_inline_alpesjug.png">
1 change => invalidate all the cache
<html>
<head>
<title>AlpesJUG</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta content="width=device-width,initial-scale=1" name="viewport">
<link rel="stylesheet" href="assets/styles/main-f11f93b619.css">
<link rel="stylesheet" href="assets/styles/theme-564f6756da.css">
<link rel="stylesheet" href="assets/styles/ie-d41d8cd98f.css">
</head>
<body>...</body>
<script src="scripts/vendor-a6d64f3f39.js"></script>
<script src="scripts/app-0d43bb3b4b.js"></script>
<script src="scripts/theme-439fdb6f87.js">
</html>
static content
small size objects
small # of objects
dynamic content
larger size objects
more objects
more objects
even more larger objects
mobile devices
Matthew Yohe [CC BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0) or GFDL (http://www.gnu.org/copyleft/fdl.html)], via Wikimedia Commons
RFC 7540
Multiplexing of requests
concurrent requests
1 TCP connection
streams: bidirectional frames
source: http://www.splicemarketing.co.uk/_blog/Splice_Blog/post/the-arrival-of-http2/#.VlrbuiCrSV4
Method: GET
Scheme: HTTPS
Accept: image/webp,image/*,*/*;q=0.8
Accept-Language: en-US,en
Path: /resource
Host: http://www.alpesjug.org/
User-Agent: Mozilla/5.0
Path: /images
optional TLS (but for all browsers, it's mandatory)
TLS 1.2 min
no compression
no renegociation
stronger crypto
<img src="data:image/png;base64,iVBORw0KGgoAAA
...E2AAA7JN+/OU8bK5CYII="
alt="html_inline_alpesjug.png"
title="html_inline_alpesjug.png">
<html>
<head>
<title>AlpesJUG</title>
<link rel="stylesheet" href="assets/styles/main-f11f93b619.css">
<link rel="stylesheet" href="assets/styles/theme-564f6756da.css">
<link rel="stylesheet" href="assets/styles/ie-d41d8cd98f.css">
</head>
<body>...</body>
<script src="scripts/vendor-a6d64f3f39.js"></script>
<script src="scripts/app-0d43bb3b4b.js"></script>
<script src="scripts/theme-439fdb6f87.js">
</html>
source: https://github.com/http2/http2-spec/wiki/Implementations
1001010101011 01100011010
000010101011010 101010101011
0110100101110010 01010111111110001010
01011101010111 10101010101
00000101010110 101010110101
OSFA API
(One Size Fits All API)
optimized for none of the devices
chatty API (lots of calls)
single items | multiple items | |
---|---|---|
synchronous | Object | Iterable |
asynchronous | Future | Observable |
Marble diagram
def Observable<Map> getVideos(userId) {
return VideoService.getVideos(userId)
// we only want the first 10 of each list
.take(10)
.flatMap({ Video video ->
def m = video.getMetaData();
def b = video.getBookmark();
def r = video.getRating();
// compose these together
return Observable.zip(m, b, r {
metadata, bookmark, rating ->
// no transform to complete dictionary
// of data we want for each Video
return [id: video.videoId]
<< metadata << bookmark << rating
})
})
}
The "zip" operator combines the 3 asynchronous Observables into 1
http://netflix.com/genreList/1249
http://netflix.com/genreList/1249?titleprops=name,ratings
http://netflix.com/genreList/1249?rowOffset=0&rowSize=5&titleprops=name,boxshot
http://netflix.com/genreList/1249/setRating?titleId=5&view=movieDetailPage
/model.json
https://...
/model.json
Cache
Model
HTTP
D
a
t
a
S
o
u
r
c
e
Router DataSource
Client
View
Middleware
Recommandation Service
Title
Service
Rating
Service
Express
source: http://techblog.netflix.com/2015/08/making-netflixcom-faster.html
Pull too fast: waste resources
Pull at the wrong moment: get blocked
Pull too slow: run behind
source: Erik Meijer https://www.youtube.com/watch?v=FvMuPtuvP5w
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
GET /stream HTTP/1.1 1
Host: example.com
Accept: text/event-stream
proxies and load balancers reconfiguration
No need to reconfigure!
(it's HTTP)
var websocket =
new WebSocket('ws://websocketserver/echo');
websocket.onopen = function () {
...
};
websocket.onmessage = function (e) {
...
};
websocket.onerror = function (error) {
...
};
var eventSource =
new EventSource('http://sseserver/echo');
eventSource.onopen = function () {
...
};
eventSource.onmessage = function (e) {
...
};
eventSource.onerror = function (error) {
...
};
eventSource.addEventListener('foo', function(e) {
...
}, false);
Use case: Préchargement de 500 Tweets sur une page web (nginx configuré en tant que proxy)
chat, chat, chat !
Share living editing
GPS GoogleMap-like
Games
Fintech / Trading
Betting
Games
Realtime timetables
Animated data apps (charts, monitoring, etc.)
10,000 concurrent clients
[{"title":"Value 0","price":66,"param1":"1","param2":"22","param3":"33"},
{"title":"Value 1","price":63,"param1":"11","param2":"2","param3":"53"},
{"title":"Value 2","price":85,"param1":"1","param2":"22","param3":"33"},
{"title":"Value 3","price":21,"param1":"31","param2":"12","param3":"4"},
{"title":"Value 4","price":10,"param1":"151","param2":"22","param3":"33"},
{"title":"Value 5","price":6,"param1":"11","param2":"21","param3":"33"},
{"title":"Value 6","price":60,"param1":"11","param2":"222","param3":"33"}]
[{"op":"replace","path":"/2/price","value":5},
{"op":"replace","path":"/3/param2","value":"32"}]
[{"title":"Value 0","price":66,"param1":"1","param2":"22","param3":"33"},
{"title":"Value 1","price":63,"param1":"11","param2":"2","param3":"53"},
{"title":"Value 2","price":5,"param1":"1","param2":"22","param3":"33"},
{"title":"Value 3","price":21,"param1":"31","param2":"32","param3":"4"},
{"title":"Value 4","price":10,"param1":"151","param2":"22","param3":"33"},
{"title":"Value 5","price":6,"param1":"11","param2":"21","param3":"33"},
{"title":"Value 6","price":60,"param1":"11","param2":"222","param3":"33"}]
Push, don't poll
Dynamic
Cache
Incremental
Data
0.5s is the latency objective
“The more others invest in amazing UI, the more yours seems lousy”
Einstein « Relativity concepts applied to UI », OpenRoadMedia, 1937
The world is more and more dynamic and we've got the tools
Be ready to animate data, become streamers!
By Streamdata.io