如何使用MemCachier在DigitalOcean应用平台上部署Express应用程序并进行扩展

_ 作者选择了 免费和开源基金作为 写给捐款计划的一部分接受捐款。

介绍

[Express] (https://expressjs.com/)是用Node来构建快网应用和API的通俗框架. DigitalOcean的App平台 是一个[Platform as a Service (PaaS) (https://andsky.com/tech/tutorials/what-is-platform-as-a-service-paas)产品,用于从代码寄存器中配置和部署应用程序. 它提供了一个快速有效的方法 部署你的快递应用。 在此教程中,您会在 DigitalOcean App 平台上部署一个快取应用程序,然后用 [DigitalOcean Marketplace Add-On for MemCachier] (https://marketplace.digitalocean.com/add-ons/memcachier) 添加缓存来缩放. MemCachier符合被嵌入对象缓存系统,但具有若干优点,如可用性高的更佳故障情景.

您将首先构建一个 Express 应用程序, 用于计算质数, 具有 Like 按钮, 并使用模板引擎 。 这些特点将使你日后能够执行若干隐蔽策略。 然后将您的应用程序代码推到 GitHub 上,并将其部署在 App 平台上. 最后,你将实施三种物品缓存技术,使你的应用更快,更可扩展. 到这个教程结束时,你就可以在 App 平台上部署一个 Express 应用程序, 执行缓存资源密集型计算的技术, 提供观点和会话.

前提条件

步骤 1 — 设置 Express 模板渲染视图

在此步骤中,您将安装 express 的模板引擎,为您的应用程序的首页路线(GET /)创建模板,并更新路线以使用该模板。模板引擎允许您稍后缓存渲染的视图,增加请求处理速度,减少资源使用量。

要开始,请与您的编辑器一起导航 Express 服务器的项目目录,如果它尚未打开,您可以回到 How To Get Started with Node.js and Express上的前提教程,以识别您已保存项目文件的位置。

您将为 Express 安装模板引擎,以便在应用程序中使用静态模板文件。模板引擎将模板文件中的变量替换为值,并将模板转换为 HTML 文件,以响应请求。

安装 嵌入式 JavaScript 模板 (ejs) 库. 如果您喜欢,您可以使用其他一个 模板引擎支持 Express,如 Mustache, Pug,或 Nunjucks。

1npm install ejs

随着ejs现在安装,您将配置您的Express应用程序以使用它。

在您的编辑器中打开server.js文件,然后添加突出的行:

 1[label server.js]
 2const express = require('express');
 3
 4const app = express();
 5
 6app.set('view engine', 'ejs');
 7
 8app.get('/', (req, res) => {
 9  res.send('Successful response.');
10});
11
12...

此行将 应用程序设置属性 view engine 设置为 ejs

保存檔案

<$>[注] 注: 在本教程中,您将使用视图引擎设置,但另一个有用的设置是视图

接下来,创建一个视图目录,然后创建视图/index.ejs文件,并在编辑器中打开它。

将初始模板标记添加到该文件:

 1[label views/index.ejs]
 2<!DOCTYPE html>
 3<html lang="en">
 4  <head>
 5    <meta charset="utf-8">
 6    <meta name="viewport" content="width=device-width, initial-scale=1">
 7    <title>Find the largest prime number</title>
 8  </head>
 9  <body>
10    <h1>Find the largest prime number</h1>
11
12    <p>
13      For any number N, find the largest prime number less than or equal to N.
14    </p>
15  </body>
16</html>

保存檔案

随着创建的模板,您将更新您的路线以使用它。

打开server.js文件并更新突出的代码:

1[label server.js]
2...
3
4app.get('/', (req, res) => {
5  res.render('index');
6});
7
8...

答案渲染方法将模板的名称作为其第一个参数,在这种情况下,索引与文件view/index.ejs匹配。

重新启动应用程序以加载更改。如果服务器正在运行,请在终端按CTRL+C

1node server.js

在您的 Web 浏览器中访问localhost:3000,该浏览器将显示您的模板的内容。

Your rendered template with a heading and paragraph

您的应用程序现在有一个模板渲染视图,但它还没有做任何事情. 您将添加功能来找到一个优质号码。

步骤 2 — 将功能添加到您的 Express 应用程序

在此步骤中,您将使用这些功能与应用程序进行交互,一旦在 [步骤 4] 中部署到 App Platform(https://andsky.com/tech/tutorials/how-to-deploy-an-express-application-and-scale-with-memcached-on-digitalocean-appplatform# step-4-deploying-on-app-platform)。

寻找一个主要号码

在本节中,您将添加一个函数到您的应用程序中,该函数会找到最大的原始数小于或等于N,其中N指任何数字。

),并附加)。

view/index.ejs中,添加一个包含输入元素的表单来输入N:

 1[label views/index.ejs]
 2...
 3
 4<p>
 5  For any number N, find the largest prime number less than or equal to N.
 6</p>
 7
 8<form action="/" method="get">
 9  <label>
10    N
11    <input type="number" name="n" placeholder="e.g. 10" required>
12  </label>
13  <button>Find Prime</button>
14</form>
15
16...

表单的操作提交到 /,该操作由服务器.js 中的主路径 app.get('/'...) 处理. 由于表单的方法设置为 get',则数据 `n' 将作为查询参数附加到操作 URL。

保存檔案

接下来,当使用n的查询参数创建请求时,您将将该数据传输到模板中。

