最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Redis 在 Node.js 中的应用

    正文概述 掘金(清风晴雨)   2021-03-20   587

    相信很多开发者都了解 Redis,至少听说过它。

    Redis 是为集群应用提供分布式缓存机制而闻名。然而,这只是它其中的一个亮点。

    Redis 是一个功能强大、通用的内存数据库。强大是因为它非常快;通用是因为它可以处理缓存、类似数据库的特性、会话管理、实时分析、事件流等...

    但是,当使用 Redis 作为常规数据库时,必须注意内存中的部分。

    在本文中,我们将探索 Redis 缓存模式的一些最有趣的细微差别,使用 Node.js 作为环境来运行一些基准测试。让我们开始吧!

    缓存的类型

    你可能听说过。随着建立在关系数据库之上的系统快速增长,为了获得更好的性能,通常需要在查询方面减少一些压力。

    缓存,作为一种物理实现,在系统应用中随处可见:从数据库层本身到应用服务层,甚至作为远程分布式独立服务(就像 Redis )。

    在继续探索之前,我们先来看下这些类型。

    Type 1:数据库的集成式缓存

    根据你所遵循的系统设计,数据库集成可以帮助你的系统提高一些处理性能。

    例如,如果你在读取数据时使用 CQRS 将负载驱动到 NoSQL 数据库,而在写入数据时将负载驱动到关系数据库,那么这可能是一种类似数据库的集成形式,以实现缓存。

    然而,这是容易出错的,并且需要大量的人力来运行它,更不用说维护它了。

    其他的数据库,如 Aurora 提供了在数据库级别启用缓存的内置机制。换句话说,应用层和客户端不需要知道缓存的存在,因为数据库体系结构本身负责所有事情:通过内部复制逻辑到达时进行更新。

    Redis 在 Node.js 中的应用

    显然,在内存和跨集群实例的数据同步方面存在一些限制。但是,给定正确的用例场景,这将是一个可以考虑使用的强大集成。

    Type 2:本地应用缓存

    编程式缓存是最常用的类型之一,因为它们只是存储数据的内存结构。

    每一种编程语言都有其内置的或社区驱动的库,可以很快轻松地提供本地缓存。

    它的特点就是超级快。数据在内存中,因此你可以快速地访问到数据,比通过像是 TCP 请求来获取数据快得多。

    另外,如果你工作在一个分布式微服务的环境世界中,那么集群中的每个节点都拥有自己的版本化数据集,这些数据不会在其他节点之间共享,更不可能在某个节点突然关闭时丢失的所有数据了。

    Type 3:远程缓存(Redis)

    通常,这种类型的缓存也称为端缓存,这意味着它作为服务存在于其他地方,而不是你的应用或数据库。

    因为它们是远程工作的,所以它们必须在极端环境下表现良好。这就是为什么它们通常可以在几毫秒内处理大量数据加载的原因。

    选择这种类型的缓存是需要讨论和考虑的。例如,你有关于你的(或你的提供商的)网络延迟的详细信息吗?你能水平扩展你的 Redis 集群吗?

    由于应用程序与外部之间存在通信,所以当处理数据失败或变得太慢时,必须有一个计划。开发人员通常通过混合使用本地和远程缓存策略来解决这个问题,这将为他们在边缘情况下提供第二个保护屏障。

    缓存的模式

    同样,根据你的系统需求,实现缓存的方式可以根据实际情况而变化。

    让我们花点时间来分析一下最常见的缓存模式。

    Cache-aside 模式

    这是最常用的缓存模式。顾名思义,它存在于的系统体系中的不同地方(应用程序除外)。

    应用程序负责 缓存数据库 之间的编排,这样会是最好的。请看下图:

    Redis 在 Node.js 中的应用

    Cache-aside 执行流:

    1. 第一步是检查缓存,看看是否有所需的数据。如果成功,应用程序将信息返回给客户端,而不调用数据库。
    2. 如果没有,应用程序将转到数据库获取最新的信息。
    3. 最后,一旦有了最新版本的数据,应用程序就决定对缓存进行写入,使其也能感知。

    这种策略有很多好处,比如可以灵活地处理缓存和数据库中完全不同的数据类型。需要对数据库进行仔细的考虑设计,因为对数据库的更改可能会变得非常痛苦。然而,缓存给了你更多的自由来使用更灵活的数据结构。

    请注意,在向数据库写入数据和缓存更新失败的情况下,上面图像中演示的策略可能会带来麻烦。对于这种情况,有备选方案很重要,比如 TTL(生存时间)设置,开发者为使缓存中的特定数据失效建立一个超时。这样,当缓存数据更新失败时,应用程序不会太长时间处理超时的数据。

    Write-Through 模式

    此模式采用与 Cache-saside 模式相反的方法。在这里,当检测到任何更改时,应用程序首先写入缓存,然后再转到数据库。

    这就是它的名字的来源,因为在进行最终的数据库写入之前,它都要经过缓存层。

    Redis 在 Node.js 中的应用

    Write-Through 执行流:

    在对这种策略建模时,必须非常小心,特别是在数据库写入失败的情况下。

    对于这种情况,你可以建立一个重试策略来尝试不惜一切代价将数据保存到数据库,或者抛出一个事务错误,回滚之前的缓存写入。

    这里,只需注意每个流的总体处理时间的相应增加。

    Redis For Node.js

    我们将在 Node.js 应用程序上运行一个基准测试,该应用程序将公开两个 API 端点:一个处理缓存的数据,另一个没有缓存。

    我们的目标是演示如何快速配置你的项目来使用 cache-aside 模式,同时对这两个 API 端点进行基准测试,看看 Redis 能为 REST API 的性能提升有多大。

    环境准备

    你可以按照官方的快速入门配置 redis 环境:

    wget http://download.redis.io/redis-stable.tar.gz
    tar xvzf redis-stable.tar.gz
    cd redis-stable
    make
    make install
    

    然后,执行 redis-server 命令启动 redis 服务:

    Redis 在 Node.js 中的应用

    接下来,我们创建一个项目文件夹,cd 进入到它。

    初始化并安装依赖:

    npm init
    npm install express redis axios
    

    最后,根目录创建 index.js 文件,添加如下代码:

    const axios = require("axios");
    const express = require("express");
    const redis = require("redis");
    
    const app = express();
    const redisClient = redis.createClient(6379); // Redis server started at port 6379
    const MOCK_API = "https://jsonplaceholder.typicode.com/users/";
    
    app.get("/users", (req, res) => {
      const email = req.query.email;
    
      try {
        axios.get(`${MOCK_API}?email=${email}`).then(function (response) {
          const users = response.data;
    
          console.log("User successfully retrieved from the API");
    
          res.status(200).send(users);
        });
      } catch (err) {
        res.status(500).send({ error: err.message });
      }
    });
    
    app.get("/cached-users", (req, res) => {
      const email = req.query.email;
    
      try {
        redisClient.get(email, (err, data) => {
          if (err) {
            console.error(err);
            throw err;
          }
    
          if (data) {
            console.log("User successfully retrieved from Redis");
    
            res.status(200).send(JSON.parse(data));
          } else {
            axios.get(`${MOCK_API}?email=${email}`).then(function (response) {
              const users = response.data;
              redisClient.setex(email, 600, JSON.stringify(users));
    
              console.log("User successfully retrieved from the API");
    
              res.status(200).send(users);
            });
          }
        });
      } catch (err) {
        res.status(500).send({ error: err.message });
      }
    });
    
    const PORT = process.env.PORT || 3000;
    app.listen(PORT, () => {
      console.log(`Server started at port: ${PORT}`);
    });
    

    该代码使用了一个外部 mock API JSONPlaceholder,这对于 API fake 非常有用。在本例中,我们将搜索一些虚拟用户数据。

    注意,端口 6379 是 Redis 的默认值,你可以在上图 redis 服务启动日志中可以看到。

    有两个 API。第一种基本上从用户的 API 中获取数据,中间没有缓存。通过这种方式,你可以看到后续的 HTTP 调用会给应用程序的总体性能增加多少重载。

    第二个 API 总是在 Redis 中检查给定的数据。如果数据的 key 是存在的,我们跳过 mock API 调用,直接从缓存中获取数据,否则,我们会继续获取信息并将结果存储到 Redis 中。

    很简单,对吧?让我们继续看看基准测试代码。首先,让我们添加一个 Node 库来帮助我们解决这个问题。我们将使用 api-benchmark 工具,因为它功能强大,能够为基准生成可视化报告。

    执行以下代码,安装 api-benchmark:

    npm install api-benchmark
    

    然后,在根目录创建另一个文件 benchmark.js , 并添加如下代码:

    var apiBenchmark = require("api-benchmark");
    const fs = require("fs");
    
    var services = {
      server1: "http://localhost:3000/",
    };
    var options = {
      minSamples: 100,
    };
    
    var routeWithoutCache = { route1: "users?email=Nathan@yesenia.net" };
    var routeWithCache = { route1: "cached-users?email=Nathan@yesenia.net" };
    
    apiBenchmark.measure(
      services,
      routeWithoutCache,
      options,
      function (err, results) {
        apiBenchmark.getHtml(results, function (error, html) {
          fs.writeFile("no-cache-results.html", html, function (err) {
            if (err) return console.log(err);
          });
        });
      }
    );
    
    apiBenchmark.measure(
      services,
      routeWithCache,
      options,
      function (err, results) {
        apiBenchmark.getHtml(results, function (error, html) {
          fs.writeFile("cache-results.html", html, function (err) {
            if (err) return console.log(err);
          });
        });
      }
    );
    

    我们将分别执行两个命令:一个没有缓存;另一个使用 redis 缓存。

    这里对每个 API 服务进行 100 个请求的负载,这对于为生产准备的压测不是理想的,但已经足够证明 Redis 的能力了。稍后你可以使用更多请求重新运行测试,以查看差距是如何增大的。

    执行 node index.js 命令,接着另起终端,执行 node benchmark.js

    如果一切顺利,你可能会在项目的根目录下看到两个新的 HTML 文件。在 web 浏览器中打开它们,可能会显示类似如下所示的结果:

    • Redis 缓存 API 的基准测试结果:

    Redis 在 Node.js 中的应用

    • 无缓存的 API 基准此时结果:

    Redis 在 Node.js 中的应用

    看下右侧面板中生成的统计数据。非缓存 API 在大约 11 秒内结束了测试,而redis 缓存 API 在大约 0.9 秒内就结束了测试。

    如果你在开发每秒接收大量请求的应用程序,那么这是一个非常不错的选择?。

    总结

    现实中还会有其他的缓存模式,但是,为了简单起见,我们只关注最流行和最强大的缓存模式。

    现代应用注重考虑性能。随着时间的推移,这个要求变得越来越严格,因为对应用程序请求的复杂性和数量呈指数级增长。

    Redis 只是众多缓存选项中的一个。事实上,它强大、灵活,深受许多公司和技术社区的喜爱。

    我建议你实现一下第二个缓存模式,即 write-through,对比与 cache-aside 在性能方面的区别。

    你可以在 GitHub 上看到本文中的示例代码。

    参考

    • Powerful Caching with Redis for Node.js Applications

    起源地下载网 » Redis 在 Node.js 中的应用

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元