はじめに
RedisのGo言語向けクライアントライブラリRedigoの使い方を見ます。
本記事ではコネクションプールの使い方を見ていきます。
環境
- OS: Windows 10
- Redis: win-3.2.100
- Go言語: 1.11
基本の流れ
コネクションプールとは、Redisサーバへ接続するときにコネクションを再利用することで接続を高速化する機能です。
さっそくですが、Redigoでコネクションプールを利用する典型的なコードを以下に示します。
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で得たプールの状態をつど出力しています。
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のコネクションプールの使い方を見ました。