在「server.js」中,添加突出的代码:

 1[label server.js]
 2...
 3
 4app.get('/', (req, res) => {
 5  const n = req.query.n;
 6
 7  if (!n) {
 8    res.render('index');
 9    return;
10  }
11
12  const locals = { n };
13  res.render('index', locals);
14});
15
16...

这些行会检查请求是否具有名为n的查询参数。

注意:用户输入不能总是值得信赖,因此生产准备的应用程序的最佳做法是使用图书馆验证输入,例如 Joi

渲染方法有第二个可选参数,即本地 这个参数定义了传给模板的本地变量来渲染视图。 短语属性名称定义了本地对象的n属性。

保存檔案

现在该模板有一些数据,您可以显示它。

视图/index.ejs中,添加突出的行以显示N的值:

 1[label views/index.ejs]
 2...
 3
 4<form action="/" method="get">
 5  <label>
 6    N
 7    <input type="number" name="n" placeholder="e.g. 10" required>
 8  </label>
 9  <button>Find Prime</button>
10</form>
11
12<% if (locals.n) { %>
13  <p>N: <%= n %></p>
14<% } %>
15
16...

如果本地变量n存在于此视图中,则要求应用显示它。

保存该文件,然后重新启动您的服务器以更新应用程序. 该表单现在将按钮加载到 Find Prime . 该应用程序将能够接受用户输入并在表单下显示。

Your rendered template now with a form to find a prime number

在提交表单后,URL将更改以包含一个n查询参数,如http://localhost:3000/?n=40,如果您输入了40

Your rendered template now showing the number submitted below the form

现在可以提交和显示N的值,您将添加一个函数以查找最大数小于或等于N

创建一个utils目录,然后创建一个utils/findPrime.js文件。

在编辑器中打开findPrime.js,并添加实数查找函数:

 1[label utils/findPrime.js]
 2/**
 3 * Find the largest prime number less than or equal to `n`
 4 * @param {number} n A positive integer greater than the smallest prime number, 2
 5 * @returns {number}
 6 */
 7module.exports = function (n) {
 8  let prime = 2; // initialize with the smallest prime number
 9  for (let i = n; i > 1; i--) {
10    let isPrime = true;
11    for (let j = 2; j < i; j++) {
12      if (i % j == 0) {
13        isPrime = false;
14        break;
15      }
16    }
17    if (isPrime) {
18      prime = i;
19      break;
20    }
21  }
22  return prime;
23};

一个 JSDoc评论文档函数. 该算法从第一个总数(2)开始,然后通过数字循环,从 n开始,并在每个循环中减少数字为 1。 该函数将继续循环并寻找一个总数,直到数字是 2,最小的总数。

每个循环假设当前数是一个总数,然后测试这个假设. 它会检查当前数是否具有1和自身以外的因子. 如果当前数可以由任何大于1和小于自己而没有剩余的数分开,那么它不是一个总数. 然后该函数将尝试下一个数。

保存檔案

接下来,将查找主要函数导入到server.js:

1[label server.js]
2const express = require('express');
3const findPrime = require('./utils/findPrime');
4
5...

更新您的主路由控制器以找到一个主要号码并将其值传递到模板中. 在server.js中,添加突出的代码:

 1[label server.js]
 2...
 3
 4app.get('/', (req, res) => {
 5  const n = req.query.n;
 6
 7  if (!n) {
 8    res.render('index');
 9    return;
10  }
11
12  const prime = findPrime(n);
13
14  const locals = { n, prime };
15  res.render('index', locals);
16});
17
18...

保存檔案

现在,您将添加代码,以在模板中显示结果. 在view/index.ejs中,显示N的值:

 1[label views/index.ejs]
 2...
 3
 4<form action="/" method="get">
 5  <label>
 6    N
 7    <input type="number" name="n" placeholder="e.g. 10" required>
 8  </label>
 9  <button>Find Prime</button>
10</form>
11
12<% if (locals.n && locals.prime) { %>
13  <p>
14    The largest prime number less than or equal to <%= n %> is <strong><%= prime %></strong>.
15  </p>
16<% } %>
17...

保存檔案

现在重新启动服务器。

例如,本教程将使用10。如果您提交数字10,您将收到一个答案,表示最大数小于或等于10是7

您的应用程序现在可以选择一个号码,然后找到并显示一个主要号码,然后您将添加一个喜欢按钮。

添加一个喜欢的按钮

目前,您的应用程序可以根据提交的每个数字N产生不同的视图。除了更新文本外,这些视图的内容可能会保持不变。您将在本节中添加的 Like 按钮将提供更新视图的内容的方法。

使用一个 Like 按钮,应用程序需要某个地方存储类似的数据. 虽然持久存储是理想的,但你会将喜欢存储在内存中,因为实施数据库超出了本教程的范围。

打开「server.js」以添加以下变量:

 1[label server.js]
 2...
 3
 4app.set('view engine', 'ejs');
 5
 6/**
 7 * Key is `n`
 8 * Value is the number of 'likes' for `n`
 9 */
10const likesMap = {};
11
12...

LikesMap对象被用作地图来存储所有所请求的数字的喜欢. 密钥是n,其值是n的喜欢数。

请在server.js中添加突出的行,以便为N初始化喜欢:

 1[label server.js]
 2...
 3
 4  const prime = findPrime(n);
 5
 6  // Initialize likes for this number when necessary
 7  if (!likesMap[n]) likesMap[n] = 0;
 8
 9  const locals = { n, prime };
