Webmardi Fribourg, June 3rd 2014
© David Buchmann, Liip AG
1 | apt-get update |
2 | apt-get install varnish |
1 | apt-get update |
2 | apt-get install varnish |
/etc/default/varnish
, replace port 6081 by port 801 | service apache2 restart |
2 | service varnish restart |
1 | GET /path |
2 |
3 | HTTP/1.1 200 OK |
4 | Content-Type: text/html |
5 |
6 | <html>...</html> |
https://twitter.com/stevelosh/status/372740571749572610
http://httpstatusdogs.com
1 | Cache-Control: s-maxage=3600, max-age=900 |
2 | Expires: Thu, 15 May 2014 08:00:00 GMT |
1 | Cache-Control: s-maxage=0, private, no-cache |
1 | Vary: Accept, Cookie |
1 | Last-Modified: Tue, 13 May 2014 08:13:20 GMT |
1 | If-Modified-Since: Tue, 13 May 2014 08:13:20 GMT |
2 |
3 | 304 Not Modified |
1 | ETag: 82901821233 |
1 | If-None-Match: 82901821233 |
2 |
3 | 304 Not Modified |
wget -Sq --spider http://localhost/path.html curl -o /dev/null -sD - http://cmf.lo/app_dev.php
HTTP/1.1 200 OK Date: Wed, 12 May 2014 08:20:06 GMT Cache-Control: no-cache Vary: Accept-Language,Accept-Encoding Content-Type: text/html; charset=UTF-8
1 | // DefaultController::indexAction |
2 | $response = $this ->render( '::index.html.twig' ); |
3 | $response ->setMaxAge(600); |
4 | $response ->setPublic(); |
5 |
6 | return $response ; |
01 | fos_http_cache: |
02 | rules: |
03 | # login must not be cached |
04 | - |
05 | match: |
06 | path: ^/(login|login_check|logout) |
07 | controls: |
08 | private: true |
09 | max_age: 0 |
10 | # Cache the homepage for 10 minutes |
11 | - |
12 | match: |
13 | path: ^/$ |
14 | vary: Cookie |
15 | controls: |
16 | public: true |
17 | max_age: 600 |
1 | # ... |
2 | # Cache everything else for 1 hour |
3 | - |
4 | match: |
5 | path: ^/ |
6 | vary: Cookie |
7 | controls: |
8 | public: true |
9 | max_age: 3600 |
vcl_recv: entry point, lookup or pass vcl_fetch: receive from backend, cache or not vcl_deliver: remove headers not for client vcl_hash: determine cache key vcl_hit: found in cache, deliver or pass vcl_miss: pass or fetch vcl_pipe: do not alter request vcl_error: define error page
Think carefully and test thoroughly
1 | sub vcl_recv { |
2 | if (req.http.Cookie) { |
3 | if (req.url ~ "^/static") { |
4 | remove req.http.Cookie; |
5 | } |
6 | } |
7 | } |
1 | sub vcl_deliver { |
2 | if (obj.hits > 0) { |
3 | set resp.http.X-Cache = "HIT"; |
4 | } else { |
5 | set resp.http.X-Cache = "MISS"; |
6 | } |
7 | } |
1 | fos_http_cache: |
2 | rules: |
3 | #... |
4 | reverse_proxy_ttl: 600 |
01 | sub vcl_fetch { |
02 | ... |
03 | if (beresp.http.X-Reverse-Proxy-TTL) { |
04 | C{ |
05 | char *ttl; |
06 | ttl = VRT_GetHdr(sp, HDR_BERESP, "\024X-Reverse-Proxy-TTL:"); |
07 | VRT_l_beresp_ttl(sp, atoi(ttl)); |
08 | }C |
09 | unset beresp.http.X-Reverse-Proxy-TTL; |
10 | } |
11 | ... |
1 | # app/config.yml |
2 | framework: |
3 | templating: |
4 | assets_version: v1 |
1 | < link rel = "stylesheet" href = "/css/style.css?v1" type = "text/css" /> |
2 | ... |
3 | < script src = "/js/scripts.js?v1" ></ script > |
1 | // CommentsController::postAction |
2 | // the page changed, send a purge request for this url |
3 | ... |
4 | $path = $this ->generate( 'page' , array ( |
5 | 'id' => $post ->getPage()->getId()) |
6 | ); |
7 | $cacheManager = $container ->get( 'fos_http_cache.cache_manager' ); |
8 | $cacheManager ->invalidatePath( $path ); |
9 | ... |
1 | # app/config.yml |
2 |
3 | fos_http_cache: |
4 | proxy_client: |
5 | varnish: |
6 | servers: 4.4.4.11:80, 4.4.4.22:80 |
7 | base_url: yourwebsite.com |
01 | # default.vcl |
02 | sub vcl_recv { |
03 | if (req.request == "PURGE") { |
04 | if (!client.ip ~ invalidators) { |
05 | error 405 "PURGE not allowed"; |
06 | } |
07 | return (lookup); |
08 | } |
09 | } |
10 | sub vcl_hit { |
11 | if (req.request == "PURGE") { |
12 | purge; |
13 | error 200 "Purged"; |
14 | } |
15 | } |
16 | sub vcl_miss { // same as hit |
Group based caching
1 | /** @var $cm CacheManageer */ |
2 | $cm ->tagResponse( $response , array ( 'comment-42' )); |
3 | ... |
4 | $cm ->invalidateTags( array ( 'comment-42' )); |
01 | use FOS\HttpCacheBundle\Configuration\Tag; |
02 |
03 | class CommentController extends Controller |
04 | { |
05 | /** |
06 | * @Tag({"comments", "'comment-'~id"}) |
07 | */ |
08 | public function commentAction( $id ) |
09 | { |
10 | // ... |
1 | ban("obj.http.host ~ " + req.http.x-host |
2 | + " && obj.http.x-url ~ " + req.http.x-url |
3 | + " && obj.http.content-type ~ " + |
4 | req.http.x-content-type |
5 | + " && obj.http.x-cache-tags ~ " + |
6 | req.http.x-cache-tags |
7 | ); |
1 | # app/config/config.yml |
2 | framework: |
3 | esi: { enabled: true } |
4 | # app/config/routing.yml |
5 | _internal: |
6 | resource: "@FrameworkBundle/Resources/config/routing/internal.xml" |
7 | prefix: /_internal |
1 | {# index.html.twig #} |
2 | {% render 'DbuCoreBundle:Comments:comments' |
3 | with {}, {'standalone': true} %} |
1 | fos_http_cache: |
2 | rules: |
3 | # do not apply rules to _internal |
4 | - |
5 | match: |
6 | path: ^/_internal |
7 | # no controls |
1 | // UserController::showBoxAction |
2 | $response = $this ->render( |
3 | 'DbuCoreBundle:User:box.html.twig' ); |
4 | $response ->setVary( 'Cookie' , false); |
5 | $response ->setMaxAge(0); |
6 | $response ->setPrivate(); |
7 | return $response ; |
1 | // CommentsController::commentsAction |
2 | $response = $this ->render( |
3 | 'DbuCoreBundle:Comments:comments.html.twig' , |
4 | array ( 'comments' => $this ->getComments())); |
5 | $response ->setMaxAge(3600); |
6 | $response ->setPublic(); |
7 |
8 | return $response ; |
1 | $cm = $container ->get( 'fos_http_cache.cache_manager' ); |
2 | $kernel = $this ->container->get( 'http_kernel' ); |
3 | $path = $kernel ->generateInternalUri( |
4 | 'DbuCoreBundle:Comments:comments' |
5 | ); |
6 | $cm ->invalidatePath( $path ); |
Note: generateInternalUri is helpful but does not check if controller exists
$ app/console cache:clear --env=prod --no-debug $ curl -H "Surrogate-Capability: abc=ESI/1.0" \ http://performance.lo/
1 | ... |
2 | < esi:include src = "/_internal/secure/DbuCoreBundle%3AUser%3AshowLogin/none.html" onerror = "continue" /> |
3 | ... |
4 | < esi:include src = "/_internal/secure/DbuCoreBundle%3AComments%3Acomments/none.html" onerror = "continue" /> |
5 | ... |
1 | sub vcl_recv { |
2 | set req.http.Surrogate-Capability = "abc=ESI/1.0"; |
3 | # try to lookup even if there is a cookie |
4 | return (lookup); |
5 | } |
1 | sub vcl_fetch { |
2 | if (beresp.http.Surrogate-Control ~ "ESI/1.0") { |
3 | unset beresp.http.Surrogate-Control; |
4 | set beresp.do_esi = true; |
5 | } |
6 | if (beresp.http.Vary) { |
7 | return (pass); |
8 | } |
9 | } |
1 | sub vcl_deliver { |
2 | if (! req.url ~ ".*\.(css|js|png)(\?.*)?$") { |
3 | set resp.http.Vary = "Cookie"; |
4 | unset resp.http.Last-Modified; |
5 | } |
6 | } |