Skip Navigation
Show nav
Dev Center
  • Get Started
  • ドキュメント
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • ドキュメント
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
View categories

Categories

  • Heroku のアーキテクチャ
    • Dyno (アプリコンテナ)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • スタック (オペレーティングシステムイメージ)
    • ネットワーキングと DNS
    • プラットフォームポリシー
    • プラットフォームの原則
  • Developer Tools
    • コマンドライン
    • Heroku VS Code Extension
  • デプロイ
    • Git を使用したデプロイ
    • Docker によるデプロイ
    • デプロイ統合
  • 継続的デリバリーとインテグレーション
    • 継続的統合
  • 言語サポート
    • Node.js
      • Working with Node.js
      • Node.js Behavior in Heroku
      • Troubleshooting Node.js Apps
    • Ruby
      • Rails のサポート
      • Bundler の使用
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Python でのバックグランドジョブ
      • Python Behavior in Heroku
      • Django の使用
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Maven の使用
      • Spring Boot の使用
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go の依存関係管理
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • データベースとデータ管理
    • Heroku Postgres
      • Postgres の基礎
      • Postgres スターターガイド
      • Postgres のパフォーマンス
      • Postgres のデータ転送と保持
      • Postgres の可用性
      • Postgres の特別なトピック
      • Migrating to Heroku Postgres
    • Heroku Data For Redis
    • Apache Kafka on Heroku
    • その他のデータストア
  • AI
    • Working with AI
    • Heroku Inference
      • Inference API
      • Quick Start Guides
      • AI Models
      • Inference Essentials
    • Vector Database
    • Model Context Protocol
  • モニタリングとメトリクス
    • ログ記録
  • アプリのパフォーマンス
  • アドオン
    • すべてのアドオン
  • 共同作業
  • セキュリティ
    • アプリのセキュリティ
    • ID と認証
      • シングルサインオン (SSO)
    • Private Space
      • インフラストラクチャネットワーキング
    • コンプライアンス
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Team
    • Heroku Connect (Salesforce 同期)
      • Heroku Connect の管理
      • Heroku Connect のリファレンス
      • Heroku Connect のトラブルシューティング
  • パターンとベストプラクティス
  • Heroku の拡張
    • Platform API
    • アプリの Webhook
    • Heroku Labs
    • アドオンのビルド
      • アドオン開発のタスク
      • アドオン API
      • アドオンのガイドラインと要件
    • CLI プラグインのビルド
    • 開発ビルドパック
    • Dev Center
  • アカウントと請求
  • トラブルシューティングとサポート
  • Salesforce とのインテグレーション
  • 言語サポート
  • Node.js
  • Working with Node.js
  • Memcache を使用した Express.js アプリケーションのスケーリング

This article was contributed by The MemCachier Add-on

MemCachier manages and scales clusters of memcache servers so you can focus on your app. Tell us how much memory you need and get started for free instantly. Add capacity later as you need it.

follow @MemCachier on Twitter

Memcache を使用した Express.js アプリケーションのスケーリング

日本語 — Switch to English

最終更新日 2020年06月12日(金)

Table of Contents

  • 前提条件
  • Heroku への Express.js アプリケーションのデプロイ
  • Express.js ミドルウェアの記述方法を学ぶ
  • キャッシングを Express に追加する
  • 参考情報と資料

Memcache は、Web アプリとモバイルアプリバックエンドのパフォーマンスとスケーラビリティを改善する技術です。ページの読み込みが遅すぎる場合や、アプリにスケーラビリティの問題がある場合は、Memcache の使用を検討してください。小規模なサイトであっても、Memcache の導入によってページの読み込みを高速化し、将来の変化にアプリを対応させることができます。

このガイドでは、単純な Express 4​ アプリケーションを 作成して Heroku にデプロイし、Memcache を追加してパフォーマンスのボトルネックを軽減する方法を示します。

このガイドで扱うサンプルアプリの動作は こちら​で確認できます。また、 ソースコードを確認​したり、 次の Heroku ボタンを使用してデプロイすることができます。

Heroku にデプロイ

前提条件

このガイドの手順を完了する前に、以下のすべての条件を満たしていることを確認してください。

  • Node.js の知識がある (Express.js の知識もあれば理想的です)
  • Heroku ユーザーアカウント (無料ですぐにサインアップ)
  • 「Heroku スターターガイド (Node.js)​」の手順を理解している
  • Node.js、npm​、Heroku CLI​ がコンピュータにインストールされている

Heroku への Express.js アプリケーションのデプロイ

Express.js は、最小限を指向し、アプリケーションスケルトンを必要としないフレームワークです。次のように、Node.js アプリを作成し、依存関係として express​ を追加するだけです。

