Поиск по сайту:

Как развернуть экспресс-приложение и масштабировать его с помощью MemCachier на платформе приложений


Автор выбрал программу Write for DOnations.

Введение

система кэширования объектов memcached, но имеет несколько преимуществ, таких как лучшие сценарии отказа с кластерами высокой доступности.

Сначала вы создадите приложение Express, которое вычисляет простое число, имеет кнопку «Мне нравится» и использует механизм шаблонов. Эти функции позволят вам позже реализовать несколько стратегий кэширования. Затем вы отправите код своего приложения на GitHub и развернете его на платформе приложений. Наконец, вы реализуете три метода кэширования объектов, чтобы сделать ваше приложение более быстрым и масштабируемым. К концу этого руководства вы сможете развернуть приложение Express на App Platform, внедрив методы кэширования ресурсоемких вычислений, визуализированных представлений и сеансов.

Предпосылки

  • На вашем компьютере установлен Node.js, который можно настроить с помощью инструкции по установке Node.js и созданию локальной среды разработки.
  • Базовый сервер Express с Node.js. Выполните шаги 1–2 из нашего руководства Как начать работу с Node.js и Express.
  • Как установить Git для настройки.
  • Подробнее о ценах на платформу приложений.
  • Веб-браузер, например Chrome.
  • Понимание шаблонизаторов Express.
  • Понимание промежуточного ПО Express. Подробнее об этой теме можно прочитать в нашем руководстве «Как создать пользовательское промежуточное ПО в Express.js».

Шаг 1 — Настройка визуализированного представления экспресс-шаблона

На этом шаге вы установите механизм шаблонов для Express, создадите шаблон для домашнего маршрута вашего приложения (GET /) и обновите маршрут для использования этого шаблона. Механизм шаблонов позволяет кэшировать визуализированные представления позже, увеличивая скорость обработки запросов и уменьшая использование ресурсов.

Для начала перейдите в каталог проекта на сервере Express с помощью вашего редактора, если он еще не открыт. Вы можете вернуться к обязательному руководству по началу работы с Node.js и Express, чтобы определить, где вы сохранили файлы проекта.

Вы установите механизм шаблонов для Express, чтобы использовать статические файлы шаблонов в своем приложении. Механизм шаблонов заменяет переменные в файле шаблона значениями и преобразует шаблон в файл HTML, который отправляется в качестве ответа на запрос. Использование шаблонов упрощает работу с HTML.

Установите механизмы шаблонов, которые поддерживает Express, например Mustache, Pug или Nunjucks.

  1. npm install ejs

После установки ejs вы настроите свое приложение Express для его использования.

Откройте файл server.js в своем редакторе. Затем добавьте выделенную строку:

const express = require('express');

const app = express();

app.set('view engine', 'ejs');

app.get('/', (req, res) => {
  res.send('Successful response.');
});

...

Эта строка задает для свойства настройки приложения view engine значение ejs.

Сохраните файл.

Примечание. В этом руководстве вы будете использовать параметр view engine, но другим полезным параметром является views. Параметр views сообщает приложению Express, где найти файлы шаблонов. Значение по умолчанию — ./views.

Затем создайте каталог views. Затем создайте файл views/index.ejs и откройте его в своем редакторе.

Добавьте в этот файл разметку начального шаблона:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Find the largest prime number</title>
  </head>
  <body>
    <h1>Find the largest prime number</h1>

    <p>
      For any number N, find the largest prime number less than or equal to N.
    </p>
  </body>
</html>

Сохраните файл.

Создав шаблон, вы обновите свой маршрут, чтобы использовать его.

Откройте файл server.js и обновите выделенный код:

...

app.get('/', (req, res) => {
  res.render('index');
});

...

Метод ответа render принимает имя шаблона в качестве первого параметра. В этом случае index соответствует файлу views/index.ejs.

Перезапустите приложение, чтобы изменения вступили в силу. Остановите сервер, если он запущен, нажав CTRL+C в терминале. Затем снова запустите сервер:

  1. node server.js

Посетите localhost:3000 в своем веб-браузере, который теперь будет отображать содержимое вашего шаблона.

Ваше приложение теперь имеет представление, отображаемое на основе шаблона, но оно еще ничего не делает. Далее вы добавите функциональность для поиска простого числа.

Шаг 2 — Добавление функциональности в ваше экспресс-приложение

На этом шаге вы добавите функции для поиска простого числа и лайкания чисел с помощью кнопки «Нравится». Вы будете использовать эти функции для взаимодействия с приложением после его развертывания на платформе приложений на шаге 4.

Нахождение простого числа

В этом разделе вы добавите в свое приложение функцию, которая находит наибольшее простое число, меньшее или равное N, где N относится к любому числу.

N будет отправлен через форму с методом GET на домашний маршрут (/) с добавлением N в качестве параметра запроса: localhost:3000/?n=10 (где 10 — пример запроса). Домашний маршрут может иметь несколько URL-адресов, создающих визуализированные представления, каждое из которых может кэшироваться отдельно.

В views/index.ejs добавьте форму с элементом ввода для ввода N:

...

<p>
  For any number N, find the largest prime number less than or equal to N.
</p>

<form action="/" method="get">
  <label>
    N
    <input type="number" name="n" placeholder="e.g. 10" required>
  </label>
  <button>Find Prime</button>
</form>

...

Действие формы отправляется в /, который обрабатывается домашним маршрутом app.get(/ ...) в server.js. Поскольку метод формы установлен на get, данные n будут добавлены к URL-адресу действия в качестве параметра запроса.

Сохраните файл.

Затем, когда будет сделан запрос с параметром запроса n, вы передадите эти данные в шаблон.

В server.js добавьте выделенный код:

...

