【问题标题】:Caching Dynamically Generated Pages缓存动态生成的页面
【发布时间】:2018-06-18 08:24:46
【问题描述】:

我们正在寻求加速我们的网站。更具体地说,我们正在寻求降低 TTFB。我们的网站主要由基于url路径(提取主题)和url路径中的参数动态生成的页面组成。 这些条目被放入一个 sql 查询中,该查询从我们使用 php 执行的数据库中提取所有正确的数据。

问题来了:

这些查询可以完美地生成页面以及与之相关的所有信息(例如标签)。 但是,每次访问者访问页面时都重新运行代码会花费大量时间,从而导致 TTFB/服务器响应时间较长。本质上,这些页面只需每月使用一次 sql 查询更新一次。在这之间,应该可以将它们作为预加载/预生成的静态 HTML 页面提供服务(直到我们指示刷新)。我们目前正在使用 Cloudflare 作为 CDN,它在加快网站速度方面已经非常出色。现在,即使我们启用了“缓存所有内容”的页面规则,我们仍然可以看到它重新运行包括 sql 查询在内的 php 代码。

问题:

有人知道实现缓存网站动态部分这一目标的好方法吗?无论是使用 Cloudflare 还是通过其他方式?我知道 Akamai 提供这项服务,但显然,将网站切换到另一个 CDN 会产生一些转换成本,我们对 Cloudflare 相当满意。

提前致谢!

【问题讨论】:

  • 您是否看到它在每个请求时重新运行,或者只是有时?

标签: sql-server cloudflare response-time


【解决方案1】:

如果您的网站每天都有数百个访问者众多的页面,您可能希望为您的网站实施某种缓存机制以加快页面加载时间。每个客户端-服务器请求都包含多个数据库查询、服务器响应和处理时间,从而增加了整体页面加载时间。最常见的解决方案是制作称为缓存文件的动态页面的副本并将它们存储在单独的目录中,以后可以将其用作静态页面,而不是一次又一次地重新生成动态页面。 了解动态页面和缓存文件

缓存文件是由动态页面生成的静态副本,这些文件生成一次并存储在单独的文件夹中直到过期,当用户请求内容时,提供相同的静态文件而不是动态生成的页面,从而绕过需要使用服务器端代码一遍又一遍地重新生成 HTML 并从数据库中请求结果。例如,运行多个数据库查询,计算和处理 PHP 代码到 HTML 输出需要几秒钟,增加动态页面的整体页面加载时间,但是缓存文件仅包含纯 HTML 代码,您可以在任何文本编辑器中打开它或浏览器,这意味着它根本不需要处理时间。

动态页面:下图中的示例显示了如何生成动态页面。顾名思义,它是完全动态的,它与数据库对话并根据用户在请求期间提供的不同变量生成 HTML 输出。例如,用户可能想要列出特定作者的所有书籍,它可以通过向数据库发送查询并生成新的 HTML 内容来做到这一点,但每个请求都需要几秒钟的时间来处理,并且使用了特定的服务器内存,这不是如果网站的访问者很少,那就很重要了。但是,考虑到数百名访问者一遍又一遍地从您的网站请求和生成动态页面,这将大大增加压力,从而导致延迟输出和客户端浏览器上的 HTTP 错误。

动态页面示例

缓存文件:- 下图说明了如何提供缓存文件而不是动态页面,如上所述,缓存文件只不过是静态网页。它们包含纯 HTML 代码,缓存页面内容更改的唯一方法是 Web 开发人员手动编辑文件。正如您所见,缓存文件既不需要数据库连接,也不需要处理时间,它是持续降低服务器压力和页面加载时间的理想解决方案。

缓存文件示例 PHP 缓存

使用PHP缓存动态页面还有其他方法,但大家最常用的方法是PHP Output Buffer和Filesystem Functions,将这两种方法结合起来,我们就可以拥有华丽的缓存系统。

PHP 输出缓冲区:它有趣地提高了性能并减少了下载所需的时间,因为输出不是分段发送到浏览器,而是将整个 HTML 页面作为一个变量发送到浏览器。方法非常简单,看看下面的代码:

<?php
ob_start(); // start the output buffer

/* the content */
ob_get_contents();  gets the contents of the output buffer
ob_end_flush(); // Send the output and turn off output buffering
?>

当您在代码顶部调用 ob_start() 时,它会打开输出缓冲,这意味着此后的任何内容都将存储在缓冲区中,而不是在浏览器上输出。可以使用 ob_get_contents() 检索缓冲区中的内容。您应该在代码末尾调用 ob_end_flush() 将输出发送到浏览器并关闭缓冲。

PHP Filesystem :——你可能对PHP文件系统很熟悉,它是PHP核心的一部分,它允许我们读写文件系统。看看下面的代码。