10  res.render('index', locals);
11
12...

如果语句检查当前号码的喜欢是否存在. 如果没有喜欢,那么喜欢Maps号码将初始化为0

接下来,添加喜欢作为视图的本地变量:

 1[label server.js]
 2...
 3
 4  const prime = findPrime(n);
 5
 6  // Initialize likes for this number when necessary
 7  if (!likesMap[n]) likesMap[n] = 0;
 8
 9  const locals = { n, prime, likes: likesMap[n] };
10  res.render('index', locals);
11
12...

保存檔案

现在视图有喜欢数据,您可以显示其值并添加一个 喜欢 按钮。

view/index.ejs中,添加喜欢按钮标记:

 1[label views/index.ejs]
 2...
 3
 4<% if (locals.n && locals.prime) { %>
 5  <p>
 6    The largest prime number less than or equal to <%= n %> is <strong><%= prime %></strong>.
 7  </p>
 8
 9  <form action="/like" method="get">
10    <input type="hidden" name="n" value="<%= n %>">
11    <input type="submit" value="Like"> <%= likes %>
12  </form>
13<% } %>
14...

您的完成的文件现在应该匹配如下:

 1[label views/index.ejs]
 2<!DOCTYPE html>
 3<html lang="en">
 4  <head>
 5    <meta charset="utf-8">
 6    <meta name="viewport" content="width=device-width, initial-scale=1">
 7    <title>Find the largest prime number</title>
 8  </head>
 9  <body>
10    <h1>Find the largest prime number</h1>
11
12    <p>
13      For any number N, find the largest prime number less than or equal to N.
14    </p>
15
16    <form action="/" method="get">
17      <label>
18        N
19        <input type="number" name="n" placeholder="e.g. 10" required>
20      </label>
21      <button>Find Prime</button>
22    </form>
23
24    <% if (locals.n && locals.prime) { %>
25      <p>
26        The largest prime number less than or equal to <%= n %> is <strong><%= prime %></strong>.
27      </p>
28      <form action="/like" method="get">
29        <input type="hidden" name="n" value="<%= n %>">
30        <input type="submit" value="Like"> <%= likes %>
31      </form>
32    <% } %>
33  </body>
34</html>

保存檔案

重新启动服务器,然后提交一个数字. 一个 喜欢 按钮将出现在0等数的首数结果后。

A screencapture of the page with a box around the newly added Like button

点击 Like 按钮将发送一个GET请求到/like,当前值为N作为查询参数,通过隐藏的输入。

您现在将添加路线来处理请求。

回到「server.js」中,添加路由:

 1[label server.js]
 2...
 3
 4app.get('/', (req, res) => {
 5  ...
 6});
 7
 8app.get('/like', (req, res) => {
 9  const n = req.query.n;
10
11  if (!n) {
12    res.redirect('/');
13    return;
14  }
15
16  likesMap[n]++;
17
18  res.redirect(`/?n=${n}`);
19});
20
21...

這個新路線會檢查「n」是否存在,否則會重定向到家,否則會增加這個數字的喜歡數量,最後則會重定向到點擊「喜歡」按鈕的視圖。

您的完成的文件现在应该匹配如下:

 1[label server.js]
 2const express = require('express');
 3const findPrime = require('./utils/findPrime');
 4
 5const app = express();
 6
 7app.set('view engine', 'ejs');
 8
 9/**
10 * Key is `n`
11 * Value is the number of 'likes' for `n`
12 */
13const likesMap = {};
14
15app.get('/', (req, res) => {
16  const n = req.query.n;
17
18  if (!n) {
19    res.render('index');
20    return;
21  }
22
23  const prime = findPrime(n);
24
25  // Initialize likes for this number when necessary
26  if (!likesMap[n]) likesMap[n] = 0;
27
28  const locals = { n, prime, likes: likesMap[n] };
29  res.render('index', locals);
30});
31
32app.get('/like', (req, res) => {
33  const n = req.query.n;
34
35  if (!n) {
36    res.redirect('/');
37    return;
38  }
39
40  likesMap[n]++;
41
42  res.redirect(`/?n=${n}`);
43});
44
45const port = process.env.PORT || 3000;
46app.listen(port, () =>
47  console.log(`Example app is listening on port ${port}.`)
48);

保存檔案

重新启动应用程序并再次测试 喜欢 按钮. 喜欢的计数将增加每个点击。

<$>[注] 注: 您也可以使用的 POST 方法,而不是 GET 这个路径. 它会更 RESTful 因为一个更新是对一个资源。 这个教程使用 GET 而不是引入表单 POST 请求的身体处理,所以你可以工作与现在熟悉的请求查询参数。

您的应用程序现在已经完成了完全功能的功能,因此您可以准备将其部署到 App Platform. 在下一步中,您将将应用程序的代码与git联系起来,并将该代码推到GitHub。

第3步:创建你的代码存储库

在此步骤中,您将创建一个代码存储库,以便为您的部署存储所有文件。 首先,您将您的代码委托给 git,然后将其推到GitHub存储库。

承诺你的代码去Git

在本节中,你将把你的代码委托给 git,所以它已经准备好推到GitHub。

注意: 如果您未使用您的用户名配置设置,请确保 设置 Git,并使用 SSH 验证您的 GitHub 帐户。