app.get('/', (req, res) => {
  const n = req.query.n;
  
  if (!n) {
    res.render('index');
    return;
  }
  
  const locals = { n };
  res.render('index', locals);
});

...

Эти строки будут проверять, есть ли в запросе параметр запроса с именем n. Если это так, вы визуализируете представление index и передаете ему значение n. В противном случае вы создаете представление index без данных.

Примечание. Не всегда можно доверять пользовательскому вводу, поэтому для готового к работе приложения рекомендуется проверять ввод с помощью такой библиотеки, как Joi.

Метод render имеет второй необязательный параметр, locals. Этот параметр определяет локальные переменные, передаваемые в шаблон для отображения представления. Сокращенное имя свойства определяет свойство n объекта locals. Когда переменная имеет то же имя, что и свойство объекта, которому она назначается, имя переменной можно не указывать. Таким образом, { n: n } можно записать как { n }.

Сохраните файл.

Теперь, когда в шаблоне есть некоторые данные, вы можете отобразить их.

В views/index.ejs добавьте выделенные строки для отображения значения N:

...

<form action="/" method="get">
  <label>
    N
    <input type="number" name="n" placeholder="e.g. 10" required>
  </label>
  <button>Find Prime</button>
</form>

<% if (locals.n) { %>
  <p>N: <%= n %></p>
<% } %>

...

Если для этого представления существует локальная переменная n, вы указываете приложению отображать ее.

Сохраните файл, затем перезапустите сервер, чтобы обновить приложение. Теперь форма будет загружаться с кнопкой «Найти Прайм». Приложение сможет принимать пользовательский ввод и отображать его под формой.

Введите любое число в форму. После отправки формы URL-адрес изменится и будет включать параметр запроса n, например http://localhost:3000/?n=40 если вы введете 40. Введенное вами значение также будет загружено в форме как N: 40.

Теперь, когда можно отправить и отобразить значение для N, вы добавите функцию для поиска наибольшего простого числа, меньшего или равного N. Затем вы отобразите этот результат в своем представлении.

Создайте каталог utils. Затем создайте файл utils/findPrime.js.

Откройте findPrime.js в своем редакторе и добавьте функцию поиска простых чисел:

/**
 * Find the largest prime number less than or equal to `n`
 * @param {number} n A positive integer greater than the smallest prime number, 2
 * @returns {number}
 */
module.exports = function (n) {
  let prime = 2; // initialize with the smallest prime number
  for (let i = n; i > 1; i--) {
    let isPrime = true;
    for (let j = 2; j < i; j++) {
      if (i % j == 0) {
        isPrime = false;
        break;
      }
    }
    if (isPrime) {
      prime = i;
      break;
    }
  }
  return prime;
};

Комментарий JSDoc документирует функцию. Алгоритм начинается с первого простого числа (2), затем перебирает числа, начиная с n и уменьшая число на 1 в каждом цикле. . Функция будет продолжать цикл и искать простое число, пока число не станет 2, наименьшим простым числом.

Каждый цикл предполагает, что текущее число является простым числом, а затем проверяет это предположение. Он проверит, имеет ли текущее число коэффициент, отличный от 1 и самого себя. Если текущее число можно разделить на любое число больше 1 и меньше самого себя без остатка, то оно не является простым числом. Затем функция попытается ввести следующий номер.

Сохраните файл.

Затем импортируйте функцию поиска простых чисел в server.js:

const express = require('express');
const findPrime = require('./utils/findPrime');

...

Обновите контроллер домашнего маршрута, чтобы найти простое число и передать его значение в шаблон. По-прежнему в server.js добавьте выделенный код:

...

app.get('/', (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.render('index');
    return;
  }
  
  const prime = findPrime(n);

  const locals = { n, prime };
  res.render('index', locals);
});

...

Сохраните файл.

Теперь вы добавите код для отображения результата в вашем шаблоне. В views/index.ejs отобразите значение N:

...

<form action="/" method="get">
  <label>
    N
    <input type="number" name="n" placeholder="e.g. 10" required>
  </label>
  <button>Find Prime</button>
</form>

<% if (locals.n && locals.prime) { %>
  <p>
    The largest prime number less than or equal to <%= n %> is <strong><%= prime %></strong>.
  </p>
<% } %>
...

Сохраните файл.

Теперь перезапустите сервер.

Чтобы протестировать функциональность, введите любое число. В качестве примера в этом руководстве будет использоваться 10. Если вы отправите число 10, вы получите ответ, в котором будет указано: Самое большое простое число, меньшее или равное 10, равно < отметка>7..

Теперь ваше приложение может принимать число, а затем находить и отображать простое число. Далее вы добавите кнопку «Нравится».

Добавление кнопки «Нравится»

В настоящее время ваше приложение может создавать разные представления в зависимости от каждого отправленного числа N. Помимо обновления текста, содержимое этих представлений, скорее всего, останется прежним. Кнопка «Нравится», которую вы добавите в этот раздел, позволит обновить содержимое представления. Эта кнопка демонстрирует необходимость аннулирования кэшированного представления при изменении его содержимого, что будет полезно при кэшировании визуализированных представлений позже в этом руководстве.

С кнопкой «Мне нравится» приложению нужно где-то хранить подобные данные. Хотя постоянное хранилище идеально, вы будете хранить лайки в памяти, потому что реализация базы данных выходит за рамки этого руководства. Таким образом, данные будут эфемерными, что означает, что все данные будут потеряны при остановке сервера.

Откройте server.js, чтобы добавить следующую переменную:

...

app.set('view engine', 'ejs');

/**
 * Key is `n`
 * Value is the number of 'likes' for `n`
 */
const likesMap = {};

...

