Promise of Push (HTTP/2 Web Performance)
-
Upload
colin-bendell -
Category
Software
-
view
831 -
download
4
Transcript of Promise of Push (HTTP/2 Web Performance)
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 Web Performance
Promise of Push
@ColinBendellDirector, CTO Office
©2016 AKAMAI | FASTER FORWARD™
Desktop
90th50thBudget
Mobile
©2016 AKAMAI | FASTER FORWARD™
Improving Perf:
Blame the content
©2016 AKAMAI | FASTER FORWARD™
WebPageTest
Looking for culprits
©2016 AKAMAI | FASTER FORWARD™
Time-To-First-Byte
25% of Perf. Budget is waiting
Many causes: User (Network, Device, CPU), App Server, etc
©2016 AKAMAI | FASTER FORWARD™
Introducing
Network Waste1.48s (full throughput)
87.6% WASTE
©2016 AKAMAI | FASTER FORWARD™
Network Waste
An opportunity to PUSH content
Move the experience
87.6% WASTE
©2016 AKAMAI | FASTER FORWARD™
Chapter 1: PUSH on the Web
©2016 AKAMAI | FASTER FORWARD™
Direct TCPPoll/Long PollPushletsSSE
Hasn't Push been solved?
How to PUSH
TCP/IP Socket (eg: XMPP)<stream:stream
to='example.com'xmlns='jabber:client'version='1.0'>
<stream:streamfrom='example.com'id='someid'xmlns='jabber:client'version='1.0'>
<message from='[email protected]'to='[email protected]'>
<body>Art thou not Romeo, and a Montague?</body>
</message> <message from='[email protected]'to='[email protected]'>
<body>Neither, fair saint, if either thee dislike.</body>
</message><message from='[email protected]'
to='[email protected]'><body>How camest thou hither,
tell me, and wherefore? </body></message>
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
TCP/IP Socket (eg: XMPP)• Proprietary & efficient
• Any content could be sent
• But requires app logic to encode/decode
• Likely ports blocked by firewall rules
How to PUSH
Polling / Long PollingGET /messages HTTP/1.1Host: queue.example.comAuthorization: ...
HTTP/1.1 200 OKContent-Length: 100{ "from": "[email protected]","to": "[email protected]","message: "Do you bite your
thumb at us, sir?}
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
Polling / Long Polling• Ports 80/443
• Polling is chatty; has battery impacts
• Custom protocol: requires app logic
• Connection timeouts
• Must manually handle disconnect & resume
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
PushletGET /messages HTTP/1.1Host: queue.example.comAuthorization: ... HTTP/1.1 200 OK
Transfer-Encoding: ChunkedConnection: keep-aliveContent-Type: text/html<html><head/><body><h1>Messages</h1><p>From: [email protected]</p><p>To: [email protected]</p><p>I do bite my thumb, sir.</p>
<p>From: [email protected]</p><p>To: [email protected]</p><p>No, sir, I do not bite my thumb at you, sir, but I bite my thumb, sir.<p>
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
Pushlet• No app logic required
• Continuously "loading" UX
• CPU/Network/Battery impacts?
• Text only
• Client must must detect connection loss
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
Server-Side-EventsGET /messages HTTP/1.1Host: api.example.comAccept: text/event-streamAuthorization: ...Last-Event-ID: 41
HTTP/1.1 200 OKConnection: keep-aliveContent-Type: text/event-streamTransfer-Encoding: chunked
id: 42event: receiveMessagedata: { "from": "[email protected]","to": "[email protected]","message: "Do you bite your
thumb at us, sir?}id: 43event: stageActiondata: { "from": "[email protected]","to": "[email protected]","message: "Do you bite your
thumb at us, sir?}
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
Server-Side-EventsHTTP/1.1 200 OKConnection: keep-aliveContent-Type: text/event-streamTransfer-Encoding: chunked
id: 42event: recieveMessagedata: { "from": "[email protected]",
"to": "[email protected]","message: "Do you bite your
thumb at us, sir?}id: 43event: stageActiondata: { "from": "[email protected]","to": "[email protected]","message: "Do you bite your
thumb at us, sir?}
<script type="text/javascript">
var evtSource = new EventSource("//api.example.com/messages", { withCredentials: true } );
evtSource.addEventListener("receiveMessage", function(e) {var newElement = document.createElement("p");
var obj = JSON.parse(e.data);newElement.innerHTML = "Message: " + obj.message;eventList.appendChild(newElement);
}, false);
</script>
©2016 AKAMAI | FASTER FORWARD™
How to PUSH
Server-Side-Events• HTML5 browsers or Polyfill
• Standard APIs
• Text only messages
• Intermediates must support flushing
©2016 AKAMAI | FASTER FORWARD™
HTTP/1.1
State of Push• Build it yourself
• Requires app logic to be loaded
• (generally) Text only
• Messaging focused, not performance
©2016 AKAMAI | FASTER FORWARD™
WebRTCWebSocketHTTP/2 Push(Do we need yet
another technology?)
Wait! What about:
©2016 AKAMAI | FASTER FORWARD™
Peer-to-Peer with
WebRTC• Peer to Peer• UDP (+FlowControl)• Web APIs• BYOA (bring your own app)• Usually: Audio/Video
Flexibility: HighRealTime: HighComplexity: High
©2016 AKAMAI | FASTER FORWARD™
One-to-One with
WebSockets• One-to-One (User to Server)• Bidirectional socket• Web APIs• BYOA (bring your own app)• Can by binary or text• Not cacheable
Flexibility: MediumRealTime: HighComplexity: Medium-High
var ws = new WebSocket('wss://example.com/socket');
ws.onerror = function (error) { }ws.onclose = function () { }ws.onopen = function () { }
ws.onmessage = function(msg) {if(msg.data instanceof Blob) {processBlob(msg.data);
} else {processText(msg.data);
}}
Function sendMessage(msgText) {ws.send(msgText);
}
©2016 AKAMAI | FASTER FORWARD™
One-to-Many with
HTTP/2 Push• Many-to-One (Users to Server)• H2 standard• Cacheable• Transparent to app
Flexibility: MediumRealTime: LowComplexity: Low
[ 0.270] recv (stream_id=13) :method: GET[ 0.270] recv (stream_id=13) :path: /demo/h2/push.css[ 0.271] recv (stream_id=13) accept: */*[ 0.271] recv PUSH_PROMISE frame <length=72, flags=0x04, stream_id=13>
; END_HEADERS(padlen=0, promised_stream_id=2)
©2016 AKAMAI | FASTER FORWARD™
9 (8+)6+ (4.4)
©2016 AKAMAI | FASTER FORWARD™
Overview
HTTP/2 BasicsFix HTTP/1.1 Performance Other?├ Head of Line Blocking├ Parallelism├ Header compression└ Backward Compatible
├ Prioritization├ Flow Control└ Binary Protocol
└ Server Push
©2016 AKAMAI | FASTER FORWARD™
Chapter 2: PUSH_PROMISE
HTTP/2 PUSH_PROMISE
Frame BasicsPUSH_PROMISE:url: /push.cssuser-agent: chrome
RST_STREAM
DATA
promised-stream-id: 2
a:hover {color: blue}…
2
2
… or ...
13
©2016 AKAMAI | FASTER FORWARD™
HTTP/2
Reading the Matrix$ nghttp https://www.colinbendell.com/demo/h2/pushtest.html -vv
[ 0.135] ConnectedThe negotiated protocol: h2[ 0.230] send SETTINGS frame <length=12, flags=0x00, stream_id=0>
(niv=2)[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100][SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535]
[ 0.230] send PRIORITY frame <length=5, flags=0x00, stream_id=3>(dep_stream_id=0, weight=201, exclusive=0)
[ 0.230] send PRIORITY frame <length=5, flags=0x00, stream_id=5>(dep_stream_id=0, weight=101, exclusive=0)
[ 0.230] send PRIORITY frame <length=5, flags=0x00, stream_id=7>(dep_stream_id=0, weight=1, exclusive=0)
Frame Name Bytes
Odd = Client Initiated
©2016 AKAMAI | FASTER FORWARD™
[ 0.230] send HEADERS frame <length=60, flags=0x25, stream_id=13>; END_STREAM | END_HEADERS | PRIORITY(padlen=0, dep_stream_id=11, weight=16, exclusive=0); Open new stream:method: GET:path: /demo/h2/pushtest.html:scheme: https:authority: www.colinbendell.comaccept: */*accept-encoding: gzip, deflateuser-agent: nghttp2/1.6.0
Familiar HTTP Request
PseudoHeaders
©2016 AKAMAI | FASTER FORWARD™
[ 0.267] recv SETTINGS frame <length=30, flags=0x00, stream_id=0>(niv=5)[SETTINGS_HEADER_TABLE_SIZE(0x01):4096][SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100][SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535][SETTINGS_MAX_FRAME_SIZE(0x05):16384][SETTINGS_MAX_HEADER_LIST_SIZE(0x06):16384]
[ 0.267] recv SETTINGS frame <length=0, flags=0x01, stream_id=0>; ACK(niv=0)
[ 0.267] send SETTINGS frame <length=0, flags=0x01, stream_id=0>; ACK(niv=0)
Flow Control
©2016 AKAMAI | FASTER FORWARD™
HTTP/2
PUSH_PROMISE[ 0.270] recv (stream_id=13) :method: GET[ 0.270] recv (stream_id=13) :path: /demo/h2/push.css[ 0.270] recv (stream_id=13) :authority: www.colinbendell.com[ 0.270] recv (stream_id=13) :scheme: https[ 0.270] recv (stream_id=13) host: www.colinbendell.com[ 0.271] recv (stream_id=13) accept: */*[ 0.271] recv (stream_id=13) user-agent: nghttp2/1.6.0[ 0.271] recv (stream_id=13) accept-encoding: gzip, deflate[ 0.271] recv PUSH_PROMISE frame <length=72, flags=0x04, stream_id=13>
; END_HEADERS(padlen=0, promised_stream_id=2)
Existing Stream
Even: Server Initiated
©2016 AKAMAI | FASTER FORWARD™
[ 0.313] recv (stream_id=2, sensitive) :status: 200[ 0.313] recv (stream_id=2, sensitive) server: Apache[ 0.313] recv (stream_id=2, sensitive) etag: "e00af44aba37028e02014b6842fd3748:1461342896"[ 0.313] recv (stream_id=2, sensitive) last-modified: Fri, 22 Apr 2016 16:34:56 GMT[ 0.313] recv (stream_id=2, sensitive) accept-ranges: bytes[ 0.313] recv (stream_id=2, sensitive) content-length: 64[ 0.313] recv (stream_id=2, sensitive) content-type: text/css[ 0.313] recv (stream_id=2, sensitive) cache-control: max-age=3571[ 0.313] recv (stream_id=2, sensitive) expires: Sun, 24 Apr 2016 16:22:33 GMT[ 0.313] recv (stream_id=2, sensitive) date: Sun, 24 Apr 2016 15:23:02 GMT[ 0.313] recv HEADERS frame <length=469, flags=0x04, stream_id=2>
; END_HEADERS(padlen=0); First push response header
[ 0.313] recv (stream_id=2, length=64, srecv=64, crecv=621) DATALorem;ipsum[ 0.313] recv DATA frame <length=64, flags=0x00, stream_id=2>[ 0.313] recv DATA frame <length=0, flags=0x01, stream_id=2>
; END_STREAM
Delay, Push Response
©2016 AKAMAI | FASTER FORWARD™
HTTP/2
PUSH_PROMISE$ nghttp https://www.colinbendell.com/demo/h2/pushtest.html -vv --no-push[ 0.123] ConnectedThe negotiated protocol: h2[ 0.518] send SETTINGS frame <length=18, flags=0x00, stream_id=0>
(niv=3)[SETTINGS_MAX_CONCURRENT_STREAMS(0x03):100][SETTINGS_INITIAL_WINDOW_SIZE(0x04):65535][SETTINGS_ENABLE_PUSH(0x02):0]
Push Not Supported
[ 0.230] send RST_STREAM frame <length=5, flags=0x07, stream_id=4>
Push not accepted
chrome://net-internals
©2016 AKAMAI | FASTER FORWARD™
Invoking
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 PUSH
How do I enable?
HTTP/1.1 200 OKContent-Type: text/htmlLink: /jquery.js; rel=preload; as=scriptLink: /global.css; rel=preload; as=style
1. Use the ` Link: <URL>; rel=preload; as=style` notation
Pros:• Easy to implement• H2O / nginX support
Cons:• Too-Late; Is TTFB• Status & headers committed• Interferes with Browser's Pre-Loader
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 PUSH
How do I enable?public class MyServlet extends HttpServlet{public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{String uri = request.getRequestURI();if ("/index.html".equals(uri)){String resourceToPush = "/js/jquery.js";RequestDispatcher dispatcher =
request.getRequestDispatcher(resourceToPush);dispatcher.push(request);
}}
}
2. Manually Instrument
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 PUSH
How do I enable?2. Manually Instrument
Pros:• Before TTFB• Not impacted by page• Can extend to Next Page
Cons:• Another layer to maintain
©2016 AKAMAI | FASTER FORWARD™
Visualizing & Debug
©2016 AKAMAI | FASTER FORWARD™
Visualizing Results
©2016 AKAMAI | FASTER FORWARD™
Delayed Push with
Link=preload
©2016 AKAMAI | FASTER FORWARD™
Manual Instrument (Jetty)
©2016 AKAMAI | FASTER FORWARD™
Push headers are 'provisional'
©2016 AKAMAI | FASTER FORWARD™
Timeline not helpful (Chrome 50)
©2016 AKAMAI | FASTER FORWARD™
Canary (52) shows PUSH in Timeline
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 (Chrome)
Adopted Push Stream
chrome://net-internals to find unclaimed pushes
Adopted pushes are logged (not a frame)
* PUSH is kept in memory for 5 minutes and then get deleted if unclaimed
©2016 AKAMAI | FASTER FORWARD™
Debugging PUSH• Terminal: nghttp
• Chrome: look for provisional header or use net-internals
• Firefox: enabling logging
• PUSHed content isn't added to cache until used in the page
©2016 AKAMAI | FASTER FORWARD™
Browser Behavior
Browser support
Testing PUSH
©2016 AKAMAI | FASTER FORWARD™
HTTP/2
Connection Coalescing
Coalescing is ignored by Chrome/FF
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 Chrome
Connection coalescing not availif (!HostPortPair::FromURL(gurl).Equals(host_port_pair())) {
if (proxy_delegate_ &&proxy_delegate_->IsTrustedSpdyProxy(
ProxyServer(ProxyServer::SCHEME_HTTPS, host_port_pair()))) {
if (gurl.SchemeIs("https")) {EnqueueResetStreamFrame(
stream_id, request_priority, RST_STREAM_REFUSED_STREAM,base::StringPrintf("Rejected push of cross origin HTTPS
content %d ""from trusted proxy",associated_stream_id));
return false;}
Only Host + Port considered! Boo!
©2016 AKAMAI | FASTER FORWARD™
Browser Support
Implementation notes• PUSHed content from different hosts are
rejected instead of coalescing
• Only used PUSHes hit cache
• FF uses init flow control of 64kb
©2016 AKAMAI | FASTER FORWARD™
Chapter 3: PUSH opportunity
©2016 AKAMAI | FASTER FORWARD™
www.rakuten.co.uk
TTFB: Network Waste
TTFB: 330ms
95.9% Waste
©2016 AKAMAI | FASTER FORWARD™
www.rakuten.co.uk
Time-to-First-Resource
TTFR: 762ms
91.1% Waste
©2016 AKAMAI | FASTER FORWARD™
www.rakuten.co.uk
33.7% Perf Waste1.48s (full throughput)
87.6% WASTE Vis. Complete: 4.38s
©2016 AKAMAI | FASTER FORWARD™
www.forever21.com
WPT: Network Waste81.7%
0.6s (full throughput)
©2016 AKAMAI | FASTER FORWARD™
Browser Support
Implementation notes• Measures opportunity to use the network
ahead of DOM/Browser load
• Indicator metric – NOT silver bullet
• Examines WPT results
• Tool to be released later this month
©2016 AKAMAI | FASTER FORWARD™
Chapter 4: Results
©2016 AKAMAI | FASTER FORWARD™
PUSH Results
Insurance SiteOriginal CSS/JSOriginal
Everything Images
©2016 AKAMAI | FASTER FORWARD™
PUSH Results
Cosmetics Site
Original
CSS/JS Everything
Images
©2016 AKAMAI | FASTER FORWARD™
Automatic PUSH_PROMISE
But when you get it Right:
5.27s 8.59s
©2016 AKAMAI | FASTER FORWARD™
PUSH Results
Conclusions• Bad: too much, slows DOM complete
• Bad: too much can compete with preloader
• Bad: Wrong order congests network
• Good: Critical CSS & JS (& some images)
• Awesome: Network + Resource Timing
©2016 AKAMAI | FASTER FORWARD™
PUSH Results
Implementation questions• Impact on Set-Cookie session tracking?
• REsponsive ServerSide?
• Client Hints?
©2016 AKAMAI | FASTER FORWARD™
Chapter 5: Epilogue
©2016 AKAMAI | FASTER FORWARD™
HTTP/2 PUSH_PROMISE
Programmatic accept<?php$transfers = 1;$callback = function() use (&$transfers) {
$transfers++;return CURL_PUSH_OK;
};$mh = curl_multi_init();curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, $callback);$ch = curl_init();curl_setopt($ch, CURLOPT_VERBOSE, 1);curl_setopt($ch, CURLOPT_URL, 'https://localhost:8443/index.html');
©2016 AKAMAI | FASTER FORWARD™
PUSH
What's Next:• Browser & OS Support
• Automated PUSH using analytics
• Next page resources
• Host coalescing
©2016 AKAMAI | FASTER FORWARD™
PUSH
What about?• Javascript / CSS / Image early processing? • 3rd Party Content & Link=PreConnect ?
• PUSH to surrogate but not client?
• Delay & FlowControl: PUSH + Priorities• Cache Management / Invalidation
©2016 AKAMAI | FASTER FORWARD™
PUSH
10? 10?7??
©2016 AKAMAI | FASTER FORWARD™
Thanks!@ColinBendell