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 とのインテグレーション
  • 言語サポート
  • Go
  • Memcache を使用した Gin アプリケーションのスケーリング

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 を使用した Gin アプリケーションのスケーリング

日本語 — Switch to English

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

Table of Contents

  • 前提条件
  • Heroku への Gin アプリケーションのデプロイ
  • キャッシングを Gin に追加する
  • 参考情報と資料

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

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

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

Heroku にデプロイ

前提条件

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

  • Go の知識がある (Gin の知識もあれば理想的です)
  • Heroku ユーザーアカウント (無料ですぐにサインアップ)
  • 「Heroku スターターガイド (Go)​」の手順を理解している
  • Go、govendor​、Heroku CLI​ がコンピュータにインストールされている
  • GOPATH​ 環境変数が設定されていることの確認

Heroku への Gin アプリケーションのデプロイ

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

$ cd $GOPATH/src
$ mkdir gin_memcache
$ cd gin_memcache
$ govendor init
$ govendor fetch github.com/gin-gonic/gin@v1.2

Gin フレームワークをインストールしたので、アプリのコードを追加できます。訪問者が送信した数よりも小さい最大の素数を計算するページを作成します。

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

package main

import (
  "net/http"
  "os"
  "strconv"

  "github.com/gin-gonic/gin"
)

func main() {
  port := os.Getenv("PORT")

  if port == "" {
    port = "3000"
  }

  router := gin.New()
  router.Use(gin.Logger())
  router.LoadHTMLGlob("templates/*.tmpl.html")
  router.Static("/static", "static")

  router.GET("/", func(c *gin.Context) {
    n := c.Query("n")
    if n == "" {
      // Render view
      c.HTML(http.StatusOK, "index.tmpl.html", nil)
    } else {
      i, err := strconv.Atoi(n)
      if err != nil || i < 1 || i > 10000 {
        // Render view with error
        c.HTML(http.StatusOK, "index.tmpl.html", gin.H{
          "error": "Please submit a valid number between 1 and 10000.",
        })
      } else {
        p := calculatePrime(i)
        // Render view with prime
        c.HTML(http.StatusOK, "index.tmpl.html", gin.H{"n": i, "prime": p})
      }
    }
  })

  router.Run(":" + port)
}

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

次に、対応するビューを追加しましょう。templates/index.tmpl.html​ ファイルを作成し、 次のコードを貼り付けます。

{{ define "index.tmpl.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>Gin caching example</title>
  </head>

  <body>
    <div class="container">
      <h1>
        Gin 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 .prime }}
        <div class="alert alert-primary">
          <p class="lead">Largest prime less or equal than {{ .n }} is {{ .prime }}</p>
        </div>
      {{ end }}

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

    </div>
  </body>
</html>
{{ end }}

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

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

$ echo web: gin_memcache > Procfile

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

$ echo 'vendor/*' > .gitignore
$ echo '!vendor/vendor.json' >> .gitignore

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

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

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

$ heroku create
$ git push heroku master
$ heroku open

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

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

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

Memcache のセットアップ

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

$ heroku addons:create memcachier:dev

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

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

$ govendor fetch github.com/memcachier/mc

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

package main

import (
  // ...
  "github.com/memcachier/mc"
)

func main() {
  username := os.Getenv("MEMCACHIER_USERNAME")
  password := os.Getenv("MEMCACHIER_PASSWORD")
  servers := os.Getenv("MEMCACHIER_SERVERS")

  mcClient := mc.NewMC(servers, username, password)
  defer mcClient.Quit()
  // ...
}
// ...

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

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

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

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

これを実現するために、main.go​ で GET​ ルートを変更して次の部分を

// ...
p = calculatePrime(i)
// ...

次のように置き換えます。

// ...
key := "prime." + strconv.Itoa(i)
p := 0
// Look in cache
val, _, _, err := mcClient.Get(key)
if err != nil {
  // Prime not in cache (calculate and store)
  p = calculatePrime(i)
  val = strconv.Itoa(p)
  mcClient.Set(key, val, 0, 0, 0)
} else {
  // Found it!
  p, _ = strconv.Atoi(val)
}
// ...

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

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

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

$ heroku addons:open memcachier

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

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

HTML ビューのレンダリングは概してコストの高い計算なので、可能であれば常に、レンダリングされたビューをキャッシュするべきです。Gin では、gin-contrib/cache​ ライブラリを使用してこれを容易に実現できます。govendor​ でライブラリを取得します。

$ govendor fetch github.com/gin-contrib/cache

ここで、main.go​ で次のようにして、レンダリングされたビューをキャッシュできます。

package main

import (
  // ...
  "github.com/gin-contrib/cache"
  "github.com/gin-contrib/cache/persistence"
  // ...
)

func main() {
  // ...
  mcStore := persistence.NewMemcachedBinaryStore(servers, username, password, persistence.FOREVER)

  router.GET("/", cache.CachePage(mcStore, persistence.DEFAULT, func(c *gin.Context) {
    // ...
  }))
  // ...
}
// ...

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

<!-- ... -->

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

<!-- ... -->

ここで、POST​ ルートのコントローラーを main.go​ に作成し、送信された “いいね” を変数に格納する必要があります。

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

// ...
func main() {
  // ...

  likes := make(map[string]int)
  router.POST("/", func(c *gin.Context){
    n := c.PostForm("n")
    likes[n] += 1
    c.Redirect(http.StatusMovedPermanently, "/?n=" + n)
  })

  router.GET("/", cache.CachePage(mcStore, persistence.DEFAULT, func(c *gin.Context) {
    // ...
  }))
  //...
}
// ...

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

// ...

// Render view with prime
c.HTML(http.StatusOK, "index.tmpl.html", gin.H{"n": i, "prime": p, "likes": likes[n] })

// ...

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

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

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

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

// ...

router.POST("/", func(c *gin.Context){
  n := c.PostForm("n")
  likes[n] += 1
  mcStore.Delete(cache.CreateKey("/?n=" + n))
  c.Redirect(http.StatusMovedPermanently, "/?n=" + n)
})

// ...

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

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

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

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

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

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

Gin でセッションを使用するには、gin-contrib/session​ が必要です。

$ govendor fetch github.com/gin-contrib/sessions
$ govendor fetch github.com/gin-contrib/sessions/memcached

main.go​ での設定は簡単です。

package main

import (
    // ...
  "github.com/gin-contrib/sessions"
  "github.com/gin-contrib/sessions/memcached"
  // ...
)

func main() {
  // ...
  // add below `router := gin.New()`
  sessionStore := memcached.NewMemcacheStore(mcClient, "", []byte("secret"))
  router.Use(sessions.Sessions("mysession", sessionStore))
  // ...
}
// ...

セッションを自由に使用できるようになりました。Gin でのセッションの使用法についての詳細は、gin-contrib/sessions の README​ を参照してください。

参考情報と資料

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

関連カテゴリー

  • Go
Que-Go を使用したバックグラウンドジョブ Que-Go を使用したバックグラウンドジョブ

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