2019年8月27日火曜日

redigo 超入門

概要

gomodule/redigo は golang から Redis にアクセスするためのライブラリです
Sentinel や Redis Cluster を使う場合に別のライブラリと組み合わせる必要があります
今回は基本的な使い方を紹介します

環境

  • CentOS 7.6.1810
  • go 1.12.1
    • redigo d3876d43bbbeb98b4d67309b4d3eef49438ef74c

基本

まずは基本です
Redis Server に接続して info server コマンドを実行してみます

  • cd $GOPATH/src/github.com/hawksnowlog/a
  • vim main.go
package main

import (
        "fmt"
        "github.com/gomodule/redigo/redis"
)

func main() {
        c, err := redis.Dial("tcp", "localhost:6379")
        if err != nil {
                fmt.Println(err)
                return
        }
        defer c.Close()
        fmt.Println(redis.String(c.Do("info", "server")))
}
  • go fmt github.com/hawksnowlog/a
  • go build github.com/hawksnowlog/a
  • ./a

Redis Server の情報が文字列として表示されます
Do 関数を使って redis-cli で使うコマンドをそのまま指定します
サブコマンドがある場合は複数の引数として指定します

set/get

次に set/get コマンドを実行してみます
Redis 上では文字列として管理されますが golang 上で扱う場合には適切な型に変更して使います

  • vim main.go
package main

import (
        "fmt"
        "github.com/gomodule/redigo/redis"
)

func main() {
        c, err := redis.Dial("tcp", "localhost:6379")
        if err != nil {
                fmt.Println(err)
                return
        }
        defer c.Close()
        fmt.Println(c.Do("set", "key", "value"))
        fmt.Println(redis.String(c.Do("get", "key")))
        fmt.Println(c.Do("set", "key2", 0))
        fmt.Println(redis.Int(c.Do("get", "key2")))
}

=> value
=> 0

Do 関数で get したあとで redis.Stringredis.Int を使って golang 上で扱える型に変換します
このあと紹介しますがリストやハッシュなども同じように変換してから使います

リスト型

rpush や lset, lrange の使い方です
ポイントは redis.Strings を使って文字列のスライスに変換してあげる点です

  • vim main.go
package main

import (
        "fmt"
        "github.com/gomodule/redigo/redis"
)

func main() {
        c, err := redis.Dial("tcp", "localhost:6379")
        if err != nil {
                fmt.Println(err)
                return
        }
        defer c.Close()
        fmt.Println(c.Do("rpush", "my_list", "a"))
        fmt.Println(c.Do("rpush", "my_list", "b"))
        fmt.Println(c.Do("lset", "my_list", 0, "A"))
        fmt.Println(c.Do("lset", "my_list", 1, "B"))
        myList, err := redis.Strings(c.Do("lrange", "my_list", 0, -1))
        if err != nil {
                fmt.Println(err)
                return
        }
        for i, v := range myList {
                fmt.Printf("%d -> %s\n", i, v)
        }
}

=> 0 -> A
=> 1 -> B

lset コマンドはキーで指定したリストが存在しない場合エラーになるのであらかじめ rpushlpush を使ってリストを作成しておきます
あとは range 関数を使ってスライスをループして値を取得すれば OK です

ハッシュ型

あとは代表的な型としてハッシュ型の使い方を紹介します
これは hgetall コマンドを使って取り出すことで golang 上では map として扱うことができます

  • vim main.go
package main

import (
        "fmt"
        "github.com/gomodule/redigo/redis"
)

func main() {
        c, err := redis.Dial("tcp", "localhost:6379")
        if err != nil {
                fmt.Println(err)
                return
        }
        defer c.Close()
        fmt.Println(c.Do("hset", "my_hash", "key1", "value1"))
        fmt.Println(c.Do("hset", "my_hash", "key2", "value2"))
        fmt.Println(redis.String(c.Do("hget", "my_hash", "key1")))
        myMap, err := redis.StringMap(c.Do("hgetall", "my_hash"))
        if err != nil {
                fmt.Println(err)
                return
        }
        for k, v := range myMap {
                fmt.Printf("%s -> %s\n", k, v)
        }
}

=> key1 -> value1
=> key2 -> value2

redis.StringMap を使ってコンバートすることで golang 上で map として扱うことができます
リストのときもそうでしたが要素は String になっているので数値を扱う場合にはキャストなどの型変換が必要です

ハッシュ型+構造体

最後に構造体を扱う方法を紹介します
ハッシュとして登録したデータを golang の構造体に marshall して golang 上で扱えるようにすることができます

  • vim main.go
package main

import (
        "fmt"
        "github.com/gomodule/redigo/redis"
)

type Profile struct {
        Name string `redis:"name"`
        Age  int    `redis:"age"`
}

func main() {
        c, err := redis.Dial("tcp", "localhost:6379")
        if err != nil {
                fmt.Println(err)
                return
        }
        defer c.Close()
        fmt.Println(c.Do("hset", "profile", "name", "hawksnowlog"))
        fmt.Println(c.Do("hset", "profile", "age", 99))
        pMap, err := redis.Values(c.Do("hgetall", "profile"))
        if err != nil {
                fmt.Println(err)
                return
        }
        p := Profile{}
        err = redis.ScanStruct(pMap, &p)
        if err != nil {
                fmt.Println(err)
                return
        }
        fmt.Println(p.Name)
        fmt.Println(p.Age)
}

=> hawksnowlog, 99

ハッシュにマッピングする構造体 (ここでは Profile) を作成しておきます
hgetall するところまでは同じです
データを取り出したあとで構造体にマッピングするための redis.ScanStruct を呼び出します
マッピング対象の構造体を引数の二番目に参照として渡します
あとは普通に構造体を扱うようにデータを参照できます

keys からのデータ取得

これもよくあるパターンかなと思います
あるプレフィックスで取得したキーに対してデータを取得する方法です

  • vim main.go
package main

import (
    "fmt"
    "github.com/gomodule/redigo/redis"
)

const prefix = "prefix::"

func main() {
    c, err := redis.Dial("tcp", "localhost:6379")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer c.Close()
    keys, err := redis.Strings(c.Do("keys", prefix+"*"))
    if err != nil {
        fmt.Println(err)
        return
    }
    m := make(map[string][]byte)
    for _, k := range keys {
        v, err := redis.Bytes(c.Do("get", k))
        if err != nil {
            fmt.Println(err)
            return
        }
        m[k] = v
    }
    for k, v := range m {
        fmt.Printf("%s -> %s\n", k, string(v))
    }
}

最後に

redigo の基本的な使い方を紹介しました
今回は Do 関数を使ってコマンドを実行しましたが Send 関数を使えばパイプライン実行することもできます
ポイントはコマンドの実行結果をちゃんと golang で扱えるようにしてあげる点かなと思います
Do 自体は interface{} を返すので interface の扱いに慣れている人は直接 Do 関数の返り値を扱っても OK だと思います
慣れていない場合には redis.xxx のコンバート用の関数を使うことをおすすめします

参考サイト

0 件のコメント:

コメントを投稿