Объект likesMap используется в качестве карты для хранения лайков для всех запрошенных номеров. Ключ — n, а его значения — количество лайков для n.

Лайки для номера должны быть инициализированы при отправке номера. По-прежнему в server.js добавьте выделенные строки, чтобы инициализировать лайки для N:

...

  const prime = findPrime(n);

  // Initialize likes for this number when necessary
  if (!likesMap[n]) likesMap[n] = 0;

  const locals = { n, prime };
  res.render('index', locals);

...

Этот оператор if проверяет, существуют ли лайки для текущего номера. Если лайков нет, то число likesMaps инициализируется равным 0.

Затем добавьте лайки в качестве локальной переменной для представления:

...

  const prime = findPrime(n);

  // Initialize likes for this number when necessary
  if (!likesMap[n]) likesMap[n] = 0;

  const locals = { n, prime, likes: likesMap[n] };
  res.render('index', locals);

...

Сохраните файл.

Теперь, когда в представлении есть данные о лайках, вы можете отобразить их значение и добавить кнопку «Нравится».

В views/index.ejs добавьте разметку кнопки «Нравится»:

...

<% if (locals.n && locals.prime) { %>
  <p>
    The largest prime number less than or equal to <%= n %> is <strong><%= prime %></strong>.
  </p>

  <form action="/like" method="get">
    <input type="hidden" name="n" value="<%= n %>">
    <input type="submit" value="Like"> <%= likes %>
  </form>
<% } %>
...

Ваш заполненный файл теперь должен соответствовать следующему:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Find the largest prime number</title>
  </head>
  <body>
    <h1>Find the largest prime number</h1>

    <p>
      For any number N, find the largest prime number less than or equal to N.
    </p>

    <form action="/" method="get">
      <label>
        N
        <input type="number" name="n" placeholder="e.g. 10" required>
      </label>
      <button>Find Prime</button>
    </form>

    <% if (locals.n && locals.prime) { %>
      <p>
        The largest prime number less than or equal to <%= n %> is <strong><%= prime %></strong>.
      </p>
      <form action="/like" method="get">
        <input type="hidden" name="n" value="<%= n %>">
        <input type="submit" value="Like"> <%= likes %>
      </form>
    <% } %>
  </body>
</html>

Сохраните файл.

Перезапустите сервер, затем отправьте номер. Кнопка «Нравится» появится после результата простого числа с числом лайков 0.

Нажатие кнопки «Нравится» отправляет запрос GET в /like с текущим значением N в качестве параметра запроса через скрытый ввод. На данный момент вы получите сообщение об ошибке 404 с Cannot GET /like, потому что у вашего приложения еще нет соответствующего маршрута.

Теперь вы добавите маршрут для обработки запроса.

Вернитесь в server.js и добавьте маршрут:

...

app.get('/', (req, res) => {
  ...
});

app.get('/like', (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.redirect('/');
    return;
  }

  likesMap[n]++;

  res.redirect(`/?n=${n}`);
});

...

Этот новый маршрут проверяет, существует ли n. Если нет, он перенаправляет домой. В противном случае количество лайков увеличивается. Наконец, он перенаправляет на представление, где была нажата кнопка «Нравится».

Ваш заполненный файл теперь должен соответствовать следующему:

const express = require('express');
const findPrime = require('./utils/findPrime');

const app = express();

app.set('view engine', 'ejs');

/**
 * Key is `n`
 * Value is the number of 'likes' for `n`
 */
const likesMap = {};

app.get('/', (req, res) => {
  const n = req.query.n;
  
  if (!n) {
    res.render('index');
    return;
  }
  
  const prime = findPrime(n);

  // Initialize likes for this number when necessary
  if (!likesMap[n]) likesMap[n] = 0;

  const locals = { n, prime, likes: likesMap[n] };
  res.render('index', locals);
});

app.get('/like', (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.redirect('/');
    return;
  }

  likesMap[n]++;

  res.redirect(`/?n=${n}`);
});

const port = process.env.PORT || 3000;
app.listen(port, () =>
  console.log(`Example app is listening on port ${port}.`)
);

Сохраните файл.

Перезапустите приложение и снова проверьте кнопку «Нравится». Количество лайков будет увеличиваться с каждым кликом.

Примечание. Вы также можете использовать метод POST вместо GET для этого маршрута. Это было бы более RESTful, потому что в ресурс вносится обновление. В этом учебном пособии используется GET, а не вводится обработка тела запроса формы POST, чтобы вы могли работать с уже знакомыми параметрами запроса запроса.

Теперь ваше приложение содержит полностью функционирующие функции, поэтому вы можете подготовиться к его развертыванию на платформе приложений. На следующем шаге вы зафиксируете код приложения с помощью git и отправите этот код на GitHub.

Шаг 3 — Создание репозитория кода

На этом шаге вы создадите репозиторий кода для хранения всех файлов для вашего развертывания. Сначала вы зафиксируете свой код в git, а затем отправите его в репозиторий GitHub. Вы будете использовать этот репозиторий для развертывания с помощью App Platform.

Отправка кода в Git

В этом разделе вы зафиксируете свой код в git, чтобы он был готов к отправке на GitHub.

Примечание. Если вы не настроили параметры со своим именем пользователя, обязательно настройте Git и аутентифицируйте свою учетную запись GitHub с помощью SSH.

Сначала инициализируйте репозиторий git:

  1. git init

Затем скажите Git исключить зависимости вашего приложения. Создайте новый файл с именем .gitignore и добавьте следующее:

node_modules

# macOS file
.DS_Store

Примечание. Строка .DS_Store относится к macOS и не обязательно должна присутствовать для других операционных систем.

Сохраните и закройте файл.

Теперь добавьте все файлы в git:

  1. git add .

