Linux/PHP文件访问问题(构建自定义缓存)

Linux/PHP文件访问问题(构建自定义缓存),php,rhel5,Php,Rhel5,我有个小问题,希望有人能解释一下情况 情况: 我有一个定制的部分缓存机制,它内置于PHPCMS中。简而言之,当CMS中的模板被处理时,它会处理“可缓存”的PHP代码,而不会处理“不可缓存”的PHP代码,然后生成的代码会保存为一个文件,以供将来访问该页面时处理 问题是: 当系统“查找”缓存文件时,我遇到文件访问延迟。当站点没有流量时,使用GLOB查找匹配文件的250000个缓存文件需要1/4的时间,有时流量峰值需要10-15秒。似乎两个独立的客户端会话不能同时运行GLOB,所以它们都是瓶颈 我要找

我有个小问题,希望有人能解释一下情况

情况:

我有一个定制的部分缓存机制,它内置于PHPCMS中。简而言之,当CMS中的模板被处理时,它会处理“可缓存”的PHP代码,而不会处理“不可缓存”的PHP代码,然后生成的代码会保存为一个文件,以供将来访问该页面时处理

问题是:

当系统“查找”缓存文件时,我遇到文件访问延迟。当站点没有流量时,使用GLOB查找匹配文件的250000个缓存文件需要1/4的时间,有时流量峰值需要10-15秒。似乎两个独立的客户端会话不能同时运行GLOB,所以它们都是瓶颈

我要找的是:

。。。是提供块缓存的替代方法或优化,没有瓶颈问题。必须考虑我的独特关注概述如下。我需要一种更快的方式来访问这些文件,或者需要一种可选的部分页面缓存方向:/

==============================================

简编代码:

// var to hold cached page path, if found
$pageCache = NULL;

// get the URL for current page
// there is actually some other code here that could alter the 'theURL4Cache' var for various reasons, but for simplicity in this example lets just keep it the REQUEST_URI
$GLOBALS['theURL4Cache'] = $_SERVER['REQUEST_URI'];

// check if existing cache file is in place
$filePattern = 'parsed/page_cache/*^' . $_SERVER['SERVER_PORT'] . '^' . $_SERVER['HTTP_HOST'] . '^' . (($_SESSION['isMobile']) ? 'M' : 'D') . '^L' . $language . '^T*^P' . $attributes['pageId'] . '^' . md5($GLOBALS['theURL4Cache']) . sha1($GLOBALS['theURL4Cache']) . '.php';
$fileArr = glob($filePattern);

// possible multiple files found that fit / expired files found that fit the pattern
// lets grab the newest file and try to use it
if(count($fileArr)){ 
        rsort($fileArr);
        $file = $fileArr[0]; // get file with latest expire date
        if($file > 'parsed/page_cache/' . date('Y-m-d-H-i-s')) $pageCache = $file; // set an attribute to hold the valid file path to the cached file
        // remove files that are no longer corrent
        for($i=(($pageCache === NULL) ? 0 : 1); $i<count($fileArr);$i++) unlink($fileArr[$i]);
    };
if($pageCache){
  // cached page is found, lets process and output this puppy
  include($pageCache);
} else {
  // cached page is not found, let's build a cacheable page from the CMS template
  $newCode = // ..... various code is processed here to isolate the cacheable code blocks and process while leaving the non-cacheable blocks intact ..... //
  // create the new file path where the cached code will be placed
  // first we need an expiration date
  $cacheDate = date_create();
  date_add($cacheDate, date_interval_create_from_date_string( $cache_increment . ' ' . $cache_interval)); // $cache_increment and $cache_interval are stored in the CMS DB for each page, giving the content manager control over the expiration of the page in cache
  $filePath = $GLOBALS['iProducts']['physicalRoot'] . "/parsed/page_cache/" . date_format($cacheDate,'Y-m-d-H-i-s') . "^" . $_SERVER['SERVER_PORT'] . '^' . $_SERVER['HTTP_HOST'] . '^' . (($_SESSION['isMobile']) ? 'M' : 'D') ."^L" . $language . "^T" . $templateId . "^P" . $attributes['pageId'] . "^" .md5($GLOBALS['theURL4Cache']) . sha1($GLOBALS['theURL4Cache']) . '.php'; // create the file path
  if(file_exists($filePath)) unlink($filePath); // delete the file if it already exists
  $fp = fopen($filePath,"w"); // create the new file
  flock($fp,LOCK_EX);
  fwrite($fp,$newCode); // write the cache file
  flock($fp,LOCK_UN);
  // now output this puppy
  eval($newCode);
};
你问,为什么你的文件名这么乱

很高兴你问我!CMS的另一部分包括“智能缓存管理”,如果内容管理器修改了页面或模板,则会从系统中清除所有受影响的缓存页面。此外,页面中的内容可能会因URL查询字符串中的属性而有所不同,如果是移动设备呈现,SSL与非SSL、域名或当前会话语言,引擎支持多个语言内容,所有内容都与同一页面关联,并根据会话语言有条件地输出

