Download - HTTP Caching and PHP
HTTP caching and PHP David de Boer
Hello DPC!
• I’m David
• Lead developer at Driebit, Amsterdam
• Open source enthusiast
– Phil Karlton
“There are only two hard things in Computer Science: cache invalidation and naming things.”
Why we cache
• Reduce response times
• Reduce server load
• Cheap
How we cache
Client App
Client AppCache
How we cache
Efficient caching
• Maximize cache hits
• TTL ∞
GET /apple HTTP/1.1Host: fresh-‐fruit.comConnection: close...
!!HTTP/1.1 200 OKHost: fresh-‐fruit.comX-‐Cache: HITAge: 567648000...
• Maximize cache hits
• TTL ∞
• Invalidation
Efficient caching
AppCache
Let’s go invalidate
Cache• Varnish
• Nginx
Varnish
# /etc/varnish/default.vcl sub vcl_hit { if (req.request == "PURGE") { purge; error 204 "Purged"; } } !sub vcl_miss { if (req.request == "PURGE") { purge; error 204 "Purged (Not in cache)"; }}
$ composer require friendsofsymfony/http-‐cache @alpha
FOSHttpCache
App
Connect and purge
use FOS\HttpCache\ProxyClient\Varnish;$servers = ['10.0.0.1:6081'];$proxyClient = new Varnish($servers); $proxyClient -‐>purge('/news/articles/42') -‐>purge('/news') -‐>flush();
Level upsub vcl_recv { if (req.request == "BAN") { ban("obj.http.x-‐host ~ " + req.http.x-‐host + " && obj.http.x-‐url ~ " + req.http.x-‐url + " && obj.http.content-‐type ~ " + req.http.x-‐content-‐type ); ! error 200 "Banned"; } } !sub vcl_fetch { # Set ban lurker-‐friendly custom headers set beresp.http.x-‐url = req.url; set beresp.http.x-‐host = req.http.host; }
Level up
use FOS\HttpCache\CacheInvalidator; !$invalidator = new CacheInvalidator( $proxyClient); !$invalidator -‐>invalidateRegex('.*', 'image/png') -‐>invalidateRegex('^/admin') -‐>flush();
Get organized
• Complex relationships between URLs
• Knowing when to invalidate what
• Depends on application logic
Taggingsub vcl_recv { if (req.request == "BAN") { if (req.http.x-‐cache-‐tags) { ban("obj.http.x-‐host ~ " + req.http.x-‐host + " && obj.http.x-‐url ~ " + req.http.x-‐url + " && obj.http.content-‐type ~ " + req.http.x-‐content-‐type + " && obj.http.x-‐cache-‐tags ~ " + req.http.x-‐cache-‐tags ); error 200 "Banned"; } else { # ... } } } !# ...
Tagging
// overview.phpheader('Cache-‐Control: max-‐age=3600');header('X-‐Cache-‐Tags: art-‐1,art-‐2,art-‐3'); !!// article.phpheader('Cache-‐Control: max-‐age=3600');header('X-‐Cache-‐Tags: art-‐2'); $invalidator-‐>invalidateTags(['art-‐2']);
use FOS\HttpCache\Tests\VarnishTestCase; !class MyTest extends VarnishTestCase{ public function testHit() { $first = $this-‐>getResponse('/cached.php'); $this-‐>assertMiss($first); $second = $this-‐>getResponse('/cached.php'); $this-‐>assertHit($second); }}
OK (1 test, 2 assertions)
Test-driven invalidation
Test-driven invalidation
class MyTest extends VarnishTestCase{ public function testTags() { $miss = $this-‐>getResponse('/article.php'); $hit = $this-‐>getResponse('/article.php'); $invalidator-‐>invalidateTags(['art-‐2']); $this-‐>assertMiss($this-‐>getResponse('/article.php')); } }
OK (1 test, 1 assertion)
Symfony bundle$ composer require friendsofsymfony/http-‐cache-‐bundle @alpha !/** * @InvalidateRoute("fruits-‐overview", params={"type" = "latest"}) * @InvalidateRoute("fruits", params={"num" = "id"}) */public function editAction($id){ # ... /** * @Tag(expression="'fruit-‐'~$id") */public function showAction($id){ # ...
Symfony bundlefos_http_cache: cache_control: rules: -‐ match: host: ^login.example.com$ headers: cache_control: public: false s_maxage: 0 last_modified: "-‐1 hour" vary: [Accept-‐Encoding, Accept-‐Language] -‐ match: attributes: { _controller: ^AcmeBundle:Default:.* } additional_cacheable_status: [400] headers: cache_control: { public: true, max_age: 15, s_maxage: 30 }
https://github.com/FriendsOfSymfony/FOSHttpCache
https://github.com/FriendsOfSymfony/FOSHttpCacheBundle
http://foshttpcache.readthedocs.org