Наконец, зафиксируйте эти изменения с помощью следующей команды:

  1. git commit -m "Initial commit"

Параметр -m используется для указания сообщения фиксации, которое вы можете обновить любым сообщением, которое пожелаете.

После фиксации кода вы получите следующий вывод:

Output
[main (root-commit) deab84e] Initial commit 6 files changed, 1259 insertions(+) create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 server.js create mode 100644 utils/findPrime.js create mode 100644 views/index.ejs

Вы зафиксировали свой код в git. Затем вы отправите его на GitHub.

Отправка кода в репозиторий GitHub

Теперь, когда код вашего приложения зафиксирован в git, вы готовы отправить его на GitHub. Затем вы можете подключить код к платформе приложений DigitalOcean и развернуть его.

Во-первых, в своем браузере войдите в GitHub и документацию GitHub по созданию нового репозитория.

Вернувшись в свой терминал, добавьте только что созданный репозиторий в качестве удаленного источника, обновив свое имя пользователя:

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

Эта команда сообщает Git, куда вставить ваш код.

Затем переименуйте ветку по умолчанию main:

  1. git branch -M main

Наконец, отправьте код в свой репозиторий:

  1. git push -u origin main

Введите свои учетные данные, если будет предложено.

Вы получите вывод, подобный следующему:

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

Код вашего приложения теперь находится на GitHub и готов к развертыванию App Platform.

Шаг 4 — Развертывание на платформе приложений

На этом этапе вы развернете свое экспресс-приложение на платформе приложений DigitalOcean. Вы создадите приложение App Platform, разрешите ему доступ к вашему репозиторию GitHub, а затем развернете его.

Вы начнете с обновления настроек среды, чтобы вашу конфигурацию можно было прочитать с метки среды PORT.

Обновление настроек среды вашего приложения

В этом разделе вы расширите свой сервер Express, чтобы разрешить чтение конфигурации порта приложения из переменной среды. Поскольку конфигурация, скорее всего, изменится между развертываниями, это обновление позволит вашему приложению считывать порт из среды платформы приложений.

Откройте файл server.js в своем редакторе. Затем в нижней части файла обновите выделенный код, заменив существующую строку app.listen и добавив новую строку const port:

...

const port = process.env.PORT || 3000;
app.listen(port, () =>
  console.log(`Example app is listening on port ${port}.`)
);

Этот код указывает на использование переменной среды PORT, если она существует, или по умолчанию на порт 3000 в противном случае.

Теперь ваше приложение сможет считывать порт из среды App Platform, в которой вы его развернете.

Создание и развертывание вашего приложения на платформе приложений

Теперь вы можете настроить свое приложение с помощью App Platform.

С вас будет взиматься плата за запуск этого приложения на платформе приложений, при этом счета за веб-службы будут взиматься посекундно (начиная минимум с одной минуты). Цены отображаются на экране просмотра. Подробнее см. в разделе Цены на платформу приложений.

Сначала войдите в свою учетную запись DigitalOcean. Из документации по продукту «Как создавать приложения на платформе приложений».

На экране «Создать ресурс из исходного кода» выберите GitHub в качестве поставщика услуг. Затем дайте DigitalOcean разрешение на доступ к вашему репозиторию. Лучше всего выбрать только тот репозиторий, который вы хотите развернуть. Если вы этого не сделали, вам будет предложено установить приложение DigitalOcean GitHub. Выберите свой репозиторий из списка и нажмите «Далее».

На экране «Ресурсы» нажмите «Редактировать план», чтобы выбрать размер плана. В этом руководстве будет использоваться базовый план с веб-службами наименьшего размера (512 МБ ОЗУ | 1 виртуальный ЦП) для ресурса express-memcache. Базовый план и наименьшая веб-служба предлагают достаточно ресурсов для этого примера экспресс-приложения. После того, как вы установили свой план, нажмите «Назад».

Затем щелкните вкладку «Информация» на левой панели навигации и обратите внимание на регион, в котором находится ваше приложение. Это понадобится вам на следующем шаге, когда вы добавите надстройку DigitalOcean Marketplace для MemCachier.

Наконец, щелкните вкладку «Обзор», затем нажмите кнопку «Создать ресурсы», чтобы создать и развернуть приложение. Для запуска сборки потребуется некоторое время. Когда он будет завершен, вы получите сообщение об успешном завершении со ссылкой на ваше развернутое приложение.

Итак, вы создали приложение Express, которое находит простое число и имеет кнопку «Нравится». Вы зафиксировали код приложения в Git и отправили его на GitHub, а затем развернули приложение на платформе приложений.

Чтобы сделать ваше приложение Express более быстрым и масштабируемым, вы реализуете три стратегии кэширования объектов. Вам нужен кеш, который вы создадите на следующем шаге.

Шаг 5 — Настройка кэша объектов с помощью MemCachier

На этом шаге вы создадите и настроите кеш объектов. Любое дополнение MemCachier из DigitalOcean Marketplace. Кэш MemCachier — это хранилище ключей и значений в памяти.

Сначала вы добавите надстройку MemCachier из DigitalOcean Marketplace. Посетите страницу надстройки MemCachier и нажмите Добавить MemCachier. На следующем экране выберите регион, в котором находится ваше приложение App Platform, которое вы отметили ранее. Ваше приложение и кеш должны находиться в одном регионе, чтобы задержка была минимальной. Вы можете просмотреть настройки своего приложения App Platform, если вам нужно снова найти регион. При желании вы можете выбрать план. Затем нажмите «Добавить MemCachier», чтобы подготовить кеш.

Чтобы выяснить сопоставление имени региона с ярлыком, см. Доступные центры обработки данных DigitalOcean. Например, регион Сан-Франциско соответствует sfo3.