首先,启动一个git存储库:

1git init

接下来,请告诉 Git 排除应用的依赖性,创建一个名为 `.gitignore’的新文件,并添加以下内容:

1[label .gitignore]
2node_modules
3
4# macOS file
5.DS_Store

注意: **.DS_Store 行是 macOS 特定的,不需要在其他操作系统中存在。

保存并关闭文件。

现在,添加所有文件到 git:

1git add .

最后,使用以下命令进行这些更改:

1git commit -m "Initial commit"

使用-m选项来指定 commit 消息,您可以用您想要的任何消息来更新。

執行您的代碼後,您將收到這樣的輸出:

1[secondary_label Output]
2[main (root-commit) deab84e] Initial commit
3 6 files changed, 1259 insertions(+)
4 create mode 100644 .gitignore
5 create mode 100644 package-lock.json
6 create mode 100644 package.json
7 create mode 100644 server.js
8 create mode 100644 utils/findPrime.js
9 create mode 100644 views/index.ejs

你已经将你的代码发送给 git. 接下来,你会把它推到 GitHub。

将您的代码推到GitHub存储库

现在,你的应用程序的代码已被注入 git,你已经准备好将其推到GitHub,然后可以将代码连接到DigitalOcean App Platform并部署。

首先,在您的浏览器中,登录到 GitHub 并 创建一个新的存储库称为 express-memcache. 创建一个没有 README.gitignore 或许可文件的空白存储库. 您可以使存储库私人或公开。 您也可以查看 GitHub 关于创建一个新的存储库的文档

回到您的终端,将新创建的存储库添加为远程源,更新您的用户名:

1git remote add origin https://github.com/your_username/express-memcache.git

这个命令告诉Git在哪里按你的代码。

接下来,重命名默认分支main:

1git branch -M main

最后,将代码推到您的存储库:

1git push -u origin main

请在请求时输入您的身份证。

您将收到类似于以下的输出:

 1[secondary_label Output]
 2Enumerating objects: 10, done.
 3Counting objects: 100% (10/10), done.
 4Delta compression using up to 8 threads
 5Compressing objects: 100% (7/7), done.
 6Writing objects: 100% (10/10), 9.50 KiB | 9.50 MiB/s, done.
 7Total 10 (delta 0), reused 0 (delta 0), pack-reused 0
 8To https://github.com/your_username/express-memcache.git
 9 * [new branch]      main -> main
10Branch 'main' set up to track remote branch 'main' from 'origin'.

您的应用程序的代码现在在GitHub上,已准备好由App Platform部署。

步骤4:在App平台上部署

在此步骤中,您将部署您的 Express 应用到 DigitalOcean App Platform

您将首先更新环境设置,以便您的配置可以从PORT环境标签中读取。

更新您的应用程序的环境设置

在本节中,您将扩展您的 Express 服务器,以允许应用程序的端口配置从环境变量中读取。

然后,在文件的底部,更新突出的代码,以取代现有的app.listen行,并添加一个新的const port行:

1[label server.js]
2...
3
4const port = process.env.PORT || 3000;
5app.listen(port, () =>
6  console.log(`Example app is listening on port ${port}.`)
7);

此代码表示要使用PORT环境变量,如果它存在,或者默认为端口3000

您的应用程序现在将能够从您现在部署的 App Platform 环境中读取端口。

在App平台上创建和部署您的应用程序

您现在可以使用 App Platform 设置您的应用程序。

<$>[info] 在 App Platform 上运行此应用程序时,您将被收取费用,而网页服务则被收取次数(最少一分钟)。 价格显示在 Review 屏幕上。 查看 App Platform Pricing以获取详细信息。

首先,您可以登录您的 DigitalOcean 帐户。 从 应用程序仪表板,点击 创建 ,然后点击** 应用程序** .您还可以遵循我们的 产品文档如何在应用程序平台中创建应用程序

Create Resource From Source Code 屏幕上,选择** GitHub** 作为** 服务提供商** 。然后,给DigitalOcean访问您的存储库的权限。最佳做法是只选择您想要部署的存储库。

资源 屏幕上,点击 ** 编辑计划** 选择你的计划大小.本教程将使用** 基本计划** 与最小的尺寸** 网络服务** (** 512 MB RAM, 1 vCPU** ) 对** 快递缓存** 资源. 基本计划和最小的 Web 服务提供足够的资源这个样本 Express 应用程序. 一旦你设置了你的计划,点击** 返回** 。

接下来,点击导航栏的左侧的 Info 选项卡,并记住您的应用程序所在的区域。当您为 MemCachier添加DigitalOcean Marketplace Add-On时,您将需要此功能。

最后,点击 查看 选项卡,然后点击** 创建资源 ** 按钮来构建和部署您的应用程序。

到目前为止,您已经创建了一个Express应用程序,该应用程序会找到一个首选号码,并且有一个喜欢按钮,您将应用程序的代码委托给Git,并将其推到GitHub,然后在App平台上部署应用程序。

为了使您的 Express 应用程序更快、更可扩展,您将实施三个对象缓存策略,您需要一个缓存,您将在下一步创建一个缓存。

步骤 5 — 使用 MemCachier 配置对象缓存

在此步骤中,您将创建并配置一个对象缓存。 任何 memcached-兼容的缓存将为本教程工作。 您将提供一个 MemCachier Add-On从DigitalOcean市场。 一个 MemCachier缓存是内存中的关键值存储。

首先,您要加入数字海洋市场推出的MemCachier Add-On. 访问MemCachier Add-On page并点击Add MemCachier . 在下一张屏幕上, 请选择您 App Platface app 所在的区域, 您先前已经注意到了 。 您的应用程序和缓存应该在同一区域, 以便延迟度尽可能低。 如果您需要再次找到区域, 您可以查看您的 App 平台 应用程序的设置 。 你可以选择一个计划。 然后,单击** Add MemCachier** 来提供您的缓存.

<$>[info] 若要查找区域名称为 slug 地图,请参阅 DigitalOcean 的 Available Datacenters

接下来,您将配置您的 Express 应用程序以使用缓存。 访问 Add-Ons 仪表板,然后点击您的 MemCachier 添加器的名称,以打开仪表板。 在 MemCachier 添加器仪表板上,点击 Show 按钮以显示** Configuration Variables** 以加载包含 MEMCACHIER_USERNAMEMEMCACHIER_PASSWORDMEMCACHIER_SERVERS 的值的显示屏。 请注意这些值,因为您将需要它们。

Screencapture of redacted values for configuration variables in the Add-Ons dashboard

您现在将把您的 MemCachier 配置变量保存为您的应用程序的环境变量 。 回到你的App平台应用程序的仪表板上,点击Settings . 然后,在** Contents** 下,点击** express-memc...** 。 滚动到** 环境变量** 部分,点击** Edit ** ,然后用三键( " MEMCACHIER-USERNAME " 、 " MEMCACHIER-PASSWORD " 和 " MEMCACHIER-SERVERS " )和从MemCachier仪表板获得的相应值来添加你的memcacalier配置变量。 对于MEMCACHIER_PASWORD,请检查** Encrypt** ,因为其值是密码。 点击** 保存** 以更新应用程序.

Screencapture of App Platform configuration window for environment variables

现在,您将在 Express 应用程序中配置 memcache 客户端,使用您刚刚保存的环境变量,以便该应用程序可以与您的缓存通信。

在您的终端,安装 memjs库:

1npm install memjs

接下来,创建一个服务目录,然后创建服务/memcache.js文件,并在编辑器中打开它。在文件的顶部,导入memjs,并配置一个缓存客户端:

1[label services/memcache.js]
2const { Client } = require('memjs');
3
4module.exports = Client.create(process.env.MEMCACHIER_SERVERS, {
5  failover: true,
6  timeout: 1,
7  keepAlive: true,
8});

保存檔案

此代码创建了 MemCachier 缓存客户端 。 至于选择,失败被设定为真实使用MemCachier的高可用性集群。 如果服务器失败, 存储在该服务器上的所有密钥的命令将自动被发送到下一个可用的服务器 。 1'秒的 " 超时 " 对已部署的应用程序来说比 " 0.5 " 秒的缺省要好。 KeepAlive:real'即使闲置时也保持与您缓存的连接开通,这是可取的,因为连接速度很慢,缓存必须快才能有效.

在此步骤中,您使用DigitalOcean Marketplace的MemCachier插件提供缓存,然后将缓存的配置设置添加为App Platform环境变量,允许您使用memjs配置客户端,以便您的Express应用可以与缓存进行通信。

一切都已准备好开始在Express中实现缓存,您将接下来做。

步骤 6 — 使用 MemCachier 实现 Express 缓存

随着 Express 应用程序的部署和 MemCachier 插件的提供,您现在可以使用您的对象缓存。在此步骤中,您将实施三种对象缓存技术。您将开始通过缓存资源密集的计算来提高使用速度和效率。

缓存高资源计算

在本节中,您将缓存资源密集的计算以加速您的应用程序,从而更有效地使用CPU。findPrime函数是一种资源密集的计算,当提交了足够大的数字时,您将缓存其结果并在可用时提供缓存值,而不是重复计算。

首先,打开「server.js」以添加 memcache 客户端:

1[label server.js]
2const express = require('express');
3const findPrime = require('./utils/findPrime');
4const memcache = require('./services/memcache');
5
6...

然后在缓存中存储计算的首数:

 1[label server.js]
 2...
 3
 4  const prime = findPrime(n);
 5
 6  const key = 'prime_' + n;
 7
 8  memcache.set(key, prime.toString(), { expires: 0 }, (err) => {
 9    if (err) console.log(err);
10  });
11
12...

保存檔案

设置方法将一个密钥作为第一个参数和一个字符串的值作为第二个参数,所以你将初始数转换为一个字符串。第三个选项参数确保存储的项目永远不会过期。

<$>[注] 注: 作为最佳做法,缓存错误应以优雅的方式处理。缓存是一种改进,通常不应该在失败时破坏应用程序。

<$>[注] 注: 此时,您的应用程序将继续在本地工作,但没有缓存。当呼叫memcache.set时,会出现输出错误,因为它无法找到缓存服务器:

1[secondary_label Output]
2MemJS: Server <localhost:11211> failed after (2) retries with error - connect ECONNREFUSED 127.0.0.1:11211
3Error: No servers available
4...

对于本教程的剩余部分,你不需要本地缓存. 如果你想要它工作,你可以运行 memcached 在localhost:11211,这是memjs的默认值。

接下来,阶段并做出你的变化:

1git add . && git commit -m "Add memjs client and cache prime number"

然后,将这些更改推到 GitHub,该更改应自动部署到 App Platform:

1git push

您的 App Platform 仪表板将从部署消息转换为指示您的应用正在构建的消息,当构建完成时,请在浏览器中打开应用程序并提交一个号码以找到其最大的首选项。

注意: **您的仪表板可能会显示一个等待服务消息. 该消息通常会自行解决。

接下来,回到 Add-Ons 仪表板,然后点击您的命名服务的 View MemCachier 选项,以查看缓存的分析仪表板。

The MemCachier analytics dashboard

在此仪表板上, All Time Stats 板上的 Set Cmds** 选项和** Storage** 板上的** Items** 统计都增加了 1. 每次提交数字,** Set Cmds** 和** Items** 都会增加。

<$>[注] 注: 在 App Platform 上检查应用程序的日志可能有助于调试。

有了存储在缓存中的项目,你可以利用它们. 现在你会检查一个项目是否已缓存,如果是这样,你会从缓存中服务它;否则,你会像以前一样找到第一号。

回到「server.js」中,用突出的行更新您的文件,您将同时修改现有行并为缓存添加新行:

 1[label server.js]
 2...
 3
 4app.get('/', (req, res) => {
 5  const n = req.query.n;
 6
 7  if (!n) {
 8    res.render('index');
 9    return;
10  }
11
12  let prime;
13
14  const key = 'prime_' + n;
15
16  memcache.get(key, (err, val) => {
17    if (err) console.log(err);
18
19    if (val !== null) {
20      // Use the value from the cache
21      // Convert Buffer string before converting to number
22      prime = parseInt(val.toString());
23    } else {
24      // No cached value available, find it
25      prime = findPrime(n);
26
27      memcache.set(key, prime.toString(), { expires: 0 }, (err) => {
28        if (err) console.log(err);
29      });
30    }
31
32    // Initialize likes for this number when necessary
33    if (!likesMap[n]) likesMap[n] = 0;
34
35    const locals = { n, prime, likes: likesMap[n] };
36    res.render('index', locals);
37  });
38});
39
40...

保存檔案

此代码在没有值的情况下初始化prime,使用let关键字,因为它的值现在被重新分配了,然后memcache.get试图检索缓存的主要号码。控制器的大部分代码现在生活在memcache.get呼叫中,因为其结果需要确定如何处理请求。

在),因此您将其转换为字符串,然后将prime转换为数字。

承诺您的更改,并将其推送到GitHub部署:

1git add . && git commit -m "Check cache for prime number" && git push

当您向应用程序提交尚未缓存的号码时,您的 MemCachier 仪表板上的 Set Cmds 、** Items** 和** get misses** 统计数据将增加至 1. 错误发生,因为您试图在设置之前从缓存中获取该项目。

您现在正在缓存资源密集的计算,接下来,您将缓存应用程序的渲染视图。

Caching 返回的观点

在本节中,您将使用中间软件缓存 Express 应用程序所渲染的视图。以前,您将ejs设置为模板引擎,并创建了一个模板,以便为每一个提交的数字N渲染视图。

首先,创建一个middleware目录,然后创建middleware/cacheView.js文件,并在编辑器中打开它。

 1[label middleware/cacheView.js]
 2const memcache = require('../services/memcache');
 3
 4/**
 5 * Express middleware to cache views and serve cached views
 6 */
 7module.exports = function (req, res, next) {
 8  const key = `view_${req.url}`;
 9
10  memcache.get(key, (err, val) => {
11    if (err) console.log(err);
12
13    if (val !== null) {
14      // Convert Buffer string to send as the response body
15      res.send(val.toString());
16      return;
17    }
18  });
19};

您首先导入memcache客户端,然后声明一个密钥,例如view_/?n=100。接下来,您检查该密钥的视图是否在memcache.get的缓存中。

接下来,如果一个视图没有缓存,你想缓存它. 要做到这一点,通过添加突出的行来覆盖 res.send 方法:

 1[label middleware/cacheView.js]
 2...
 3
 4module.exports = function (req, res, next) {
 5  const key = `view_${req.url}`;
 6
 7  memcache.get(key, (err, val) => {
 8    if (err) console.log(err);
 9
10    if (val !== null) {
11      // Convert Buffer to UTF-8 string to send as the response body
12      res.send(val.toString());
13      return;
14    }
15
16    const originalSend = res.send;
17    res.send = function (body) {
18      memcache.set(key, body, { expires: 0 }, (err) => {
19        if (err) console.log(err);
20      });
21
22      originalSend.call(this, body);
23    };
24  });
25};

您将) ),因此将指定正确的`这一'值.