$ mkdir express_memcache
$ cd express_memcache
$ npm init
  # choose a package name and make sure the entry point is app.js
$ npm install express

開発を簡素化するために、テンプレートエンジンを使用します。このチュートリアルでは ejs​ を使用しますが、mustache​、pug​、nunjucks​ など、任意のエンジンを使用できます。

$ npm install ejs

必要なパッケージをすべてインストールしたので、アプリのコードを追加できます。訪問者が送信した数よりも小さい最大の素数を計算するページを作成します。

app.js​ を作成し、次のコードを貼り付けます。

var express = require("express");
var app = express();

// Set template engine
app.set('view engine', 'ejs')

// Bind the app to a specified port
var port = process.env.PORT || 3000;
app.listen(port);
console.log("Listening on port " + port);

// Super simple algorithm to find largest prime <= n
var calculatePrime = function(n){
  var prime = 1;
  for (var i = n; i > 1; i--) {
    var is_prime = true;
    for (var j = 2; j < i; j++) {
      if (i % j == 0) {
        is_prime = false;
        break;
      }
    }
    if (is_prime) {
      prime = i;
      break;
    }
  }
  return prime;
}

// Set up the GET route
app.get('/', function (req, res) {
  if(req.query.n) {
    // Calculate prime and render view
    var prime = calculatePrime(req.query.n);
    res.render('index', { n: req.query.n, prime: prime});
  }
  else {
    // Render view without prime
    res.render('index', {});
  }
});

次に、対応するビューを追加しましょう。views/index.ejs​ ファイルを作成し、ejs​ で拡張した次の HTML を貼り付けます。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">

    <title>Express.js caching example</title>
  </head>

  <body>
    <div class="container">
      <h1>
        Express.js caching example
      </h1>

      <p class="lead">For any number N (max 10000), we'll find the largest prime number
        less than or equal to N.
      </p>
      <!-- Form to submit a number -->
      <form class="form-inline" action="/">
        <input type="text" class="form-control" name="n" />
        <input type="submit" class="btn btn-primary" value="Find Prime" />
      </form>

      <hr>
      <!-- Show the result -->
      <% if (locals.prime) { %>
        <div class="alert alert-primary">
          <p class="lead">Largest prime less or equal than <%= n %> is <%= prime %></p>
        </div>
      <% } %>

      <!-- TODO: Error handling -->

    </div>
  </body>
</html>

動作するアプリが完成したので、node app.js​ を実行して起動できます。

Heroku 上でアプリを動作させるには、アプリの実行方法を指示する Procfile​ を作成する必要があります。

$ echo web: node app.js > Procfile

アプリを Heroku にデプロイするには、Git リポジトリでアプリが管理されている必要があります。まず、.gitignore​ ファイルを作成します。

$ echo node_modules/ > .gitignore

次に、リポジトリを作成してアプリの初期状態をコミットします。

$ git init
$ git add .
$ git commit -m 'Initial express app'

最後に、Heroku アプリを作成し、そのアプリにコードをプッシュして、実行中のアプリを調べます。

$ heroku create
$ heroku config:add NODE_ENV=production
$ git push heroku master
$ heroku open

Express.js ミドルウェアの記述方法を学ぶ

素数計算アプリは動作はしますが、大きな欠点が 1 つあります。ユーザーが文字列などの無効な入力を送信できてしまうことです。入力を検証するために、Express でミドルウェア​を作成します。

Express で利用可能な検証ミドルウェアパッケージは何種類かあり、 ほとんどの場合、そのいずれかを使用することをお勧めします。このチュートリアルでは、例証目的のために独自の検証を作成します。

Express ミドルウェアは通常、リクエストとそれに対応する応答の詳細を検査し、必要に応じて変更する関数のチェーンで構成されます。各関数は 3 つのパラメータを取ります。

  • request​ オブジェクト
  • response​ オブジェクト
  • チェーン内の次のミドルウェア関数を表す next​ 関数

各ミドルウェア関数では、必要に応じて request​ および response​ オブジェクトを 変更できます。その後、next​ ミドルウェア関数を呼び出すか、または return​ を呼び出してチェーンを途中で終了させることができます。

このアプリのために、送信されたクエリを解析し、それが 10000 より小さい数値かどうかをチェックする検証ミドルウェア関数を作成します。

  • そうである場合、関数は next​ を呼び出します。
  • そうでない場合、関数はエラー応答を return​ します。

この関数を app.js​ に追加し、GET​ ルートの処理時に呼び出します。

// ...

var validate = function(req, res, next) {
  if(req.query.n) {
    number = parseInt(req.query.n, 10);
    if(isNaN(number) || number < 1 || number > 10000){
      res.render('index', {error: 'Please submit a valid number between 1 and 10000.'});
      return;
    }
    req.query.n = number;
  }
  next();
}