Далее вы настроите приложение Express для использования кеша. Посетите панель надстроек, затем щелкните имя своей надстройки MemCachier, чтобы открыть ее панель управления. На панели инструментов надстройки MemCachier нажмите кнопку «Показать» для переменных конфигурации, чтобы загрузить отображение со значениями для MEMCACHIER_USERNAME, MEMCACHIER_PASSWORD и MEMCACHIER_SERVERS. . Обратите внимание на эти значения, поскольку они понадобятся вам в дальнейшем.

Теперь вы сохраните переменные конфигурации MemCachier в качестве переменных среды для своего приложения. Вернитесь на панель инструментов приложения App Platform и нажмите «Настройки». Затем в разделе «Компоненты» нажмите express-memc…. Прокрутите до раздела «Переменные среды», нажмите «Изменить», а затем добавьте переменные конфигурации MemCachier с тремя ключами (MEMCACHIER_USERNAME, MEMCACHIER_PASSWORD и MEMCACHIER_SERVERS) и соответствующие значения, которые вы получили на панели инструментов MemCachier. Для MEMCACHIER_PASSWORD установите флажок Зашифровать, поскольку значение является паролем. Нажмите Сохранить, чтобы обновить приложение.

Теперь вы настроите клиент memcache в своем приложении Express, используя только что сохраненные переменные среды, чтобы приложение могло взаимодействовать с вашим кешем.

В терминале установите библиотеку memjs:

  1. npm install memjs

Затем создайте каталог services. Затем создайте файл services/memcache.js и откройте его в редакторе. В верхней части файла импортируйте memjs и настройте клиент кеша:

const { Client } = require('memjs');

module.exports = Client.create(process.env.MEMCACHIER_SERVERS, {
  failover: true,
  timeout: 1,
  keepAlive: true,
});

Сохраните файл.

Этот код создает клиент кэша MemCachier. Что касается параметров, для параметра failover установлено значение true, чтобы использовать кластеры высокой доступности MemCachier. В случае сбоя сервера команды для всех ключей, хранящихся на этом сервере, будут автоматически переданы следующему доступному серверу. время ожидания в 1 секунду лучше для развернутого приложения, чем 0,5 секунды по умолчанию. keepAlive: true сохраняет соединения с вашим кешем открытыми даже в режиме ожидания, что желательно, потому что соединение устанавливается медленно, а кеши должны быть быстрыми, чтобы быть эффективными.

На этом шаге вы подготовили кеш с помощью надстройки MemCachier из DigitalOcean Marketplace. Затем вы добавили параметры конфигурации вашего кеша в качестве переменных среды платформы приложений, что позволило вам настроить клиент с помощью memjs, чтобы ваше приложение Express могло взаимодействовать с кешем.

Все готово для реализации кэширования в Express, что вы и сделаете дальше.

Шаг 6 — Внедрение кэширования в Express с помощью MemCachier

Развернув приложение Express и предоставив надстройку MemCachier, вы теперь можете использовать кеш объектов. На этом шаге вы реализуете три метода кэширования объектов. Вы начнете с кэширования ресурсоемких вычислений, чтобы повысить скорость и эффективность использования. Затем вы реализуете методы кэширования визуализированных представлений после ввода данных пользователем, чтобы улучшить обработку запросов и кэшировать кратковременные сеансы в ожидании масштабирования вашего приложения за рамками этого руководства.

Кэширование высокоресурсных вычислений

В этом разделе вы будете кэшировать ресурсоемкие вычисления, чтобы ускорить ваше приложение, что приведет к более эффективному использованию ЦП. Функция findPrime является ресурсоемким вычислением, когда передается достаточно большое число. Вы кэшируете его результат и обслуживаете это кэшированное значение, когда оно доступно, вместо повторения вычислений.

Сначала откройте server.js, чтобы добавить клиент memcache:

const express = require('express');
const findPrime = require('./utils/findPrime');
const memcache = require('./services/memcache');

...

Затем сохраните вычисленное простое число в кеше:

...

  const prime = findPrime(n);

  const key = 'prime_' + n;

  memcache.set(key, prime.toString(), { expires: 0 }, (err) => {
    if (err) console.log(err);
  });

...

Сохраните файл.

Метод set принимает ключ в качестве первого параметра и значение строки в качестве второго, поэтому вы преобразуете простое число в строку. Третий аргумент options гарантирует, что срок хранения сохраненного элемента никогда не истечет. Четвертый и последний параметр — это необязательный обратный вызов, при котором выдается ошибка, если она присутствует.

Примечание. Рекомендуется корректно обрабатывать ошибки кэша. Кэш является усовершенствованием и, как правило, не должен вызывать сбой приложения при сбое. Приложение может прекрасно работать, хотя и медленнее, без кэша.

Примечание. На этом этапе ваше приложение продолжит работать локально, но без кэширования. При вызове memcache.set будет выведена ошибка, потому что он не сможет найти сервер кеша:

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

Для остальной части этого руководства вам не нужно локальное кэширование. Если вы хотите, чтобы это работало, вы можете запустить memcached на localhost:11211, который используется memjs по умолчанию.

Затем подготовьте и зафиксируйте ваши изменения:

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

Затем отправьте эти изменения на GitHub, который должен автоматически развернуться на платформе приложений:

  1. git push

На панели инструментов App Platform сообщение «Развернуто» изменится на сообщение, указывающее на то, что ваше приложение находится в процессе сборки. Когда сборка будет завершена, откройте приложение в браузере и введите число, чтобы найти наибольшее простое число.

Примечание. На панели управления может отображаться сообщение Ожидание обслуживания. Это сообщение обычно разрешается само по себе. Если он задерживается, попробуйте обновить приложение, чтобы проверить, развернута ли сборка.