然后,通过添加突出的行来传输控制到下一个中间件:

 1[label middleware/cacheView.js]
 2...
 3
 4/**
 5 * Express middleware to cache views and serve cached views
 6 */
 7module.exports = function (req, res, next) {
 8  const key = `view_${req.url}`;
 9
10  memcache.get(key, (err, val) => {
11    if (err) console.log(err);
12
13    if (val !== null) {
14      // Convert Buffer to UTF-8 string to send as the response body
15      res.send(val.toString());
16      return;
17    }
18
19    const originalSend = res.send;
20    res.send = function (body) {
21      memcache.set(key, body, { expires: 0 }, (err) => {
22        if (err) console.log(err);
23      });
24
25      originalSend.call(this, body);
26    };
27
28    next();
29  });
30};
31
32...

在您的情况下,没有其他中间件,所以调用控制器。Express 的res.render方法渲染视图,然后呼叫res.send内部与该渲染视图.所以现在,在主路线的控制器中,当调用res.render时,您的过度函数被调用,将视图存储在缓存中,然后最终调用原始发送函数完成响应。

保存檔案

<$>[注] 注: 您也可以在控制器中向 render 方法传递回调,但您将不得不对每个被缓存的路径重复控制器中的视图缓存代码。

现在将视图缓存中间件导入到server.js:

