Пакет redis (github.com/gomodule/redigo/redis) - это клиент для базы данных Redis.
Подключения
Интерфейс Conn - это основной интерфейс для работы с Redis. Приложения создают соединения, вызывая функции Dial, DialWithTimeout или NewConn. В будущем будут добавлены функции для создания сегментированных и других типов подключений.
Приложение должно вызвать метод Close соединения, когда приложение завершит соединение.
Выполнение команд
Интерфейс Conn имеет общий метод для выполнения команд Redis:
Do(commandName string, args ...interface{}) (reply interface{}, err error)
В справочнике по командам Redis (http://redis.io/commands) перечислены доступные команды. Пример использования команды Redis APPEND:
n, err := conn.Do("APPEND", "key", "value")
Метод Do преобразует аргументы команды в bulk строки для передачи на сервер следующим образом:
Go тип Преобразование
[]byte Отправляется как есть
string Отправляется как есть
int, int64 strconv.FormatInt(v)
float64 strconv.FormatFloat(v, 'g', -1, 64)
bool true -> "1", false -> "0"
nil ""
все остальные типы fmt.Fprint(w, v)
Типы ответов команды Redis представлены с использованием следующих типов Go:
Redis тип Go тип
error redis.Error
integer int64
simple string string
bulk string []byte или nil если значение отсутствует.
array []interface{} или nil если значение отсутствует.
Используйте утверждения типа или вспомогательные функции ответа для преобразования из interface{} в конкретный тип Go для результата команды.
Конвейерная обработка
Соединения поддерживают конвейерную обработку с использованием методов Send, Flush и Receive.
Send(commandName string, args ...interface{}) error
Flush() error
Receive() (reply interface{}, err error)
Send записывает команду в выходной буфер соединения. Flush сбрасывает выходной буфер соединения на сервер. Receive читает один ответ от сервера. В следующем примере показан простой конвейер.
c.Send("SET", "foo", "bar")
c.Send("GET", "foo")
c.Flush()
c.Receive() // ответ от SET
v, err = c.Receive() // ответ от GET
Метод Do сочетает в себе функциональность методов Send, Flush и Receive. Метод Do начинается с записи команды и очистки выходного буфера. Затем метод Do получает все ожидающие ответы, включая ответ на команду, только что отправленную Do. Если какой-либо из полученных ответов является ошибкой, то Do возвращает ошибку. Если ошибок нет, Do возвращает последний ответ. Если аргумент команды для метода Do - "", то метод Do очистит выходной буфер и получит ожидающие ответы без отправки команды.
Используйте методы Send и Do для реализации конвейерных транзакций.
c.Send("MULTI")
c.Send("INCR", "foo")
c.Send("INCR", "bar")
r, err := c.Do("EXEC")
fmt.Println(r) // печатает [1, 1]
Конкурентность
Соединения поддерживают одного конкурентного вызывающего объекта метода Receive и одного конкурентного вызывающего объекта методов Send и Flush. Никакой другой конкурентности не поддерживается, включая конкурентные вызовы методов Do и Close.
Для полного одновременного доступа к Redis используйте потокобезопасный пул для получения, использования и освобождения соединения из горутины. Подключения, возвращаемые из пула, имеют ограничения конкурентности, описанные в предыдущем абзаце.
Опубликовать и подписаться
Используйте методы Send, Flush и Receive для реализации подписчиков Pub/Sub.
c.Send("SUBSCRIBE", "example")
c.Flush()
for {
reply, err := c.Receive()
if err != nil {
return err
}
// обрабатываем отправленное сообщение
}
Тип PubSubConn оборачивает Conn удобными методами для реализации подписчиков. Методы Subscribe, PSubscribe, Unsubscribe и PUnsubscribe отправляют и сбрасывают команду управления подпиской. Метод получения преобразует отправленное сообщение в удобные типы для использования в переключателе типа.
psc := redis.PubSubConn{Conn: c}
psc.Subscribe("example")
for {
switch v := psc.Receive().(type) {
case redis.Message:
fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
return v
}
}
Помощники ответа
Функции Bool, Int, Bytes, String, Strings и Values преобразуют ответ в значение определенного типа. Чтобы обеспечить удобную упаковку вызовов методов соединения Do и Receive, функции принимают второй аргумент типа error. Если ошибка не равна nil, то вспомогательная функция возвращает ошибку. Если ошибка равна nil, функция преобразует ответ в указанный тип:
exists, err := redis.Bool(c.Do("EXISTS", "foo"))
if err != nil {
// обрабатываем возврат ошибки из c.Do
// или ошибку преобразования типа.
}
Функция Scan преобразует элементы ответа массива в типы Go:
var value1 int
var value2 string
reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
if err != nil {
// обрабатываем ошибки
}
if _, err := redis.Scan(reply, &value1, &value2); err != nil {
// обрабатываем ошибки
}
Ошибки
Методы подключения возвращают с сервера ответы об ошибках в виде типа redis.Error.
Вызовите метод соединения Err(), чтобы определить, возникла ли в соединении неисправимая ошибка, например сетевая ошибка или ошибка анализа протокола. Если Err() возвращает значение, отличное от nil, соединение непригодно для использования и должно быть закрыто.
Пример (Zpop)
В этом примере реализуется ZPOP, как описано на http://redis.io/topics/transactions, с использованием WATCH/MULTI/EXEC и сценариев.
package main
import (
"fmt"
"github.com/gomodule/redigo/redis"
)
// zpop извлекает значение из ключа ZSET
// с помощью команд WATCH/MULTI/EXEC.
func zpop(c redis.Conn, key string) (result string, err error) {
defer func() {
// Вернуть соединение в нормальное состояние
// при ошибке.
if err != nil {
c.Do("DISCARD") // nolint: errcheck
}
}()
// Цикл, пока транзакция не будет успешной.
for {
if _, err := c.Do("WATCH", key); err != nil {
return "", err
}
members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0))
if err != nil {
return "", err
}
if len(members) != 1 {
return "", redis.ErrNil
}
if err = c.Send("MULTI"); err != nil {
return "", err
}
if err = c.Send("ZREM", key, members[0]); err != nil {
return "", err
}
queued, err := c.Do("EXEC")
if err != nil {
return "", err
}
if queued != nil {
result = members[0]
break
}
}
return result, nil
}
// zpopScript извлекает значение из ZSET.
var zpopScript = redis.NewScript(1, `
local r = redis.call('ZRANGE', KEYS[1], 0, 0)
if r ~= nil then
r = r[1]
redis.call('ZREM', KEYS[1], r)
end
return r
`)
// В этом примере реализуется ZPOP, как описано в
// http://redis.io/topics/transactions
// с использованием WATCH/MULTI/EXEC и сценариев.
func main() {
c, err := dial()
if err != nil {
fmt.Println(err)
return
}
defer c.Close()
// Добавляем тестовые данные с помощью конвейера.
for i, member := range []string{"red", "blue", "green"} {
if err = c.Send("ZADD", "zset", i, member); err != nil {
fmt.Println(err)
return
}
}
if _, err := c.Do(""); err != nil {
fmt.Println(err)
return
}
// Извлечь с помощью WATCH/MULTI/EXEC
v, err := zpop(c, "zset")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(v)
// Извлечь с помощью скрипта.
v, err = redis.String(zpopScript.Do(c, "zset"))
if err != nil {
fmt.Println(err)
return
}
fmt.Println(v)
}
Вывод:
red
blue
Читайте также:
- Работа с MongoDB в Golang
- Пакет bson для работы с MongoDB в Golang
- Работа с базами данных в Golang. Пакет database/sql в Golang
Комментариев нет:
Отправить комментарий