Затем вернитесь на панель надстроек, затем щелкните параметр View MemCachier для названной службы, чтобы просмотреть панель аналитики кэша.

На этой панели параметр Set Cmds на доске All Time Stats и статистика Items на доске Storage увеличились на 1. Каждый раз, когда вы отправляете номер, набор команд и элементов будут увеличиваться. Вы должны нажать кнопку «Обновить», чтобы загрузить новую статистику.

Примечание. Проверка журналов вашего приложения на App Platform может быть полезной для отладки. На панели управления вашего приложения щелкните Журналы выполнения, чтобы просмотреть их.

С элементами, хранящимися в кеше, вы можете их использовать. Теперь вы проверите, кэширован ли элемент, и если да, вы будете обслуживать его из кэша; в противном случае вы найдете простое число, как и раньше.

Вернувшись в server.js, обновите файл, добавив в него выделенные строки. Вы как измените существующие строки, так и добавите новые строки для кеша:

...

app.get('/', (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.render('index');
    return;
  }

  let prime;

  const key = 'prime_' + n;

  memcache.get(key, (err, val) => {
    if (err) console.log(err);

    if (val !== null) {
      // Use the value from the cache
      // Convert Buffer string before converting to number
      prime = parseInt(val.toString());
    } else {
      // No cached value available, find it
      prime = findPrime(n);

      memcache.set(key, prime.toString(), { expires: 0 }, (err) => {
        if (err) console.log(err);
      });
    }

    // Initialize likes for this number when necessary
    if (!likesMap[n]) likesMap[n] = 0;

    const locals = { n, prime, likes: likesMap[n] };
    res.render('index', locals);
  });
});

...

Сохраните файл.

Этот код инициализирует prime без значения, используя ключевое слово let, поскольку его значение теперь переназначается. Затем memcache.get пытается получить кэшированное простое число. Большая часть кода контроллера теперь находится в обратном вызове memcache.get, потому что его результат необходим для определения того, как обрабатывать запрос. Если кэшированное значение доступно, используйте его. В противном случае выполните вычисление, чтобы найти простое число, и сохраните результат в кеше, как и раньше.

Значение, возвращаемое обратным вызовом memcache.get, представляет собой Buffer, поэтому вы конвертируете его в строку перед преобразованием prime обратно в число.

Зафиксируйте свои изменения и отправьте их на GitHub для развертывания:

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

Когда вы отправляете число, еще не кэшированное в вашем приложении, статистика Set Cmds, Items и get misses на панели инструментов MemCachier увеличится на 1. Промах происходит из-за того, что вы пытаетесь получить элемент из кеша перед его установкой. Элемент отсутствует в кеше, что приводит к промаху, после чего элемент сохраняется. Когда вы отправляете кешированный номер, число обращений увеличивается.

Теперь вы кэшируете ресурсоемкие вычисления. Затем вы кэшируете визуализированные представления вашего приложения.

Кэширование визуализированных представлений

В этом разделе вы будете кэшировать представления, отображаемые вашим приложением Express, с промежуточным программным обеспечением. Ранее вы настроили ejs в качестве механизма шаблонов и создали шаблон для отображения представлений для каждого отправленного числа N. Для создания визуализированных представлений может потребоваться много ресурсов, поэтому их кэширование может ускорить обработку запросов и использовать меньше ресурсов.

Для начала создайте каталог middleware. Затем создайте файл middleware/cacheView.js и откройте его в редакторе. В cacheView.js добавьте эти строки для функции промежуточного программного обеспечения:

const memcache = require('../services/memcache');

/**
 * Express middleware to cache views and serve cached views
 */
module.exports = function (req, res, next) {
  const key = `view_${req.url}`;

  memcache.get(key, (err, val) => {
    if (err) console.log(err);

    if (val !== null) {
      // Convert Buffer string to send as the response body
      res.send(val.toString());
      return;
    }
  });
};

Сначала вы импортируете клиент memcache. Затем вы объявляете ключ, например view_/?n=100. Затем вы проверяете, находится ли представление для этого ключа в кеше с помощью memcache.get. Если ошибки нет и для этого ключа существует значение, делать больше нечего, поэтому запрос завершается отправкой представления обратно клиенту.

Далее, если представление не кэшируется, вы хотите его кэшировать. Для этого переопределите метод res.send, добавив выделенные строки:

...

module.exports = function (req, res, next) {
  const key = `view_${req.url}`;

  memcache.get(key, (err, val) => {
    if (err) console.log(err);

    if (val !== null) {
      // Convert Buffer to UTF-8 string to send as the response body
      res.send(val.toString());
      return;
    }

    const originalSend = res.send;
    res.send = function (body) {
      memcache.set(key, body, { expires: 0 }, (err) => {
        if (err) console.log(err);
      });

      originalSend.call(this, body);
    };
  });
};

Вы переопределяете метод res.send функцией, которая сохраняет представление в кеше перед вызовом исходной функции send, как обычно. Вы вызываете исходную функцию send с помощью call, которая устанавливает свой контекст this таким, каким он был бы, если бы не был переопределен. Обязательно используйте функцию стрелки), чтобы было указано правильное значение this.

Затем передайте управление следующему промежуточному ПО, добавив выделенную строку:

...

/**
 * Express middleware to cache views and serve cached views
 */
module.exports = function (req, res, next) {
  const key = `view_${req.url}`;

  memcache.get(key, (err, val) => {
    if (err) console.log(err);

    if (val !== null) {
      // Convert Buffer to UTF-8 string to send as the response body
      res.send(val.toString());
      return;
    }

    const originalSend = res.send;
    res.send = function (body) {
      memcache.set(key, body, { expires: 0 }, (err) => {
        if (err) console.log(err);
      });

      originalSend.call(this, body);
    };

    next();
  });
};