$fp = fopen('/path/to/file.txt', 'w');  //open file for writing
fwrite($fp, 'I want to write this'); //write 
fclose($fp); //Close file pointer

你可以看到代码 fopen() 的第一行打开文件进行写入,模式‘w’将文件指针放在文件的开头,如果文件不存在,它会尝试创建一个。第二行 fwrite() 将字符串写入打开的文件,最后 fclose() 在代码开头关闭成功打开的文件。 实现 PHP 缓存

现在你应该很清楚 PHP 输出缓冲区和文件系统了,我们可以使用这两种方法来创建我们的 PHP 缓存系统。请看下图,流程图为我们提供了缓存系统的基本概念。

php缓存系统

循环从用户请求内容开始,我们只检查当前请求的页面是否存在缓存副本,如果不存在则生成新页面,创建缓存副本然后输出结果。如果缓存已经存在,我们只需要获取文件并将其发送到用户浏览器。

查看下面的完整 PHP 缓存代码,您可以将其复制并粘贴到您的 PHP 项目中,它应该可以完美地工作,如上图所示。您可以在代码中进行设置,修改缓存过期时间、缓存文件扩展名、忽略页面等。

<?php
//settings
$cache_ext  = '.html'; //file extension
$cache_time     = 3600;  //Cache file expires afere these seconds (1 hour = 3600 sec)
$cache_folder   = 'cache/'; //folder to store Cache files
$ignore_pages   = array('', '');

$dynamic_url    = 'http://'.$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . $_SERVER['QUERY_STRING']; // requested dynamic page (full url)
$cache_file     = $cache_folder.md5($dynamic_url).$cache_ext; // construct a cache file
$ignore = (in_array($dynamic_url,$ignore_pages))?true:false; //check if url is in ignore list

if (!$ignore && file_exists($cache_file) && time() - $cache_time < filemtime($cache_file)) { //check Cache exist and it's not expired.
ob_start('ob_gzhandler'); //Turn on output buffering, "ob_gzhandler" for the compressed page with gzip.
readfile($cache_file); //read Cache file
echo '<!-- cached page - '.date('l jS \of F Y h:i:s A', filemtime($cache_file)).', Page : '.$dynamic_url.' -->';
ob_end_flush(); //Flush and turn off output buffering
exit(); //no need to proceed further, exit the flow.
}
//Turn on output buffering with gzip compression.
ob_start('ob_gzhandler');
######## Your Website Content Starts Below #########
?>
<!DOCTYPE html>
<html>
<head>
<title>Page to Cache</title>
</head>
<body>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer ut tellus libero.
</body>
</html>
<?php
######## Your Website Content Ends here #########

if (!is_dir($cache_folder)) { //create a new folder if we need to
mkdir($cache_folder);
}
if(!$ignore){
$fp = fopen($cache_file, 'w');  //open file for writing
fwrite($fp, ob_get_contents()); //write contents of the output buffer in Cache file
fclose($fp); //Close file pointer
}
ob_end_flush(); //Flush and turn off output buffering

?>

您必须将您的 PHP 内容放在附上的注释行之间,事实上我建议将它们放在单独的页眉和页脚文件中,以便它可以为所有不同的动态页面生成和提供缓存文件。如果您仔细阅读代码中的注释行,您应该会发现它几乎不言自明。

【讨论】:

  • 您好,感谢您的详尽回答。因此,如果我理解正确,此代码将生成网站的缓存文件(如果尚未生成),如果已存在则调用它?这个缓存页面存储在哪里?例如,这是否仅缓存在到期时间内已经访问过的特定用户的文件(将其缓存在他们的浏览器中),或者它是否能够在生成一次后为每个人调用此缓存页面(缓存在页面内容本身)?对不起我的无知。到目前为止,我还不太熟悉这种缓存系统。再次感谢!
  • 伴侣。你刚刚在 0.5 秒内放下了我的 TTFB。疯了吧。非常感谢您提供代码!
  • 我一直在到处寻找这个,尝试自己编写代码,这解决了我的问题。将我所有的加载时间减少了 50% - 60%
  • 这个答案值得 600 票!谢谢!
【解决方案2】:

您可以使用 cloudflare 在边缘执行此操作以获得更好的性能。好处是您可以随时将域设置为“开发中”以查看代码更改 - 无需更改服务器端机制。

您的页面规则如下所示。注意带有通配符的 url 变量。这意味着具有该变量名称的任何 url 都将被缓存在边缘。您应该可以轻松地在 50 毫秒内看到 TTFB + 整个 html 下载。

还要注意有效期。你说一个月,所以我选择了那个设置。但我可能会将其设为“1 天”。只是为了让我能密切关注。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    • 2017-07-08
    • 1970-01-01
    • 2011-05-14
    • 2019-12-13
    • 1970-01-01
    相关资源
    最近更新 更多