1[label server.js]
2const express = require('express');
3const findPrime = require('./utils/findPrime');
4const memcache = require('./services/memcache');
5const cacheView = require('./middleware/cacheView');
6
7...

添加突出的代码以与GET /主路线一起使用:

1[label server.js]
2...
3
4app.get('/', cacheView, (req, res) => {
5  ...
6});
7
8...

保存檔案

然后进行更改,并将其推到GitHub部署:

1git add . && git commit -m "Add view caching" && git push

如果您在您的应用程序中提交一个新号码,MemCachier 仪表板统计数据为 Set Cmds 、** Items** 和** get misses** 都增加了两倍:一次为首数计算和一次为视图。

<$>[注] 注: Express 应用程序设置为视图缓存在生产中是默认启用的。此视图缓存不会缓存模板的输出内容,只有底层模板本身。

现在您正在缓存视图时,您可能会注意到 喜欢 按钮不再工作. 如果您要登录喜欢值,该值确实会发生变化。

接下来,当喜欢更改时,您将通过从缓存中删除缓存视图,并更新重定向函数,并添加突出的行:

 1[label server.js]
 2...
 3
 4app.get('/like', (req, res) => {
 5  const n = req.query.n;
 6
 7  if (!n) {
 8    res.redirect('/');
 9    return;
10  }
11
12  likesMap[n]++;
13
14  // The URL of the page being 'liked'
15  const url = `/?n=${n}`;
16
17  res.redirect(url);
18});
19
20...

此视图的喜欢数已经更改,因此缓存版本将无效。 添加突出的行以在喜欢更改时从缓存中删除喜欢数:

 1[label server.js]
 2...
 3  const url = `/?n=${n}`;
 4
 5  // The view for this URL has changed, so the cached version is no longer valid, delete it from the cache.
 6  const key = `view_${url}`;
 7  memcache.delete(key, (err) => {
 8    if (err) console.log(err);
 9  });
10
11  res.redirect(url);
12...

您的「server.js」文件现在应该与以下相匹配:

 1[label server.js]
 2const express = require('express');
 3const findPrime = require('./utils/findPrime');
 4const memcache = require('./services/memcache');
 5const cacheView = require('./middleware/cacheView');
 6
 7const app = express();
 8
 9app.set('view engine', 'ejs');
10
11/**
12 * Key is `n`
13 * Value is the number of 'likes' for `n`
14 */
15const likesMap = {};
16
17app.get('/', cacheView, (req, res) => {
18  const n = req.query.n;
19
20  if (!n) {
21    res.render('index');
22    return;
23  }
24
25  let prime;
26
27  const key = 'prime_' + n;
28
29  memcache.get(key, (err, val) => {
30    if (err) console.log(err);
31
32    if (val !== null) {
33      // Use the value from the cache
34      // Convert Buffer string before converting to number
35      prime = parseInt(val.toString());
36    } else {
37      // No cached value available, find it
38      prime = findPrime(n);
39
40      memcache.set(key, prime.toString(), { expires: 0 }, (err) => {
41        if (err) console.log(err);
42      });
43    }
44
45    // Initialize likes for this number when necessary
46    if (!likesMap[n]) likesMap[n] = 0;
47
48    const locals = { n, prime, likes: likesMap[n] };
49    res.render('index', locals);
50  });
51});
52
53app.get('/like', (req, res) => {
54  const n = req.query.n;
55
56  if (!n) {
57    res.redirect('/');
58    return;
59  }
60
61  likesMap[n]++;
62
63  // The URL of the page being 'liked'
64  const url = `/?n=${n}`;
65
66  // The view for this URL has changed, so the cached version is no longer valid, delete it from the cache.
67  const key = `view_${url}`;
68  memcache.delete(key, (err) => {
69    if (err) console.log(err);
70  });
71
72  res.redirect(url);
73});
74
75const port = process.env.PORT || 3000;
76app.listen(port, () =>
77  console.log(`Example app is listening on port ${port}.`)
78);

保存檔案

承诺并推动更改部署:

1git add . && git commit -m "Delete invalid cached view" && git push

您的应用程序上的 喜欢 按钮现在会起作用. 当一个视图被喜欢时,您的 MemCachier 仪表板上的下列统计数据将发生变化:

  • 在删除视图时增加 ** 删除响应 增加 * ** 因为视图被删除并且不在缓存中增加 ** 增加响应** 因为在缓存中发现了主要号码 ** 设置 Cmds* 增加因为更新视图被添加到缓存中** 项目* 在删除和重新添加视图时保持相同。

您已经实施了渲染视图缓存和在变更时无效缓存视图,您将执行的最终策略是会话缓存。

缓存会话

在本节中,您将在 Express 应用程序中添加和缓存会话,使您的缓存会话存储器。 会话的常见用例是用户登录,因此您可以将此关于缓存会话的部分视为在未来实施用户登录系统的初步步步骤(尽管用户登录系统超出本教程的范围)。

