Help us understand the problem. What is going on with this article?

Redigoを使う(4) コネクションプールを使う

はじめに

RedisのGo言語向けクライアントライブラリRedigoの使い方を見ます。
本記事ではコネクションプールの使い方を見ていきます。

環境

基本の流れ

コネクションプールとは、Redisサーバへ接続するときにコネクションを再利用することで接続を高速化する機能です。

さっそくですが、Redigoでコネクションプールを利用する典型的なコードを以下に示します。

main.go
package main

import (
    "fmt"
    "time"

    "github.com/gomodule/redigo/redis"
)

func newPool(addr string) *redis.Pool {
    return &redis.Pool{
        MaxIdle:     3,
        MaxActive:   0,
        IdleTimeout: 240 * time.Second,
        Dial:        func() (redis.Conn, error) { return redis.Dial("tcp", addr) },
    }
}

func main() {
    // コネクションプールの作成
    pool := newPool("localhost:6379")

    // コネクションの取得
    conn := pool.Get()
    defer conn.Close()

    // 値の書き込み
    w, err := conn.Do("SET", "temperature", 20)
    if err != nil {
        panic(err)
    }
    fmt.Println(w) // OK

    // 値の取得
    r, err := redis.Int(conn.Do("GET", "temperature"))
    if err != nil {
        panic(err)
    }
    fmt.Println(r) // 20
}

redis.Pool構造体を始めに作成しておき、Redisの操作が必要になるごとにpool.Get()でプールからコネクションを取得し、使い終わったらconn.Close()する、というのが基本的な使い方です。

ウェブアプリのサーバで使うときは、起動時にプールを作成してグローバル変数などで持っておいて、リクエストをハンドルする関数の先頭でpool.Get()defer conn.Close()を行うという書き方になると思います。

プールのコネクション管理

プールのコネクション管理の仕方と、パラメータについて理解しておきましょう。

プールの起動直後は、プールはコネクションを1つも持たない状態です。pool.Getが呼ばれると、プールはサーバとの間で新たなコネクションを確立し、戻り値として返します。そしてそのコネクションでconn.Closeが呼ばれたとき、プールはサーバとの間のコネクションを切断せずに、「アイドル状態」のコネクションとして蓄えておきます。その後で再度pool.Getが呼ばれたときは、プールはアイドル状態のコネクションを「利用中」に変えて戻り値として返します。もしアイドル状態のコネクションがなければ、新しいコネクションを確立します。

デフォルトでは、リソースが許す限りいくつでもコネクションを確立できます。パラメータMaxActiveを使うと、個数に上限を設けることができます。その場合、MaxActiveを超えてコネクションをpool.Getしたとき、そのコネクションでconn.Doなどを呼ぶとErrPoolExhaustedエラーが返ってきます。あるいは、パラメータWaitをtrueにしておくと、コネクション数がMaxActiveを下回るまで、pool.Getの呼び出しをブロッキングで待たせられます。

また、パラメータMaxIdleでは、アイドル状態のコネクション数の上限を設けることができます。コネクションのconn.Closeが呼ばれたとき、既に上限数のアイドルのコネクションが存在していれば、プールはコネクションをアイドル状態にせず、サーバから切断します。

以下は、上記の挙動を理解するためのコードです。アイドルのコネクションの上限数をMaxIdle = 3、コネクションの総数の上限をMaxActive = 6とした状態で、10個のコネクションの確立と終了を1個ずつ試み、pool.Statsで得たプールの状態をつど出力しています。

main.go
package main

import (
    "fmt"
    "time"

    "github.com/gomodule/redigo/redis"
)

func newPool(addr string) *redis.Pool {
    return &redis.Pool{
        MaxIdle:     3,
        MaxActive:   6,
        IdleTimeout: 240 * time.Second,
        Dial:        func() (redis.Conn, error) { return redis.Dial("tcp", addr) },
    }
}

func printStats(pool *redis.Pool) {
    s := pool.Stats()
    fmt.Printf("Active: %d, Idle: %d, InUse: %d\n",
        s.ActiveCount, s.IdleCount, s.ActiveCount-s.IdleCount)
}

func main() {
    // コネクションプールの作成
    pool := newPool("localhost:6379")

    printStats(pool)
    fmt.Println("------")

    // コネクションを作成
    conns := make([]redis.Conn, 10, 10)
    for i := 0; i < 10; i++ {
        conns[i] = pool.Get()
        printStats(pool)
    }
    fmt.Println("------")

    // コネクションをクローズ
    for i := 0; i < 10; i++ {
        conns[i].Close()
        printStats(pool)
    }
}

実行結果は以下のようになります。アクティブ(=使用中+アイドル)のコネクション数が6を超えておらず、またアイドルの数が3を超えることがない点を確認し下さい。

Active: 0, Idle: 0, InUse: 0
------
Active: 1, Idle: 0, InUse: 1
Active: 2, Idle: 0, InUse: 2
Active: 3, Idle: 0, InUse: 3
Active: 4, Idle: 0, InUse: 4
Active: 5, Idle: 0, InUse: 5
Active: 6, Idle: 0, InUse: 6
Active: 6, Idle: 0, InUse: 6
Active: 6, Idle: 0, InUse: 6
Active: 6, Idle: 0, InUse: 6
Active: 6, Idle: 0, InUse: 6
------
Active: 6, Idle: 1, InUse: 5
Active: 6, Idle: 2, InUse: 4
Active: 6, Idle: 3, InUse: 3
Active: 5, Idle: 3, InUse: 2
Active: 4, Idle: 3, InUse: 1
Active: 3, Idle: 3, InUse: 0
Active: 3, Idle: 3, InUse: 0
Active: 3, Idle: 3, InUse: 0
Active: 3, Idle: 3, InUse: 0
Active: 3, Idle: 3, InUse: 0

なお、デフォルトでは、アイドル状態のコネクションは永久にクローズされません。IdleTimeoutまたはMaxConnLifetimeのパラメータを設定することで、期限を決めてクローズさせることができます。アイドル状態になってからIdleTimeoutの時間が経過したコネクションはクローズされます。また確立されてからMaxConnLifetimeの時間が経過したコネクションもクローズされます。

おわりに

Redigoのコネクションプールの使い方を見ました。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした