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/1249http://netflix.com/genreList/1249?titleprops=name,ratingshttp://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: 13GET /stream HTTP/1.1 1
Host: example.com
Accept: text/event-streamproxies 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