作者选择了 /dev/color以作为 Write for Donations计划的一部分获得捐款。
介绍
大多数应用程序都依赖数据,无论是来自数据库还是API。从API中获取数据会将网络请求发送到API服务器,并作为响应返回数据。这些回程需要时间,可以增加应用程序对用户的响应时间。
为了解决这些问题,您可以缓存数据,以便应用程序向 API 发出单个请求,所有后续的数据请求将从缓存中获取数据。 Redis,一个存储数据在服务器内存储的内存数据库,是一个流行的工具来缓存数据。
在本教程中,您将构建一个从 RESTful API 获取数据的 Express应用程序,使用 axios
模块。接下来,您将更改应用程序以使用 'node-redis' 模块在 Redis 中存储从 API 获取的数据。
前提条件
要遵循教程,你需要:
- Node.js 环境设置在您的服务器上. 如果您在 Ubuntu 22.04,安装最新版本的 Node.js 和 npm 在 How To Install Node.js on Ubuntu 22.04中的选项 3 下面。 对于其他操作系统,请参阅 How to Install Node.js and Create a Local Development Environment系列。
- Redis 安装在您的服务器上。 如果您使用 Ubuntu 22.04,请遵循 How To Install and Secure Redis on Ubuntu 22.04的步骤 1 和 2)。 如果您在其他操作系统上工作,请参阅 How to Install and Secure Redis系列。
- 在您的服务器上安装了非同步编程的知识。
步骤1 - 设置项目
在此步骤中,您将安装该项目所需的依赖,并启动 Express 服务器. 在本教程中,您将创建一个包含不同类型的鱼类信息的wiki。
首先,使用mkdir
命令创建项目目录:
1mkdir fish_wiki
移动到目录:
1cd fish_wiki
使用npm
命令初始化package.json
文件:
1npm init -y
-y
选项会自动接受所有默认值。
当您运行npm init
命令时,它将在您的目录中创建package.json
文件,其中包含以下内容:
1[secondary_label Output]
2Wrote to /home/your_username/fish_wiki<^/package.json:
3
4{
5 "name": "fish_wiki",
6 "version": "1.0.0",
7 "description": "",
8 "main": "index.js",
9 "scripts": {
10 "test": "echo \"Error: no test specified\" && exit 1"
11 },
12 "keywords": [],
13 "author": "",
14 "license": "ISC"
15}
接下来,您将安装以下软件包:
express
: Node.js 的 Web 服务器框架.axios
: Node.js 的 HTTP 客户端,可用于 API 调用。node-redis
:允许您在 Redis 中存储和访问数据的 Redis 客户端。
要一起安装这三个包,请输入以下命令:
1npm install express axios redis
安装包后,您将创建一个基本的Express服务器。
使用nano
或您选择的文本编辑器,创建并打开server.js
文件:
1nano server.js
在server.js
文件中,输入以下代码来创建 Express 服务器:
1[label fish_wiki/server.js]
2const express = require("express");
3
4const app = express();
5const port = process.env.PORT || 3000;
6
7app.listen(port, () => {
8 console.log(`App listening on port ${port}`);
9});
在第二行中,您将app
变量设置为express
的实例,这为您提供了get
,post
,listen
等方法的访问权限。
在下一行中,您定义并将端口
变量分配给您希望服务器聆听的端口号,如果环境变量文件中没有端口号,则将使用3000
端口作为默认值。
最后,使用app
变量,您调用express
模块的listen()
方法来启动服务器的端口3000
。
保存并关闭文件。
使用node
命令运行server.js
文件来启动服务器:
1node server.js
控制台将记录类似于以下的消息:
1[secondary_label Output]
2App listening on port 3000
由于 Node.js 不会在文件更改时自动重新加载服务器,您现在将使用 CTRL + C 停止服务器,以便在下一步更新 server.js。
一旦您安装了依赖性并创建了Express服务器,您将从RESTful API中获取数据。
步骤 2 – 从 RESTful API 获取数据而无需缓存
在此步骤中,您将基于前一步的 Express 服务器来从 RESTful API 获取数据,而无需实现缓存,从而展示数据未存储在缓存中时会发生什么。
首先,在文本编辑器中打开server.js
文件:
1nano server.js
接下来,您将从 FishWatch API 获取数据,而 FishWatch API 将返回有关鱼类的信息。
在server.js
文件中,定义一个函数以以下突出代码请求API数据:
1[label fish_wiki/server.js]
2const express = require("express");
3const axios = require("axios");
4
5const app = express();
6const port = process.env.PORT || 3000;
7
8async function fetchApiData(species) {
9 const apiResponse = await axios.get(
10 `https://www.fishwatch.gov/api/species/${species}`
11 );
12 console.log("Request sent to the API");
13 return apiResponse.data;
14}
15
16app.listen(port, () => {
17 console.log(`App listening on port ${port}`);
18});
在第二行中,您导入轴心
模块,然后您定义一个非同步函数fetchApiData()
,该函数将物种
作为参数。
在函数中,你将axios
模块的get()
方法调用到你想要的方法来获取数据的 API 终端点,即本示例中的 FishWatch API。由于get()
方法实现了 promise,你将它用await
关键字预先调用,以解决承诺。一旦承诺得到解决并从 API 返回数据,你会调用console.log()
方法。
接下来,您将定义一个接受GET
请求的快递路线. 在您的server.js
文件中,用以下代码定义该路线:
1[label fish_wiki/server.js]
2...
3
4app.get("/fish/:species", getSpeciesData);
5
6app.listen(port, () => {
7 ...
8});
在前一个代码块中,你呼吁express
模块的get()
方法,它只听取GET
请求。
/fish/:species
: Express 将收听的终端点. 终端点采用路由参数:species
,捕捉了 URL 中的那个位置中输入的任何内容。
现在路线已定义,指定getSpeciesData
回调函数:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4}
5app.get("/fish/:species", getSpeciesData);
6...
getSpeciesData
函数是向express
模块的get()
方法传递的非同步处理函数,作为第二个参数。 getSpeciesData()
函数需要两个参数:一个 请求对象和一个 响应对象。
接下来,添加突出的代码来调用fetchApiData()
,以便在getSpeciesData()
调用函数中从API中获取数据:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 const species = req.params.species;
5 let results;
6
7 results = await fetchApiData(species);
8}
9...
在函数中,您从存储在req.params
对象中的终点中提取捕获的值,然后将其分配给品种
变量。
然后,您将用物种
变量作为参数调用fetchApiData()
函数,然后将fetchApiData()
函数调用预先调用等待
语法,因为它返回了一个承诺。
接下来,添加突出的代码来处理运行时错误:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 const species = req.params.species;
5 let results;
6
7 try {
8 results = await fetchApiData(species);
9 } catch (error) {
10 console.error(error);
11 res.status(404).send("Data unavailable");
12 }
13}
14...
您定义了 try/catch
块来处理运行时错误. 在 try
块中,您呼叫fetchApiData()
来从API中获取数据
如果遇到错误,则catch
块会记录错误并返回一个404
状态代码,并响应数据不可用
。
大多数 API 返回一个 404 状态代码,当它们没有特定查询的数据时,这会自动触发捕获
块来执行。 但是,FishWatch API 返回 200 状态代码,当没有该特定查询的数据时,返回一个空数。
要触发catch()
块,您需要检查数组是否空,并在如果
条件评估为 true 时发出错误。
要做到这一点,添加突出的代码:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 ...
5 try {
6 results = await fetchApiData(species);
7 if (results.length === 0) {
8 throw "API returned an empty array";
9 }
10 res.send({
11 fromCache: false,
12 data: results,
13 });
14 } catch (error) {
15 console.error(error);
16 res.status(404).send("Data unavailable");
17 }
18}
19...
一旦从 API 返回数据,如果声明会检查结果
变量是否为空. 如果满足条件,则使用扔
声明将API 返回一个空数组
的定制错误。
相反,如果结果
变量有数据,则如果
语句条件不会满足,因此,程序会跳过如果
块并执行响应对象的发送
方法,该方法会向客户端发送响应。
发送
方法采用具有以下属性的对象:
fromCache
:属性接受一个值,帮助您知道数据是否来自 Redis 缓存或 API. 您现在分配了一个false
值,因为数据来自 API.data
:属性分配给包含 API 返回数据的results
变量。
此时,您的完整代码将看起来像这样:
1[label fish_wiki/server.js]
2
3const express = require("express");
4const axios = require("axios");
5
6const app = express();
7const port = process.env.PORT || 3000;
8
9async function fetchApiData(species) {
10 const apiResponse = await axios.get(
11 `https://www.fishwatch.gov/api/species/${species}`
12 );
13 console.log("Request sent to the API");
14 return apiResponse.data;
15}
16
17async function getSpeciesData(req, res) {
18 const species = req.params.species;
19 let results;
20
21 try {
22 results = await fetchApiData(species);
23 if (results.length === 0) {
24 throw "API returned an empty array";
25 }
26 res.send({
27 fromCache: false,
28 data: results,
29 });
30 } catch (error) {
31 console.error(error);
32 res.status(404).send("Data unavailable");
33 }
34}
35
36app.get("/fish/:species", getSpeciesData);
37
38app.listen(port, () => {
39 console.log(`App listening on port ${port}`);
40});
现在一切都已经完成了,保存并退出您的文件。
启动快递服务器:
1node server.js
Fishwatch API 接受了许多物种,但我们只将使用红鱼
物种作为终点的路线参数,您将在本教程中测试。
现在,在您的本地计算机上启动您最喜欢的网页浏览器。 导航到 http://localhost:3000/fish/red-snapper
URL。
<$>[注] 注: 如果您在远程服务器上遵循教程,则可以使用端口转发在浏览器中查看应用程序。
如果 Node.js 服务器仍在运行,请在本地计算机上打开另一台终端,然后输入以下命令:
1ssh -L 3000:localhost:3000 your-non-root-user@yourserver-ip
当您连接到服务器时,请在本地计算机网页浏览器上导航到http://localhost:3000/fish/red-snapper
。
一旦页面加载,你应该看到fromCache
设置为false
。
现在,再更新URL三次,再看看您的终端,终端将登录向 API 发送请求
等您更新浏览器的次数。
如果您在初次访问后三次更新 URL,则输出将如下:
1[secondary_label Output]
2
3App listening on port 3000
4Request sent to the API
5Request sent to the API
6Request sent to the API
7Request sent to the API
此输出显示,每次更新浏览器时都会发送网络请求到API服务器. 如果您有一个应用程序,1000个用户打到相同的端点,那就是1000个网络请求发送到API。
当您实现缓存时,对API的请求只会进行一次,所有后续请求都会从缓存中获取数据,从而提高应用程序的性能。
现在,停止您的Express服务器使用CTRL+C
。
现在,您可以从 API 请求数据并为用户提供服务,您将在 Redis 中缓存从 API 返回的数据。
步骤 3 — 使用 Redis 缓存 RESTful API 请求
在本节中,您将从 API 缓存数据,以便只有首次访问您的应用终端点才会从 API 服务器中请求数据,所有下面的请求都会从 Redis 缓存中获取数据。
打开server.js
文件:
1nano server.js
在server.js
文件中,导入node-redis
模块:
1[label fish_wiki/server.js]
2
3const express = require("express");
4const axios = require("axios");
5const redis = require("redis");
6...
在同一文件中,使用node-redis
模块连接到Redis,通过添加突出的代码:
1[label fish_wiki/server.js]
2
3const express = require("express");
4const axios = require("axios");
5const redis = require("redis");
6
7const app = express();
8const port = process.env.PORT || 3000;
9
10let redisClient;
11
12(async () => {
13 redisClient = redis.createClient();
14
15 redisClient.on("error", (error) => console.error(`Error : ${error}`));
16
17 await redisClient.connect();
18})();
19
20async function fetchApiData(species) {
21 ...
22}
23...
首先,您定义了redisClient
变量,该值设置为 undefined。之后,您定义了一个匿名自召非同步函数,这是一个在定义后立即运行的函数。您定义了一个匿名自召非同步函数,将无名函数定义包含在对面 (async () => {...})
. 为了使其自召,您立即跟随它,另一个对面 ()
,最终看起来像 (async () => {...})()
。
在该函数中,您呼吁redis
模块的createClient()
方法创建一个redis
对象.由于您在呼吁createClient()
方法时没有提供用于Redis的端口,因此Redis将使用6379
端口,即默认端口。
您还将 Node.js 命名为on()
方法,该方法记录了 Redis 对象上的事件。on()
方法使用两个参数:error
和callback
。第一个参数error
是当 Redis 遇到错误时触发的事件。第二个参数是callback
,在发出error
事件时运行。
最后,您调用了connect()
方法,该方法在默认端口6379
上启动了与 Redis 的连接。
现在,您的应用已经连接到 Redis,您将修改getSpeciesData()
回调,以便在初次访问时在 Redis 中存储数据,并从缓存中获取所有随后的请求的数据。
在server.js
文件中,添加并更新突出的代码:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 const species = req.params.species;
5 let results;
6 let isCached = false;
7
8 try {
9 const cacheResults = await redisClient.get(species);
10 if (cacheResults) {
11 isCached = true;
12 results = JSON.parse(cacheResults);
13 } else {
14 results = await fetchApiData(species);
15 if (results.length === 0) {
16 throw "API returned an empty array";
17 }
18 }
19
20 res.send({
21 fromCache: isCached,
22 data: results,
23 });
24 } catch (error) {
25 ...
26 }
27}
28...
在getSpeciesData
函数中,您将isCached
变量定义为false
。在try
块中,您将get()
方法称为node-redis
模块的get()
方法,作为参数为species
。当该方法在Redis中找到匹配species
变量值的关键时,它会返回数据,然后分配给cacheResults
变量。
接下來,一個「如果」聲明會檢查「cacheResults」變量是否有數據。如果符合條件,則「isCache」變量會被指定為「true」。接著,你會召喚「JSON」對象的「parse()」方法,以「cacheResults」為論點。「parse()」方法會將JSON串數據轉換為JavaScript對象。在JSON被解析後,你會召喚「send()」方法,該方法會將具有「fromCache」屬性設定為「isCached」變量的對象召喚。
如果node-redis
模块的get()
方法在缓存中找不到任何数据,那么cacheResults
变量将被设置为null
。因此,if
陈述将被评估为 false。
要将数据存储在Redis缓存中,您需要使用node-redis
模块的set()
方法来保存它。
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 const species = req.params.species;
5 let results;
6 let isCached = false;
7
8 try {
9 const cacheResults = await redisClient.get(species);
10 if (cacheResults) {
11 isCached = true;
12 results = JSON.parse(cacheResults);
13 } else {
14 results = await fetchApiData(species);
15 if (results.length === 0) {
16 throw "API returned an empty array";
17 }
18 await redisClient.set(species, JSON.stringify(results));
19 }
20
21 res.send({
22 fromCache: isCached,
23 data: results,
24 });
25 } catch (error) {
26 ...
27 }
28}
29...
在else
块中,一旦数据被提取,你会调用node-redis
模块的set()
方法来将数据存储在species
变量中的值的关键名下。
set()
方法使用两个参数,这些参数是关键值对: species
和 JSON.stringify(results)
。
第一个参数物种
是数据将在 Redis 中存储的密钥,请记住物种
被设置为您定义的端点传递的值,例如,当您访问/fish/red-snapper
时,物种
被设置为red-snapper
,这将在 Redis 中成为密钥。
第二个参数是JSON.stringify(results)
的值,第二个参数是JSON’s
的stringify()
方法,参数是results
变量,其中包含了从API返回的数据。该方法将JSON转换为字符串;这就是为什么,当您使用node-redis
模块的get()
方法提前从缓存中获取数据时,您提到了JSON.parse
方法,参数是cacheResults
。
您的完整文件现在将看起来如下:
1[label fish_wiki/server.js]
2const express = require("express");
3const axios = require("axios");
4const redis = require("redis");
5
6const app = express();
7const port = process.env.PORT || 3000;
8
9let redisClient;
10
11(async () => {
12 redisClient = redis.createClient();
13
14 redisClient.on("error", (error) => console.error(`Error : ${error}`));
15
16 await redisClient.connect();
17})();
18
19async function fetchApiData(species) {
20 const apiResponse = await axios.get(
21 `https://www.fishwatch.gov/api/species/${species}`
22 );
23 console.log("Request sent to the API");
24 return apiResponse.data;
25}
26
27async function getSpeciesData(req, res) {
28 const species = req.params.species;
29 let results;
30 let isCached = false;
31
32 try {
33 const cacheResults = await redisClient.get(species);
34 if (cacheResults) {
35 isCached = true;
36 results = JSON.parse(cacheResults);
37 } else {
38 results = await fetchApiData(species);
39 if (results.length === 0) {
40 throw "API returned an empty array";
41 }
42 await redisClient.set(species, JSON.stringify(results));
43 }
44
45 res.send({
46 fromCache: isCached,
47 data: results,
48 });
49 } catch (error) {
50 console.error(error);
51 res.status(404).send("Data unavailable");
52 }
53}
54
55app.get("/fish/:species", getSpeciesData);
56
57app.listen(port, () => {
58 console.log(`App listening on port ${port}`);
59});
保存和退出您的文件,并使用节点
命令运行server.js
:
1node server.js
一旦服务器启动,请在浏览器中更新http://localhost:3000/fish/red-snapper
。
注意fromCache
仍然设置为false
:
现在重新刷新页面,以便看到这次fromCache
设置为true
:
刷新页面五次,然后返回终端. 您的输出将看起来如下:
1[secondary_label Output]
2App listening on port 3000
3Request sent to the API
现在,向 API 发送的请求
仅在多次更新 URL 后一次登录,这与每一次更新登录消息的最后一节相矛盾。
要进一步确认数据存储在 Redis 中,请使用CTRL+C
来停止您的服务器。
1redis-cli
在red-snapper
键下获取数据:
1get red-snapper
您的输出将如下(编辑为简要):
1[secondary_label Output]
2"[{\"Fishery Management\":\"<ul>\\n<li><a...3\"}]"
输出显示了 API 在访问 /fish/red-snapper
终端时返回的 JSON 数据的串行版本,这确认了 API 数据存储在 Redis 缓存中。
退出 Redis 服务器客户端:
1exit
现在您可以从 API 缓存数据,您也可以设置缓存有效性。
步骤 4 – 实施缓存有效性
在缓存数据时,您需要知道数据的变化频率。一些API数据在几分钟内发生变化;其他在几个小时、几周、几个月或几年内发生变化。
在此步骤中,您将为需要在 Redis 中存储的 API 数据设置缓存有效性. 当缓存到期时,您的应用程序将向 API 发送请求以获取最近的数据。
您需要查阅您的 API 文档以设置缓存的正确期限。大多数文档都会提到数据的更新频率。然而,有些情况下文档不提供信息,因此您可能需要猜测。
一旦你选择了缓存长度,你需要将其转换为秒。为了在本教程中进行演示,你会将缓存长度设置为3分钟或180秒。
要实施缓存有效期,请打开 server.js
文件:
1nano server.js
添加突出的代码:
1[label fish_wiki/server.js]
2const express = require("express");
3const axios = require("axios");
4const redis = require("redis");
5
6const app = express();
7const port = process.env.PORT || 3000;
8
9let redisClient;
10
11(async () => {
12 ...
13})();
14
15async function fetchApiData(species) {
16 ...
17}
18
19async function getSpeciesData(req, res) {
20 const species = req.params.species;
21 let results;
22 let isCached = false;
23
24 try {
25 const cacheResults = await redisClient.get(species);
26 if (cacheResults) {
27 isCached = true;
28 results = JSON.parse(cacheResults);
29 } else {
30 results = await fetchApiData(species);
31 if (results.length === 0) {
32 throw "API returned an empty array";
33 }
34 await redisClient.set(species, JSON.stringify(results), {
35 EX: 180,
36 NX: true,
37 });
38 }
39
40 res.send({
41 fromCache: isCached,
42 data: results,
43 });
44 } catch (error) {
45 console.error(error);
46 res.status(404).send("Data unavailable");
47 }
48}
49
50app.get("/fish/:species", getSpeciesData);
51
52app.listen(port, () => {
53 console.log(`App listening on port ${port}`);
54});
在node-redis
模块的set()
方法中,您通过具有以下属性的对象的第三个参数:
EX
:接受在秒内缓存时间的值NX
:当设置为true
时,它确保set()
方法只能设置在 Redis 中尚不存在的密钥
保存和退出您的文件。
回到 Redis 服务器客户端来测试缓存的有效性:
1redis-cli
在 Redis 中删除red-snapper
键:
1del red-snapper
退出 Redis 客户端:
1exit
现在,用node
命令启动开发服务器:
1node server.js
返回浏览器并更新 http://localhost:3000/fish/red-snapper` URL. 在接下来的三分钟内,如果您更新URL,终端的输出应与以下输出一致:
1[secondary_label Output]
2App listening on port 3000
3Request sent to the API
过了三分钟后,请在浏览器中更新URL. 在终端中,您应该看到向API发送请求
已经登录了两次。
1[secondary_label Output]
2App listening on port 3000
3Request sent to the API
4Request sent to the API
此输出显示缓存已过期,并再次向 API 提出请求。
您可以停用 Express 服务器。
现在您可以设置缓存有效性,您将使用中间件下一步缓存数据。
步骤 5 – 在中间件中缓存数据
在此步骤中,您将使用 Express middleware 来缓存数据. Middleware 是一个可以访问请求对象、响应对象和应对对象的函数,该函数应该在执行后运行。
要在应用程序中使用中间件来缓存,您将修改getSpeciesData()
处理函数以从API中获取数据并将其存储在Redis中。
当您访问/fish/:species
终端时,中间件函数将首先运行以搜索缓存中的数据;如果找到,它将返回响应,而getSpeciesData
函数不会运行。
首先,打开您的server.js
:
1nano server.js
接下来,删除突出的代码:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 const species = req.params.species;
5 let results;
6 let isCached = false;
7
8 try {
9 const cacheResults = await redisClient.get(species);
10 if (cacheResults) {
11 isCached = true;
12 results = JSON.parse(cacheResults);
13 } else {
14 results = await fetchApiData(species);
15 if (results.length === 0) {
16 throw "API returned an empty array";
17 }
18 await redisClient.set(species, JSON.stringify(results), {
19 EX: 180,
20 NX: true,
21 });
22 }
23
24 res.send({
25 fromCache: isCached,
26 data: results,
27 });
28 } catch (error) {
29 console.error(error);
30 res.status(404).send("Data unavailable");
31 }
32}
33...
在getSpeciesData()
函数中,您删除所有搜索在Redis中存储的数据的代码,您还删除isCached
变量,因为函数getSpeciesData()
只会从API中获取数据并将其存储在Redis中。
一旦代码被删除,将fromCache
设置为false
,如下所示,因此getSpeciesData()
函数将如下:
1[label fish_wiki/server.js]
2...
3async function getSpeciesData(req, res) {
4 const species = req.params.species;
5 let results;
6
7 try {
8 results = await fetchApiData(species);
9 if (results.length === 0) {
10 throw "API returned an empty array";
11 }
12 await redisClient.set(species, JSON.stringify(results), {
13 EX: 180,
14 NX: true,
15 });
16
17 res.send({
18 fromCache: false,
19 data: results,
20 });
21 } catch (error) {
22 console.error(error);
23 res.status(404).send("Data unavailable");
24 }
25}
26...
getSpeciesData()
函数从API中获取数据,将其存储在缓存中,并返回用户的响应。
接下来,添加以下代码来定义 Redis 中的缓存数据中间件函数:
1[label fish_wiki/server.js]
2...
3async function cacheData(req, res, next) {
4 const species = req.params.species;
5 let results;
6 try {
7 const cacheResults = await redisClient.get(species);
8 if (cacheResults) {
9 results = JSON.parse(cacheResults);
10 res.send({
11 fromCache: true,
12 data: results,
13 });
14 } else {
15 next();
16 }
17 } catch (error) {
18 console.error(error);
19 res.status(404);
20 }
21}
22
23async function getSpeciesData(req, res) {
24...
25}
26...
cacheData()
中间件函数采用三个参数:req
,res
和next
。在try
块中,该函数检查species
变量中的值是否有数据存储在 Redis 中,如果数据处于 Redis,则返回并设置为cacheResults
变量。
接下来,如果
声明会检查cacheResults
是否有数据,如果results
变量被评估为 true,则将数据保存到results
变量中,然后中间软件会使用send()
方法返回一个对象,其属性fromCache
设置为true
和data
设置为results
变量。
但是,如果如果
语句被评估为假,执行会切换到else
块,在else
块中,你会调用next()
,然后将控制传递给下一个应该执行的函数。
若要將「cacheData()」中間軟件的控制轉移到「getSpeciesData()」函數時,當「next()」被召喚時,請相應地更新「express」模組的「get()」方法:
1[label fish_wiki/server.js]
2...
3app.get("/fish/:species", cacheData, getSpeciesData);
4...
现在get()
方法将cacheData
作为其第二个参数,这是中间软件,在Redis中搜索缓存数据并在找到时返回响应。
现在,当您访问 /fish/:species' 终端时,
cacheData()' 首先执行。如果数据被缓存,它会返回响应,请求响应周期在这里结束.但是,如果在缓存中没有数据,则将召唤 getSpeciesData()
来从 API 获取数据,将其存储在缓存中,然后返回响应。
现在完整的文件将看起来像这样:
1[label fish_wiki/server.js]
2
3const express = require("express");
4const axios = require("axios");
5const redis = require("redis");
6
7const app = express();
8const port = process.env.PORT || 3000;
9
10let redisClient;
11
12(async () => {
13 redisClient = redis.createClient();
14
15 redisClient.on("error", (error) => console.error(`Error : ${error}`));
16
17 await redisClient.connect();
18})();
19
20async function fetchApiData(species) {
21 const apiResponse = await axios.get(
22 `https://www.fishwatch.gov/api/species/${species}`
23 );
24 console.log("Request sent to the API");
25 return apiResponse.data;
26}
27
28async function cacheData(req, res, next) {
29 const species = req.params.species;
30 let results;
31 try {
32 const cacheResults = await redisClient.get(species);
33 if (cacheResults) {
34 results = JSON.parse(cacheResults);
35 res.send({
36 fromCache: true,
37 data: results,
38 });
39 } else {
40 next();
41 }
42 } catch (error) {
43 console.error(error);
44 res.status(404);
45 }
46}
47async function getSpeciesData(req, res) {
48 const species = req.params.species;
49 let results;
50
51 try {
52 results = await fetchApiData(species);
53 if (results.length === 0) {
54 throw "API returned an empty array";
55 }
56 await redisClient.set(species, JSON.stringify(results), {
57 EX: 180,
58 NX: true,
59 });
60
61 res.send({
62 fromCache: false,
63 data: results,
64 });
65 } catch (error) {
66 console.error(error);
67 res.status(404).send("Data unavailable");
68 }
69}
70
71app.get("/fish/:species", cacheData, getSpeciesData);
72
73app.listen(port, () => {
74 console.log(`App listening on port ${port}`);
75});
保存和退出您的文件。
要正确地测试缓存,您可以在 Redis 中删除红快递
键,然后进入 Redis 客户端:
1redis-cli
删除Red-snapper
键:
1del red-snapper
退出 Redis 客户端:
1exit
现在,运行server.js
文件:
1node server.js
一旦服务器启动,返回浏览器并再次访问http://localhost:3000/fish/red-snapper
。
终端会记录向 API 发送请求的消息.中间软件 cacheData()
将为接下来的三分钟服务所有请求. 如果您在四分钟的时间段中随机更新 URL,您的输出将看起来类似:
1[secondary_label Output]
2App listening on port 3000
3Request sent to the API
4Request sent to the API
该行为与应用程序在上一节中如何运作一致。
您现在可以使用 middleware 在 Redis 中缓存数据。
结论
在本文中,您构建了一种从 API 获取数据并返回数据的应用程序,然后将应用程序修改为在初次访问时在 Redis 中缓存 API 响应,并为所有后续请求提供缓存数据。
作为下一步,您可以探索 Node Redis的文档,了解更多关于在node-redis
模块中可用的功能. 您也可以阅读 Axios和 Express的文档,以深入了解本教程中涵盖的主题。
要继续建立 Node.js 技能,请参阅 如何在 Node.js 系列中编码。