...

Вызов next вызывает следующую промежуточную функцию в приложении. В вашем случае другого промежуточного ПО нет, поэтому вызывается контроллер. Метод res.render для Express визуализирует представление, а затем вызывает внутри себя res.send с этим визуализированным представлением. Итак, теперь в контроллере для домашнего маршрута ваша функция переопределения вызывается при вызове res.render, сохраняя представление в кеше перед окончательным вызовом исходной функции send чтобы завершить ответ.

Сохраните файл.

Примечание. Вы также можете передать обратный вызов методу render в контроллере, но вам придется дублировать код кэширования представления в контроллере для каждого кэшируемого маршрута.

Теперь импортируйте промежуточное ПО для кэширования представлений в server.js:

const express = require('express');
const findPrime = require('./utils/findPrime');
const memcache = require('./services/memcache');
const cacheView = require('./middleware/cacheView');

...

Добавьте выделенный код, чтобы использовать его с домашним маршрутом GET /:

...

app.get('/', cacheView, (req, res) => {
  ...
});

...

Сохраните файл.

Затем зафиксируйте свои изменения и отправьте их на GitHub для развертывания:

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

Все должно работать как обычно, когда вы отправляете число в своем приложении. Если вы отправляете новое число, статистика на панели инструментов MemCachier для Set Cmds, Items и get misss увеличивается на два: один раз для вычисления простого числа и один раз для представления. Если вы обновите приложение с тем же номером, вы увидите один хит, добавленный на панель инструментов MemCachier. Представление успешно извлекается из кеша, поэтому нет необходимости извлекать результат простого числа.

Примечание. Параметр приложения Express view cache включен по умолчанию в рабочей среде. Этот кеш представления не кэширует содержимое вывода шаблона, а только сам базовый шаблон. Представление перерисовывается при каждом запросе, даже если кеширование включено. Таким образом, он отличается, но дополняет реализованное вами кэширование визуализированного представления.

Теперь, когда вы кэшируете просмотры, вы можете заметить, что кнопка «Нравится» больше не работает. Если вы зарегистрируете значение likes, оно действительно изменится. Тем не менее, кешированное представление по-прежнему необходимо обновлять при изменении количества лайков. Кэшированное представление должно быть аннулировано при изменении представления.

Затем, когда лайки изменятся, вы аннулируете кешированное представление, удалив его из кеша. Вернитесь в server.js, обновите функцию redirect и добавьте выделенные строки:

...

app.get('/like', (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.redirect('/');
    return;
  }

  likesMap[n]++;

  // The URL of the page being 'liked'
  const url = `/?n=${n}`;

  res.redirect(url);
});

...

Количество лайков для этого представления изменилось, поэтому кэшированная версия будет недействительной. Добавьте выделенные строки, чтобы удалить количество лайков из кеша при изменении лайков:

...
  const url = `/?n=${n}`;

  // The view for this URL has changed, so the cached version is no longer valid, delete it from the cache.
  const key = `view_${url}`;
  memcache.delete(key, (err) => {
    if (err) console.log(err);
  });

  res.redirect(url);
...

Теперь ваш файл server.js должен соответствовать следующему:

const express = require('express');
const findPrime = require('./utils/findPrime');
const memcache = require('./services/memcache');
const cacheView = require('./middleware/cacheView');

const app = express();

app.set('view engine', 'ejs');

/**
 * Key is `n`
 * Value is the number of 'likes' for `n`
 */
const likesMap = {};

app.get('/', cacheView, (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.render('index');
    return;
  }

  let prime;

  const key = 'prime_' + n;

  memcache.get(key, (err, val) => {
    if (err) console.log(err);

    if (val !== null) {
      // Use the value from the cache
      // Convert Buffer string before converting to number
      prime = parseInt(val.toString());
    } else {
      // No cached value available, find it
      prime = findPrime(n);

      memcache.set(key, prime.toString(), { expires: 0 }, (err) => {
        if (err) console.log(err);
      });
    }

    // Initialize likes for this number when necessary
    if (!likesMap[n]) likesMap[n] = 0;

    const locals = { n, prime, likes: likesMap[n] };
    res.render('index', locals);
  });
});

app.get('/like', (req, res) => {
  const n = req.query.n;

  if (!n) {
    res.redirect('/');
    return;
  }

  likesMap[n]++;

  // The URL of the page being 'liked'
  const url = `/?n=${n}`;

  // The view for this URL has changed, so the cached version is no longer valid, delete it from the cache.
  const key = `view_${url}`;
  memcache.delete(key, (err) => {
    if (err) console.log(err);
  });

  res.redirect(url);
});

const port = process.env.PORT || 3000;
app.listen(port, () =>
  console.log(`Example app is listening on port ${port}.`)
);

Сохраните файл.

Зафиксируйте и отправьте изменения для развертывания:

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

Кнопка «Нравится» в вашем приложении теперь будет работать. Следующая статистика будет меняться на вашей панели управления MemCachier, когда просмотр нравится:

  • удалять количество обращений по мере удаления представления.
  • количество промахов увеличивается, поскольку представление было удалено и отсутствует в кеше.
  • получить приращение обращений, так как в кеше было найдено простое число.
  • Команды Set Cmds увеличиваются, так как обновленное представление добавляется в кеш.
  • Элементы остаются прежними, поскольку представление удаляется и добавляется повторно.

Вы реализовали кэширование визуализированных представлений и аннулирование кэшированных представлений при их изменении. Последней стратегией, которую вы будете реализовывать, является кэширование сеанса.

Кэширование сеансов