下面是一个缓存页面文件名示例: 2014-02-14-10-36-36^80^www.mydomain.com^M^L^T42^P41^A067036EF358F12A049740F035A7EE688DBB0033C19A70163D6C453DBC5B84F1889FFE2.php

以下是文件名的组成部分: 过期日期^port^域^mobileOrDesktop^语言^Template^页面^md5+sha1ofur.php

以下是所解释的组件:

过期日期:根据CMS中的content Manager条目计算出的该缓存文件过期的日期/时间。GLOB可以使用它过滤掉所有过期的文件,并通过CRON作业删除它们以进行清理。这也决定了缓存的页面是否足够新鲜,可以显示在代码的开头。 端口:80或443,表示是否通过SSL检索。根据SSL状态,内容可能会有条件地不同。 域:www.mywebsite.com多个域名可以附加到CMS安装,需要区分,以便具有相同请求URI的两个域不会显示彼此的内容 移动或桌面:M或D-允许相同的URL“嗅出”客户端并相应地提供内容。 语言:L如果不使用多种语言,L-ENG/L-GER/。。。如果使用多语言 模板:T so templateId 47将是T47-允许通过文件名轻松筛选,以识别使用给定模板的所有缓存页面,以便在CMS中修改模板时删除。 page:P因此pageId 12将是P12-允许通过文件名进行简单筛选,以识别在CMS中修改页面时要删除的给定页面的所有缓存版本。 md5+sha1OfURL.php:接受$\u SERVER['REQUEST\u URI'],并对其进行两次编码,一次是md5,一次是SHA1,将结果连接起来,以提供一个表示URL的合理唯一的ID,因为查询字符串可能会影响内容。 欢迎提出任何意见或建议。提前谢谢

一些想法:

您可能需要考虑内存缓存系统,如MyCache或ReDIS,而不是磁盘上的系统。读取和写入磁盘的io负载可能很大

有像SMARTY这样的模板引擎,它将为您完成很多这项繁重的工作

与使用globs搜索目录不同,您可能应该使用一致的哈希规则来保存文件名,然后使用第二组哈希来保存标记。例如,按如下方式保存所有文件:

$fname = sha1($GLOBALS['theURL4Cache'].ALL_THE_REST_OF_THOSE_VARIABLES);
$cachedir1 = substr($fname ,0,1);
$cachedir2 = substr($fname,0,2);
$finalname = 'parsed/page_cache/' . $cachedir1 . '/' .$cachedir2 .'/'. $fname . '.php';
这意味着一个sha1为abcdefgh的文件将保存为“parsed/page_cache/a/ab/abcdefgh.php”。这将有助于减少文件系统降级

现在,只要您知道文件名,就可以找到该文件-实际上,您正在使用目录路径作为一个大哈希表。对于您的标签,设置另一组哈希-这些哈希可以保存为磁盘、数据库或内存中的文件。每当一个页面被添加到一个标记中时,将sha1摘要或文件名添加到相应的哈希中。例如,在一个名为“parseHashes/is_mobile.php”的文件中,您可以使用如下数组

$arr = array(
    "abcd",
    "defg"
   );
每个键都是需要查找和/或删除的sha1摘要。当您需要操作标记中的文件集时,只需迭代存储的sha摘要,您就知道在哪里可以找到文件内容

尽管如此,您还是应该研究一种基于内存甚至基于数据库的解决方案。做
文件系统上的是大量工作,而且扩展性不好。

250k文件在一个目录中?那是。。。疯子每次运行glob操作时,您都会强制Linux加载目录文件并解析所有250k条目。您应该通过自动拆分为子目录来选择最小化,例如,如果您的文件哈希为abcdefg,则您应该将该文件设置为a/b/c/abcdefg类型的sub-dir模式。做任何最有意义的分层级别,并将每个目录的文件数保持在某个管理编号。谢谢,Marc B。我曾考虑过自动拆分为子目录,但后来遇到了其他问题,例如根据CRON清理的缓存过期日期进行搜索,以及基于pageId和templateId等其他因素进行搜索,以便在CMS中更新页面/模板时对缓存文件进行“智能”清理。基本上,它是从Peter那里偷来给Paul的。find可以根据inode数据扫描文件,例如上次访问时间或上次修改时间。简而言之,你有一个删除的扫描。你可以考虑将这些文件在一个单独的目录中与CRON一起查看吗?标记B:访问时间/上次修改时间的过滤将非常有效,所有的页面都有相同的过期时间,但不幸的是它们没有,它们可以通过页面或模板单独设置。好主意!该系统最初是一个基于数据库mysql的缓存系统,但我遇到了行/表锁定问题——在测试了当前基于文件的方法后,我发现它更快、更稳定。memcache解决方案是否能够管理250000页的数据量?。我将玩弄一下你推荐的一些组件,看看是否能满足其余的需求。你可能真的想看看Redis或Memcached。我认为Redis内置了标签,因此它可能非常适合您的用例。