Browse Source
git-svn-id: svn://svn.lighttpd.net/lighttpd/branches/lighttpd-1.3.x@422 152afb58-edef-0310-8abb-c4023f1b3aa9svn/tags/lighttpd-1.3.15
2 changed files with 160 additions and 2 deletions
@ -0,0 +1,156 @@
|
||||
========================= |
||||
CML (Cache Meta Language) |
||||
========================= |
||||
|
||||
--------------- |
||||
Module: mod_cml |
||||
--------------- |
||||
|
||||
:Author: Jan Kneschke |
||||
:Date: $Date: 2004/11/03 22:26:05 $ |
||||
:Revision: $Revision: 1.2 $ |
||||
|
||||
:abstract: |
||||
CML is a Meta language to describe the dependencies of a page at one side and building a page from its fragments on the other side |
||||
|
||||
.. meta:: |
||||
:keywords: lighttpd, cml |
||||
|
||||
.. contents:: Table of Contents |
||||
|
||||
Description |
||||
=========== |
||||
|
||||
CML (Cache Meta Language) wants to solves several problems: |
||||
|
||||
* dynamic content needs caching to perform |
||||
* checking if the content is dirty inside of the application is usually more expensive than sending out the cached data |
||||
* a dynamic page is usually fragmented and the fragments have different livetimes |
||||
* the different fragements can be cached independently |
||||
|
||||
Cache Decision |
||||
-------------- |
||||
|
||||
A simple example should show how to a content caching the very simple way in PHP. |
||||
|
||||
jan.kneschke.de has a very simple design: |
||||
|
||||
* the layout is taken from a template in templates/jk.tmpl |
||||
* the menu is generated from a menu.csv file |
||||
* the content is coming from files on the local directory named content-1, content-2 and so on |
||||
|
||||
The page content is static as long non of the those tree items changes. A change in the layout |
||||
is affecting all pages, a change of menu.csv too, a change of content-x file only affects the |
||||
cached page itself. |
||||
|
||||
If we model this in PHP we get: :: |
||||
|
||||
<?php |
||||
|
||||
## ... fetch all content-* files into $content |
||||
$cachefile = "/cache/dir/to/cached-content"; |
||||
|
||||
function is_cachable($content, $cachefile) { |
||||
if (!file_exists($cachefile)) { |
||||
return 0; |
||||
} else { |
||||
$cachemtime = filemtime($cachefile); |
||||
} |
||||
|
||||
foreach($content as $k => $v) { |
||||
if (isset($v["file"]) && |
||||
filemtime($v["file"]) > $cachemtime) { |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
if (filemtime("/menu/menu.csv") > $cachemtime) { |
||||
return 0; |
||||
} |
||||
if (filemtime("/templates/jk.tmpl") > $cachemtime) { |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
if (is_cachable(...), $cachefile) { |
||||
readfile($cachefile); |
||||
exit(); |
||||
} else { |
||||
# generate content and write it to $cachefile |
||||
} |
||||
?> |
||||
|
||||
Quite simple. No magic involved. If the one of the files is new than the cached |
||||
content, the content is dirty and has to be regenerated. |
||||
|
||||
Now let take a look at the numbers: |
||||
|
||||
* 150 req/s for a Cache-Hit |
||||
* 100 req/s for a Cache-Miss |
||||
|
||||
As you can see the increase is not as good as it could be. The main reason as the overhead |
||||
of the PHP interpreter to start up (a byte-code cache has been used here). |
||||
|
||||
Moving these decisions out of the PHP script into a server module will remove the need |
||||
to start PHP for a cache-hit. |
||||
|
||||
To transform this example into a CML you need 'index.cml' in the list of indexfiles |
||||
and the following index.cml file: :: |
||||
|
||||
output.content-type text/html |
||||
|
||||
output.include _cache.html |
||||
|
||||
trigger.handler index.php |
||||
trigger.if file.mtime("../lib/php/menu.csv") > file.mtime("_cache.html") |
||||
trigger.if file.mtime("templates/jk.tmpl") > file.mtime("_cache.html") |
||||
trigger.if file.mtime("content.html") > file.mtime("_cache.html") |
||||
|
||||
Numbers again: |
||||
|
||||
* 4900 req/s for Cache-Hit |
||||
* 100 req/s for Cache-Miss |
||||
|
||||
Content Assembling |
||||
------------------ |
||||
|
||||
Sometimes the different fragment are already generated externally. You have to cat them together: :: |
||||
|
||||
<?php |
||||
readfile("head.html"); |
||||
readfile("menu.html"); |
||||
readfile("spacer.html"); |
||||
readfile("db-content.html"); |
||||
readfile("spacer2.html"); |
||||
readfile("news.html"); |
||||
readfile("footer.html"); |
||||
?> |
||||
|
||||
We we can do the same several times faster directly in the webserver. |
||||
|
||||
Don't forget: Webserver are built to send out static content, that is what they can do best. |
||||
|
||||
The index.cml for this looks like: :: |
||||
|
||||
output.content-type text/html |
||||
|
||||
output.include head.html |
||||
output.include menu.html |
||||
output.include spacer.html |
||||
output.include db-content.html |
||||
output.include spacer2.html |
||||
output.include news.html |
||||
output.include footer.html |
||||
|
||||
Now we get about 10000 req/s instead of 600 req/s. |
||||
|
||||
Options |
||||
======= |
||||
|
||||
:cache.extension: |
||||
the file extension that is bound to the cml-module |
||||
|
||||
Language |
||||
======== |
||||
|
||||
... will come later ... |
Loading…
Reference in new issue