app.get('/', validate, function (req, res) {
  // ...

検証ミドルウェアからエラーメッセージが返されることがあり、その場合、index.ejs​ ビューに表示する必要があります。

<!-- Show the result -->
<!-- ... -->

<!-- Error handling -->
<% if (locals.error) { %>
  <div class="alert alert-danger">
    <p class="lead"><%= error %></p>
  </div>
<% } %>

変更をコミットしてデプロイします。

$ git commit -am 'Add input validation'
$ git push heroku master

アプリを開き、何か無効なクエリを送信して、実際にエラーメッセージが返されるかどうか確認します。

キャッシングを Express に追加する

Memcache はインメモリの分散キャッシュです。そのプライマリ API は、SET(key, value)​ と GET(key)​ の 2 つの操作で構成されます。 Memcache は、複数のサーバーに分散していますが、操作は一定の時間に実行されるハッシュマップ (または辞書) のようなものです。

Memcache の最も一般的な用途は、コストの高いデータベースクエリや HTML レンダリングをキャッシュし、これらの高コスト操作を繰り返す必要をなくすことです。

Memcache のセットアップ

Express で Memcache を使用するには、まず実際の Memcache キャッシュをプロビジョニングする必要があります。これは、MemCachier アドオン​から無料で簡単に入手できます。

$ heroku addons:create memcachier:dev

これにより、MEMCACHIER_SERVERS​、MEMCACHIER_USERNAME​、MEMCACHIER_PASSWORD​ の 3 つの環境設定が Heroku アプリケーションに 追加され、キャッシュに接続できるようなります。

Express でキャッシュを使用するには、npm​ を使用して memjs​ をインストールする必要があります。

$ npm install memjs

また、app.js​ でその設定を行う必要もあります。

// ...

var memjs = require('memjs')
var mc = memjs.Client.create(process.env.MEMCACHIER_SERVERS, {
  failover: true,  // default: false
  timeout: 1,      // default: 0.5 (seconds)
  keepAlive: true  // default: false
})

// ...

コストの高い計算のキャッシュ

コストの高い計算の結果をキャッシュすることが良い考えであるのには、2 つの理由があります。

  1. キャッシュから結果を取得した方がずっと高速であり、ユーザーエクスペリエンスも向上します。
  2. コストの高い計算には多くの CPU リソースが使われ、アプリのその他の部分の動作も低速になる可能性があります。

今回の素数計算では、入力値を 10000 までに制限しているため、実際にはコストの高い計算は発生しません。ただし、チュートリアルの目的のために、素数の計算はコストが高い計算なのでキャッシュしたいと仮定します。

これを実現するために、次のように app.js​ で GET​ ルートを変更してみます。

// ...

app.get('/', validate, function (req, res) {
  if(req.query.n) {
    var prime;
    var prime_key = 'prime.' + req.query.n;
    // Look in cache
    mc.get(prime_key, function(err, val) {
      if(err == null && val != null) {
        // Found it!
        prime = parseInt(val)
      }
      else {
        // Prime not in cache (calculate and store)
        prime = calculatePrime(req.query.n)
        mc.set(prime_key, '' + prime, {expires:0}, function(err, val){/* handle error */})
      }
      // Render view with prime
      res.render('index', { n: req.query.n, prime: prime });
    })
  }
  else {
    // Render view without prime
    res.render('index', {});
  }
});

// ...

これらの変更を Heroku にデプロイし、いくつかの数値を送信して素数を見つけます。

$ git commit -am 'Add caching'
$ git push heroku master

ページは以前と同じように動作するはずです。ただし、内部では、すでに計算された素数がキャッシュされるようになりました。キャッシュ内で何が起きているかを見るために、MemCachier のダッシュボードを開きます。

$ heroku addons:open memcachier

ダッシュボードでは、素数をリクエストするたびに統計を更新できます。最初に数値を入力すると、get misses​ が増加します。それ以降は、同じ数値をリクエストするたびに get hit​ が増加するはずです。

レンダリングされたビューのキャッシュ

HTML ビューのレンダリングは概してコストの高い計算なので、可能であれば常に、レンダリングされたビューをキャッシュするべきです。Express では、ミドルウェアを使用してこれを容易に実現できます。(クエリパラメータを含む) 与えられた URL のビューがキャッシュに存在するかどうかをチェックする cacheView​ ミドルウェア関数を app.js​ に追加してみましょう。

  • 存在する場合、ビューは即時にキャッシュから送信されます。
  • 存在しない場合、レンダリングされたビューをキャッシュするために send​ 関数を応答オブジェクトにラップし、next​ 関数を呼び出します。
// ...

var cacheView = function(req, res, next) {
  var view_key = '_view_cache_' + req.originalUrl || req.url;
  mc.get(view_key, function(err, val) {
    if(err == null && val != null) {
      // Found the rendered view -> send it immediately
      res.send(val.toString('utf8'));
      return;
    }
    // Cache the rendered view for future requests
    res.sendRes = res.send
    res.send = function(body){
      mc.set(view_key, body, {expires:0}, function(err, val){/* handle error */})
      res.sendRes(body);
    }
    next();
  });
}

app.get('/', validate, cacheView, function (req, res) {
  // ...

これはとても簡単で、正しく動作します。しかし、ビューに変更がある場合は注意が必要です。ページに変化がある場合の例として、個々の数値と、それに対して計算される最大素数に 「Like」 (いいね) ボタンを追加してみましょう。index.ejs​ ファイルで、計算された素数のすぐ下にこのボタンを配置します。

<!-- ... -->

<!-- Show the result -->
<% if (locals.prime) { %>
  <div class="alert alert-primary">
    <p class="lead">Largest prime less or equal than <%= n %> is <%= prime %></p>
    <p>Likes: <%= likes %></p>
    <form method='POST'>
      <input type="hidden" name="n" value="<%= n %>" />
      <input type="submit" class="btn btn-primary" value="Like!" />
    </form>
  </div>
<% } %>

<!-- ... -->

この “いいね” は POST​ リクエストを介して送信され、その入力を解析するには body-parser​ パッケージが必要です。

$ npm install body-parser

ここで、POST​ ルートのコントローラーを app.js​ に作成し、送信された “いいね” を変数に格納することができます。

“いいね” を変数に格納するのは適切ではありません。アプリが再起動するたびに、すべての “いいね” が 消去されてしまうからです。ここでは、便宜上そのようにしているだけです。本番アプリケーションでは、 このような情報はデータベースに保存してください。

// ...

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// Like storage (in a serious app you should use a permanent storage like a database)
var likes = {}

app.post('/', function (req, res) {
  likes[req.query.n] = (likes[req.query.n] || 0) + 1
  res.redirect('/?n=' + req.query.n)
});

// ...

さらに、"いいね" が render​ 関数に渡されることを確認する必要もあります。 これは GET​ コントローラーで行われます。

// ...

// Render view with prime
res.render('index', { n: req.query.n, prime: prime, likes: likes[req.query.n] || 0 });

// ...

ページが変化する場合の問題を実証するために、現在の実装をコミットしてテストしてみましょう。

$ git commit -am 'Add view caching'
$ git push heroku master

数値を送信すると、最大素数に加えてその下に [Like] ボタンが表示されるようになります。しかし、Like!​ をクリックしても “いいね” のカウントは増加しません。これは、ビューがキャッシュされているからです。

これを解決するには、キャッシュされたビューが更新されるたびにそのビューを無効化​する必要があります。

// ...

app.post('/', function (req, res) {
  mc.delete('_view_cache_/?n=' + req.body.n, function(err, val){/* handle error */});
  likes[req.query.n] = (likes[req.query.n] || 0) + 1
  res.redirect('/?n=' + req.query.n)
});

// ...

もう一度 Heroku にデプロイします。

$ git commit -am 'Fix view caching'
$ git push heroku master

“いいね” の数が増えることを確認できるようになりました。

セッションのキャッシング

Heroku では、再起動時に内容が失われる一時的なファイルシステムが dyno に備わっているため、セッション情報をディスクに保存することは推奨されていません。

Memcache は、タイムアウトがある短命セッションの情報を保存するのには適しています。しかし、Memcache はあくまでキャッシュであって永続的ではないため、寿命の長いセッションについては、データベースなどの永続的なストレージオプションの方が適しています。

Express でセッションを使用するには、express-session​ が必要です。Memcache でセッションを保存するには、connect-memjs​ が必要です。

$ npm install express-session connect-memjs

app.js​ での設定は簡単です。

//...

var session = require('express-session');
var MemcachedStore = require('connect-memjs')(session);

// Session config
app.use(session({
  secret: 'ClydeIsASquirrel',
  resave: 'false',
  saveUninitialized: 'false',
  store: new MemcachedStore({
    servers: [process.env.MEMCACHIER_SERVERS],
    prefix: '_session_'
  })
}));

//...

セッションを自由に使用できるようになりました。Express でのセッションの使用法についての詳細は、express-session のドキュメント​を参照してください。

参考情報と資料

  • MemCachier アドオンページ
  • MemCachier ドキュメント
  • Memcache の高度な使用法
  • Heroku スターターガイド (Node.js)

関連カテゴリー

  • Working with Node.js
Node.js 開発のベストプラクティス Node.js での Heroku の WebSocket の使用

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices