HTTP for Great Good

6 min read Original article ↗
  • Hello < me irl

  • Site Reliability Engineer

  • “DJANGO ALL THE THINGS!”

  • “...but

  • “...but

  • “...everything

  • A few vanity metrics.

  • Monthly Unique Visitors 1,115,080,411

  • Monthly Page Views 7,516,761,301

  • ...what happened to the other 64%?

  • Let’s talk about HTTP. Hypertext Transport Protocol

  • $ curl -v disqus.com

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Request

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Method

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Path

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Version

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Headers

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Response

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Status

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Headers

  • “Cool,

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > < HTTP/1.1 200 OK < Server: nginx < Date: Fri, 30 Aug 2013 06:38:37 GMT < Content-Type: text/html; charset=utf-8 < Content-Length: 10453 < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 06:38:36 GMT < Cache-Control: no-cache, must-revalidate Hmm. What can we do with this information?

  • > GET / HTTP/1.1 > User-Agent: curl/7.24.0 > Host: disqus.com

    > Accept: */* > If-Modified-Since: Fri, 30 Aug 2013 00:32:14 GMT > < HTTP/1.1 304 Not Modified < Server: nginx < Date: Fri, 30 Aug 2013 18:05:38 GMT < Last-Modified: Fri, 30 Aug 2013 00:32:14 GMT < Vary: Accept-Encoding < Expires: Fri, 30 Aug 2013 18:05:37 GMT < Cache-Control: no-cache, must-revalidate

  • 304 Not Modified No body is sent with the response

  • 304 Not Modified Client reuses its cached version

  • 304 Not Modified Usually more efficient to calculate

  • notbad.gif

  • But we can do better!

  • “Far future headers”

  • Chrome Web Inspector on second visit

  • No HTTP request 0ms

  • No HTTP request Computer actually did something right for once

  • “I SEE WHAT YOU DID THERE” - Hopefully you

  • Takeaways Clients behave differently depending on the response headers

  • Takeaways These usually come with minimal effort with static files

  • Same logic can be applied to dynamic content.

  • What’s this look like in Django?

  • “OMG!

  • Well... not really.

  • 1? 2? 3?

  • ...out of 42k requests per second.

  • Parsing HTTP.

  • WSGI.

  • Django middleware stack.

  • Do some stuff.

  • Render a template?

  • Back out through the Django middleware.

  • Transform an HttpResponse into a real HTTP response.

  • ...at 42k requests per second.

  • You’re gonna have a bad time. me

  • Until now, “client” has been a user’s browser.

  • “If only we could utilize this Cache-Control stuff better...”

  • Introducing

  • $ apt-get install varnish

  • $ brew install varnish

  • tl;dr Varnish sits between Django and your users Internet

  • tl;dr Caches HTTP responses and respects proper HTTP headers Internet

  • Stand back, science is happening.

  • Stand back, science is happening. benchmarking

  • Simple, non-scientific “Hello World”

  • Varnish: How does it work?

  • First request

  • First response “Lemme

  • “Yo,

  • Next response “wut

  • Caching: ProMoves™

  • Augment with JavaScript Update your UI optimistically

  • Augment with JavaScript Leverage cookies to store non-critical data

  • Augment with JavaScript Defer fetching user-specific data until needed

  • Let’s meet: John and Jane Doe.

  • John and Jane are different users.

  • John logs into Disqus.

  • Jane logs into Disqus.

  • Jane sees John’s stuff.

  • Jane sees John’s stuff. ^ not

  • We really want to avoid this from ever happening.

  • Cookies

  • How do users even work?

  • Unique id that represents a logged in user Session Id

  • django.contrib.sessions / django.contrib.auth Session Id

  • Could potentially cache per session id Session Id

  • If it doesn’t, Varnish can normalize it.

  • Learn: Varnish Configuration Language (VCL) in 30 seconds

  • Basically, caching is hard.

  • Go make some stuff faster.

  • We’re hiring people who hate computers. disqus.com/jobs

  • Questions? I have answers. ^ github.com/mattrobenolt @mattrobenolt some