使用 Redis 缓存使您的 SSR 站点闪电般快速

Redis 是一个内存存储,主要用作数据库。你可能听说过Redis,听说过它有多酷,但从来没有真正的用例。在本教程中,我将向您展示如何利用 Redis 来加速服务器端呈现 Server Side Rendered(SSR) Web 应用程序。如果您不熟悉 Redis,请查看我们关于安装 Redis 和创建键值对的指南,以更好地了解其工作原理。

在本文中,我们将介绍如何调整您的 Node.JS Express 应用程序,以使用 Redis 构建闪电般的快速缓存。

这样做的结果是相当戏剧性的。我能够将页面加载时间平均加快95%

问题背景#

关于我使用 Express 在服务器上呈现网页,并将其发送给用户。随着时间的流逝,我添加了更多的功能,服务器渲染的复杂性也增加了 – 例如,我最近在代码示例中添加了行号,相对而言,这需要在服务器上进行相当多的额外处理。服务器仍然非常快,但随着复杂性的增加,服务器计算可能会花费越来越长的时间。

最终,这意味着用户的页面加载时间较慢,尤其是对于那些使用慢速3G连接的用户。我已经关注这个问题一段时间了,因为我不仅希望每个人都喜欢快速阅读内容,而且还因为页面速度对SEO有重大影响。

当一个页面在code8cn.com上加载时,我运行一个像这样的快速路由:

import express from 'express';

const articleRouter = express.Router();
// Get Singular Article
articleRouter.get(['/article/:articleName/', async function(req, res, next) {
    // Process article
    // A variable to store all of our processed HTML
    let finalHtml = '';
    // A lot more Javascript goes here
    // ...
    // Finally, send the Html file to the user
    res.send(finalHtml);
});

每次有人加载页面时,文章都会从头开始处理 – 并且所有内容都会立即处理。这意味着需要几个不同的数据库调用,一些函数和一些计算非常复杂的DOM操作来创建代码。这并不令人担忧地复杂,但这也意味着服务器一直在做很多工作,以处理多个页面上的多个用户。fs.readFile

无论如何,随着事情的扩大,这将成为一个越来越大的问题。幸运的是,我们可以使用Redis来缓存页面,并立即向用户显示它们。

为什么使用 Redis 来缓存网页#

使用 Redis 进行缓存可以将您的 SSR 网站从一个缓慢笨拙的庞然大物转变为一个非常快速、响应迅速的应用程序。当我们在服务器端渲染内容时,我们最终会做很多打包,但最终产品是相同的 – 一个完整的HTML页面,交付给用户:

我们打包和处理响应的速度越快,用户的体验就越快。如果您的页面具有较高的进程负载,这意味着创建最终的已服务页面需要大量处理,则您有两个真正的选择:

  • 开始删除进程并优化代码。这可能是一个漫长的过程,但将导致服务器端的处理速度更快。
  • 使用 Redis,因此网页仅在后台处理,并且缓存的版本始终向用户显示。

老实说,你可能应该同时做这两件事 – 但Redis提供了最快的优化方式。

将 Redis 添加到您的快速路线#

首先,我们需要安装 Redis。如果您的服务器或计算机上未安装 Redis。您可以在此处找到如何安装Redis

接下来,通过运行以下命令将其安装在 Node.JS 项目中:

npm i redis

现在我们已经启动并运行了,我们可以开始更改我们的快速路线。还记得我们之前的路线是这样的吗?

import express from 'express';

const articleRouter = express.Router();
// Get Singular Article
articleRouter.get(['/article/:articleName/', async function(req, res, next) {
    // Process article
    // A variable to store all of our processed HTML
    let finalHtml = '';
    // A lot more Javascript goes here
    // ...
    // Finally, send the Html file to the user
    res.send(finalHtml);
});

让我们添加Redis:

import express from 'express';
import { createClient } from 'redis';

const articleRouter = express.Router();
// Get Singular Article
articleRouter.get(['/article/:articleName/', async function(req, res, next) {

    // Connect to Redis    
    const client = createClient();
    client.on('error', (err) => console.log('Redis Client Error', err));
    await client.connect();
    const articleCache = await client.get(req.originalUrl);
    const articleExpire = await client.get(`${req.originalUrl}-expire`);

    // We use redis to cache all articles to speed up content delivery to user
    // Parsed documents are stored in redis, and sent to the user immediately
    // if they exist
    if(articleCache !== null) {
        res.send(articleCache);
    }

    if(articleCache == null && articleExpire == null || articleExpire < new Date().getTime()) {

        // A variable to store all of our processed HTML
        let finalHtml = '';
        // A lot more Javascript goes here
        // ...
        // Finally, send the Html file to the user

        if(articleCache == null) {
            res.send(mainFile);
        }

        // We update every 10 seconds.. so content always remains roughly in sync.
        // So this not only increases speed to user, but also decreases server load
        await client.set(req.originalUrl, mainFile);
        await client.set(`${req.originalUrl}-expire`, new Date().getTime() + (10 * 1000));
    }
});

我们在这里所做的更改并不太复杂。在我们的代码中,我们只需要设置两个 Redis 键 – 一个用于存储页面的缓存 HTML 内容,另一个用于存储到期日期,因此我们可以确保内容始终是最新的。

代码摘要

让我们更详细地深入研究代码:

  • 首先,导入 Redis,以便可以通过 使用它。createClient
  • 每当用户转到我们的文章端点时,我们都会加载 Redis,而不是直接进入解析和显示文章。
  • 我们在 Redis 数据库中检查两个密钥 ()。一个关键是 。例如,此页面可能是 。另一个是到期日,存储在 中,即await client.get('key-name')req.currentUrl/article/redis-caching-ssr-site-nodejs`${req.currentUrl}-expire/article/redis-caching-ssr-site-nodejs-expire
  • 如果存在我们文章的缓存版本,我们会立即将其发送给用户 – 从而导致闪电般的快速页面加载。
  • 如果这是任何人第一次访问此页面,或者如果过期密钥已过期,那么我们必须长时间解析文章。
  • 您可能认为这意味着每10秒页面必须加载很长时间 – 但事实并非如此,如果存在缓存版本,我们将始终向用户发送缓存版本,但我们将每10秒更新一次缓存,以便最新内容可用。此更新对用户加载时间没有影响。
  • 因此,唯一发生缓慢加载的情况是此页面以前从未被访问过

由于我们使用 URL 来存储数据,因此我们可以确定,唯一路由将具有针对它们的唯一数据存储在我们的 Redis 数据库中。最终,这导致我的网站将第一个字节(TTFB)的时间改进为95%,如顶部的图像所示。

注意事项和结论#

由于我的路线变得过于复杂,因此在这里节省的时间确实非常大。如果您的路线不是超级复杂,那么您节省的时间可能会更少。但是,话虽如此,使用此方法您仍然有可能获得相当大的速度提升。

此示例证明了简单的更改如何对任何 SSR 站点产生巨大的性能影响,并且是 Redis 的一个很好的用例,显示了它的强大功能。我希望您发现这很有用,并找到一种在您自己的网站和应用程序上使用它的方法。