<$>[注] 注: 缓存非常适合存储短期会话,但缓存不持久;长期会话更适合数据库等永久存储解决方案。

安装 express-session工具,将会话添加到您的Express应用程序和 connect-memjs工具,以允许使用您的MemCachier缓存作为会话存储:

1npm install express-session connect-memjs

server.js中,导入express-sessionconnect-memjs:

1[label server.js]
2const express = require('express');
3const findPrime = require('./utils/findPrime');
4const memcache = require('./services/memcache');
5const cacheView = require('./middleware/cacheView');
6const session = require('express-session');
7const MemcacheStore = require('connect-memjs')(session);
8
9...

保存檔案

会话中间件传递到连接 memcached 模块,允许它从express.session.Store继承。

server.js中,配置会话中间件以使用您的缓存作为其存储。

 1[label server.js]
 2...
 3
 4app.set('view engine', 'ejs');
 5
 6app.use(
 7  session({
 8    secret: 'your-session-secret',
 9    resave: false,
10    saveUninitialized: true,
11    store: new MemcacheStore({
12      servers: [process.env.MEMCACHIER_SERVERS],
13      prefix: 'session_',
14    }),
15  })
16);
17
18...

使用秘密来签署会话cookie. 请确保更新您的会话秘密以一个独特的字符串。

<$>[注] 注: 您应该使用环境变量来设置生产设置的秘密. 要做到这一点,您可以将秘密设置为 `secret: process.env.SESSION_SECRETannoo 'your-session-secret',但您还需要在您的应用程序平台仪表板中设置环境变量。

重置迫使会话重置,如果在请求期间未修改,您不想不必要地再次将项目存储在缓存中,所以您将其设置为

「saveUninitialized: false」是当您只想保存修改的会话时有用的,就像经常发生在登录会话中,在身份验证后可能会向会话添加用户属性时一样。

最后,将存储设置为您的缓存,将会话缓存密钥的前缀设置为session_,这意味着缓存中的会话项目的密钥将看起来像session_<session ID>

接下来,添加一些应用级调试中间件与突出的行,这将有助于识别行动中的缓存会话:

 1[label server.js]
 2...
 3
 4app.use(
 5  session({
 6    ...
 7  })
 8);
 9
10/**
11 * Session sanity check middleware
12 */
13app.use(function (req, res, next) {
14  console.log('Session ID:', req.session.id);
15
16  // Get the item from the cache
17  memcache.get(`session_${req.session.id}`, (err, val) => {
18    if (err) console.log(err);
19
20    if (val !== null) {
21      console.log('Session from cache:', val.toString());
22    }
23  });
24
25  next();
26});
27
28...

该中间软件将登录每个请求的会话ID,然后从缓存中获取该ID的会话,并记录其内容。

保存文件,然后承诺并将更改推向部署。

1git add . && git commit -m "Add session caching" && git push

在您的应用程序中,提交一个号码,然后在您的 App Platform 仪表板中检查 Runtime Logs 以访问您的调试消息. 您将找到您登录的会话 ID 和值,证明会话正在工作并被缓存。

在您的 MemCachier 仪表板上,一旦视图和会话被缓存,您将看到3 get hits for each page refresh:1 for the view,1 for the session,和1 for getting the session in the debugging middleware. 在您的 MemCachier 仪表板上,您将看到3** get hits** for each page refresh: 1 for the view, 1 for the session, and 1 for getting the session in the debugging middleware.

您现在已经实现了会话缓存,您可以在这里停下来,或者您可以在可选的最后一步中清理您的应用程序。

(可选)步骤7 - 清理您的资源

您在本教程中部署的应用程序将被收取费用,因此在您完成工作后可以选择破坏应用程序和MemCachier Add-On。

从应用程序的仪表板中,单击 行动 ,然后单击** 破坏应用程序** 。

要清理您的 MemCachier 附加功能,请单击 添加功能 ,然后单击您的 MemCachier 附加功能的名称。接下来,单击** 设置** 和** 破坏** . 30 天不活动后,免费的 MemCachier 缓存将被禁用,但清理您的工具是一个很好的做法。

结论

在此教程中, 您创建了 Express 应用程序, 以找到一个带有 Like 按钮的质数 。 然后把那个应用推到了GitHub, 并将其部署在 DigitalOcean App平台上. 最后,你让Express的应用更快,更可扩展,通过使用MemCachier Add-On来进行缓存资源密集型计算,提供观点和会话来实施三种对象缓存技术. 您可以[在 DigitalOcean 社区寄存器中审查此教程的全部文件] (https://github.com/do-community/express-memcache) .

在你执行的每个缓存策略中,钥匙有一个前缀:prime'、view'和`session'。 除了命名空间的优势外,前缀还提供了允许您配置缓存性能的额外好处. 您在此教程中使用了 MemCachier 开发者计划, 但是您也可以 [尝试一个完全管理的计划] (https://marketplace.digitalocean.com/add-ons/memcachier# plans) , 这个计划是随Introvertion 功能集而来, 可以跟踪单个前缀的性能 。 例如,你可以监视任何前缀的命中率或命中比,提供对缓存性能的详细见解. 为了继续与MemCachier合作,你可以审查他们的文档.

要继续使用 DigitalOcean App Platform,请尝试我们的 应用程序平台如何使用指南并进一步阅读我们的 应用程序平台文档

Published At
Categories with 技术
comments powered by Disqus