В этом разделе вы добавите и кэшируете сеансы в своем приложении Express, сделав ваш кэш хранилищем сеансов. Обычным вариантом использования сеансов является вход пользователей в систему, поэтому вы можете рассматривать этот раздел о кэшировании сеансов как предварительный шаг для реализации системы входа пользователей в будущем (хотя система входа пользователей выходит за рамки этого руководства). Хранение краткосрочных сеансов в кэше может быть быстрее и более масштабируемым, чем хранение во многих базах данных.

Примечание. Кэш идеально подходит для хранения кратковременных сеансов с истекшим временем ожидания. Однако кэши не являются постоянными; долгоживущие сеансы лучше подходят для решений постоянного хранения, таких как базы данных.

Установите connect-memjs, чтобы разрешить использование кэша MemCachier в качестве хранилища сеансов:

  1. npm install express-session connect-memjs

В server.js импортируйте express-session и connect-memjs:

const express = require('express');
const findPrime = require('./utils/findPrime');
const memcache = require('./services/memcache');
const cacheView = require('./middleware/cacheView');
const session = require('express-session');
const MemcacheStore = require('connect-memjs')(session);

...

Сохраните файл.

ПО промежуточного слоя сеанса передается в модуль memcached connect, что позволяет ему наследовать от express.session.Store.

По-прежнему в server.js настройте ПО промежуточного слоя сеанса для использования кэша в качестве хранилища. Добавьте выделенные строки:

...

app.set('view engine', 'ejs');

app.use(
  session({
    secret: 'your-session-secret',
    resave: false,
    saveUninitialized: true,
    store: new MemcacheStore({
      servers: [process.env.MEMCACHIER_SERVERS],
      prefix: 'session_',
    }),
  })
);

...

secret используется для подписи файла cookie сеанса. Обязательно обновите ваш-сеанс-секрет уникальной строкой.

Примечание. Вы должны использовать переменную среды, чтобы установить свой секрет для производственных настроек. Для этого вы можете установить секрет с помощью secret: process.env.SESSION_SECRET || your-session-secret, хотя вам также потребуется установить переменную среды на панели инструментов App Platform.

resave заставляет сеанс повторно сохраняться, если он не был изменен во время запроса. Вы не хотите снова сохранять элемент в кеше без необходимости, поэтому установите для него значение false.

saveUninitialized: false полезен, когда вы хотите сохранить только измененные сеансы, как это часто бывает с сеансами входа в систему, когда свойство пользователя может быть добавлено к сеансу после аутентификации. В этом случае вы будете хранить все сеансы без разбора, поэтому установите для него значение true.

Наконец, установите store в свой кеш, установив префикс для ключей кэша сеанса на session_. Это означает, что ключ для элемента сеанса в кеше будет выглядеть как session_.

Затем добавьте промежуточное ПО для отладки на уровне приложения с выделенными строками, которое поможет идентифицировать кешированные сеансы в действии:

...

app.use(
  session({
    ...
  })
);

/**
 * Session sanity check middleware
 */
app.use(function (req, res, next) {
  console.log('Session ID:', req.session.id);

  // Get the item from the cache
  memcache.get(`session_${req.session.id}`, (err, val) => {
    if (err) console.log(err);

    if (val !== null) {
      console.log('Session from cache:', val.toString());
    }
  });

  next();
});

...

Это промежуточное ПО будет регистрировать идентификатор сеанса для каждого запроса. Затем он получает сеанс для этого идентификатора из кеша и регистрирует его содержимое. Этот подход демонстрирует, что сеансы работают и кэшируются.

Сохраните файл, затем зафиксируйте и отправьте изменения в развертывание.

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

В своем приложении отправьте число, а затем проверьте журналы выполнения на панели инструментов App Platform, чтобы получить доступ к сообщениям отладки. Вы найдете идентификатор сеанса и значение, которое вы зарегистрировали, демонстрируя, что сеансы работают и кэшируются.

На панели управления MemCachier после кэширования представления и сеанса вы увидите, что 3 получает обращения при каждом обновлении страницы: 1 для представления, 1 для сеанса и 1 для получения сеанса в промежуточном программном обеспечении отладки.

Теперь вы внедрили кэширование сеанса. Вы можете остановиться здесь или очистить свое приложение на необязательном последнем шаге.

(Необязательно) Шаг 7 — Очистка ваших ресурсов

За приложение, которое вы развернули в этом руководстве, будет взиматься плата, поэтому вы можете при желании уничтожить приложение и надстройку MemCachier, когда закончите работу с ними.

На панели инструментов приложения нажмите «Действия», затем «Удалить приложение».

Чтобы очистить надстройку MemCachier, нажмите «Дополнения», а затем название своей надстройки MemCachier. Затем нажмите «Настройки» и «Удалить». Бесплатный кеш MemCachier будет деактивирован через 30 дней бездействия, но рекомендуется почистить свои инструменты.

Заключение

В этом руководстве вы создали экспресс-приложение для поиска простого числа с помощью кнопки «Нравится». Затем вы отправили это приложение на GitHub и развернули его на платформе приложений DigitalOcean. Наконец, вы сделали приложение Express более быстрым и масштабируемым, внедрив три метода кэширования объектов с надстройкой MemCachier для кэширования ресурсоемких вычислений, визуализированных представлений и сеансов. Вы можете просмотреть все файлы для этого руководства в репозитории сообщества DigitalOcean.

В каждой реализованной вами стратегии кэширования ключи имели префикс: prime_, view_ и session_. В дополнение к преимуществу пространства имен префикс предлагает дополнительное преимущество, позволяя вам профилировать производительность кэша. В этом руководстве вы использовали план разработчика MemCachier, но вы также можете ознакомиться с их документацией.

Чтобы продолжить разработку с помощью платформы приложений DigitalOcean, ознакомьтесь с нашей документацией по платформе приложений.