Entertainment

Scn Esi Widget

Zend Framework 2 in the Fast Lane

Motivation

Let's face it: PHP is slow. Let's speed up our Zend Framework 2 web application by putting Varnish Cache in front of it!

Zend Framework 2

  • Modular
  • Secure
  • Extensible
  • Community
  • High Performing
  • Enterprise Ready

Varnish Cache

Varnish Cache is a web application accelerator also known as a caching HTTP reverse proxy. You install it in front of any server that speaks HTTP and configure it to cache the contents. Varnish Cache is really, really fast. It typically speeds up delivery with a factor of 300 - 1000x, depending on your architecture.

- www.varnish-cache.org

But I have User Info!

  1. Configure Varnish to never cache "private" parts of the page
  2. Configure Varnish to add the UUID to the hash key
  3. Do all user specific stuff in the client

But I have Dynamic Content!

  1. Probably not as much as you think
  2. Layout? Published blog post?
  3. Blog Comments? Can still cache for short time
  4. At 500 req/s, caching for 1s alleviates a tremendous load from origin servers
  5. Let's face it...as soon as the data leaves your DC, it's already old

Edge Side Includes (ESI)

Edge Side Includes is a simple markup language for edge level dynamic web content assembly. The purpose is to tackle the problem of web infrastructure scaling and is an application of edge computing.


<esi:include src="http://example.com/1.html" alt="http://bak.example.com/2.html" onerror="continue"/>
                        

Scn Esi Widget Components

  • EsiWidget Controller Plugin
  • EsiRenderer
  • EsiStrategy


That's It!

Esi Widget Controller Plugin

  • Wrapper around the ZF2 Forward Plugin
  • Sets up a clone of the MVC event with it's own Request / Response
  • Uses application routes to forward to proper controller / action
  • Injects the resulting View Model into the parent with required params

Esi Renderer

  • Only chosen when client announces surrogate capability
  • Wrapper around the ZF2 PhpRenderer
  • Replaces actual view content with ESI tag

Esi Strategy

  • Chooses EsiRenderer when client announces surrogate capability
  • Adds a response header announcing "Surrogate-Control", i.e. the HTML may contain ESI tags that need to be replaced

Installation

Composer

Composer is a tool for dependency management in PHP. It allows you to declare the dependent libraries your project needs and it will install them in your project for you.

- getcomposer.org

Composer Configuration


"minimum-stability": "dev",
"require": {
    "php": ">=5.3.3",
    "zendframework/zendframework": "2.*",
    "socalnick/scn-esi-widget": "dev-master"
}
   					    

ZF2 Application Configuration


return array(
    'modules' => array(
        'Application',
        'ScnEsiWidget',
    ),
);
   					    

Varnish Configuration


backend default {
    .host = "127.0.0.1";
    .port = "10088";
}

sub vcl_recv {
    # Set a header announcing Surrogate Capability to the origin
    # ScnEsiWidget sees this header and emits ESI tag for widgets
    set req.http.Surrogate-Capability = "varnish=ESI/1.0";
}

sub vcl_fetch {
    # Unset the Surrogate Control header and do ESI
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        set beresp.do_esi = true;
    }
}
   					    

Usage

Call Esi Widget Controller Plugin


public function esiAction()
{
    $viewModel = new ViewModel();
    $this->esiWidget()->addToViewModel($viewModel, '/application/index/recent-tweets', 'recentTweets');

    $headers = $this->getResponse()->getHeaders();
    $cacheControl = new \Zend\Http\Header\CacheControl();
    $cacheControl->addDirective('max-age', '60');
    $headers->addHeader($cacheControl);

    return $viewModel;
}
                        

Echo Widget in View Script


<div><?php echo $this->recentTweets ?></div>
                        

Action for Esi Widget


public function recentTweetsAction()
{
    $headers = $this->getResponse()->getHeaders();
    $cacheControl = new \Zend\Http\Header\CacheControl();
    $cacheControl->addDirective('max-age', '10');
    $headers->addHeader($cacheControl);

    $viewModel = new ViewModel();
    $viewModel->setTerminal(true);
    return $viewModel;
}
                        

View Script for Esi Widget


<ul>
    <li><?php echo date('h:i:s')?> @SocalNick: This is a recent tweet!</li>
    <li><?php echo date('h:i:s', time() - 10)?> @Touge: I'm at SFPHP!</li>
</ul>
                        

Demo!

  • Zend Skeleton Index
  • Zend Skeleton Index w/ESI (it works w/o Varnish!)
  • Zend Skeleton Index w/ESI behind Varnish

That's all folks!

Nicholas Calugar

Senior Software Engineer @ IGN Entertainment

twitter.com/socalnick

https://github.com/socalnick


Code: https://github.com/SocalNick/ScnEsiWidget

Slides: http://socalnick.github.com/talks/2012/09/27/zf2-fast-lane/