项目

通用

个人资料

操作

模块 mod_cml - CML(缓存元语言)

警告

  • 此模块已被弃用,建议使用提供类似功能的 mod_magnet 模块。
  • 您必须返回 1 而不是 CACHE_MISS,返回 0 而不是 CACHE_HIT (#533)

描述

CML(缓存元语言)旨在解决以下几个问题:
  • 动态内容需要缓存以提高性能
  • 在应用程序内部检查内容是否过期通常比直接发送缓存数据更耗费资源
  • 动态页面通常是碎片化的,并且这些碎片具有不同的生命周期
  • 不同的碎片可以独立缓存

缓存决策

一个简单的示例将展示如何在 PHP 中以非常简单的方式进行内容缓存。

jan.kneschke.de 网站设计非常简单
  • 布局取自 templates/jk.tmpl 中的模板
  • 菜单由 menu.csv 文件生成
  • 内容来自本地目录中名为 content-1、content-2 等的文件

只要这三类内容(布局、菜单文件、内容文件)中的任何一个没有变化,页面内容就是静态的。布局的更改会影响所有页面,menu.csv 的更改也会影响所有页面,而 content-x 文件的更改仅影响缓存页面本身。

如果我们在 PHP 中模拟它,我们会得到

<?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
}
?>

非常简单。不涉及任何魔法。如果其中一个文件比缓存内容新,则内容是脏的,必须重新生成。

现在我们来看看这些数字: * 缓存命中时 150 请求/秒 * 缓存未命中时 100 请求/秒

如您所见,性能提升不如预期。主要原因是 PHP 解释器的启动开销(此处已使用字节码缓存)。

将这些决策从 PHP 脚本中移到服务器模块中,将消除缓存命中时启动 PHP 的需要。

要将此示例转换为 CML,您需要在索引文件列表中包含 'index.cml',以及以下 index.cml 文件

output_contenttype = "text/html" 

b = request["DOCUMENT_ROOT"]
cwd = request["CWD"]

output_include = { b .. "_cache.html" }

trigger_handler = "index.php" 

if file_mtime(b .. "../lib/php/menu.csv") > file_mtime(cwd .. "_cache.html") or
   file_mtime(b .. "templates/jk.tmpl")   > file_mtime(cwd .. "_cache.html") or
   file_mtime(b .. "content.html")        > file_mtime(cwd .. "_cache.html") then
   return CACHE_MISS
else 
   return CACHE_HIT
end
再看数字
  • 缓存命中时 4900 请求/秒
  • 缓存未命中时 100 请求/秒

内容组装

有时不同的碎片已在外部生成。您需要将它们拼接在一起

<?php
 readfile("head.html");
 readfile("menu.html");
 readfile("spacer.html");
 readfile("db-content.html");
 readfile("spacer2.html");
 readfile("news.html");
 readfile("footer.html");
?> 

我们可以在 Web 服务器中直接以快数倍的速度完成同样的工作。

别忘了:Web 服务器是为了发送静态内容而构建的,这是它们最擅长的。

为此,index.cml 文件如下所示

output_contenttype = "text/html" 

cwd = request["CWD"]

output_include = { cwd .. "head.html", 
                   cwd .. "menu.html",
                   cwd .. "spacer.html",
                   cwd .. "db-content.html",
                   cwd .. "spacer2.html",
                   cwd .. "news.html",
                   cwd .. "footer.html" }

return CACHE_HIT

现在我们获得了大约 10000 请求/秒的性能,而不是 600 请求/秒。

强力磁铁

除了缓存决策的所有功能之外,CML 还能做得更多。从 lighttpd 1.4.9 开始,增加了一个“强力磁铁”功能,它可以吸引每个请求,并允许您根据需要操纵请求。

我们希望通过将文件放置在指定位置来显示维护页面

我们启用强力磁铁

cml.power-magnet  = "/home/www/power-magnet.cml"

并创建 /home/www/power-magnet.cml 文件,内容如下

dr = request["DOCUMENT_ROOT"]

if file_isreg(dr .. 'maintenance.html') then
  output_include = { dr .. 'maintenance.html' }
  return CACHE_HIT
end

return CACHE_MISS

对于每个请求的文件,都会执行 /home/www/power-magnet.cml,它会检查 docroot 中是否存在 maintenance.html,如果存在则显示它,而不是处理常规请求。

另一个例子,为请求的图像创建缩略图并提供它,而不是发送大图

## image-url is /album/baltic_winter_2005.jpg
## no params -> 640x480 is served
## /album/baltic_winter_2005.jpg/orig for full size
## /album/baltic_winter_2005.jpg/thumb for thumbnail

dr = request["DOCUMENT_ROOT"]
sn = request["SCRIPT_NAME"]

## to be continued :) ... 

trigger_handler = '/gen_image.php'
return CACHE_MISS

安装

您需要 lua,并且应该安装 libmemcached,还需要使用以下配置来配置 lighttpd

./configure ... --with-lua --with-memcached

要使用该插件,您需要加载它

server.modules = ( ..., "mod_cml", ... )

选项

cml.extension

绑定到 cml 模块的文件扩展名

cml.extension = ".cml"

cml.memcache-hosts

memcache.* 函数的主机

cml.memcache-namespace

(暂未使用)

cml.power-magnet

为每个请求执行的 cml 文件

语言

CML 使用的语言由 LUA 提供

除了 Lua 提供的函数之外,mod_cml 还提供了

  • 请求
    • REQUEST_URI
    • SCRIPT_NAME
    • SCRIPT_FILENAME
    • DOCUMENT_ROOT
    • PATH_INFO
    • CWD
    • BASEURI
  • 获取
    • 查询字符串中的参数
  • 函数
    • string md5(string)
    • number file_mtime(string)
    • string memcache_get_string(string)
    • number memcache_get_long(string)
    • boolean memcache_exists(string)

无论您的脚本做什么,都必须返回 CACHE_HIT 或 CACHE_MISS。如果发生错误,请检查错误日志。用户将收到 500 错误。如果您不喜欢标准错误页面,请使用 server.errorfile-prefix

gstrauss近 9 年前 更新 · 19 次修订