go-redis之初始化连接
如果您使用的是Redis 6,请安装 go-redis/ v8:如果您使用的是Redis 7,请安装 go-redis/ v9(目前处于测试阶段):连接到 Redis 服务器要连接到 Redis 服务器:另一种流行的方法是使用连接字符串:使用 TLS要启用 TLS/SSL,您需要提供一个空的. 如果您使用的是私有证书,则需要指定在新窗口中打开他们在.如果您得到,请尝试设置选项:通过 SSH通过 S
目录
go-redis之初始化连接
安装
如果您使用的是Redis 6,请安装 go-redis/ v8:
go get github.com/go-redis/redis/v8
如果您使用的是Redis 7,请安装 go-redis/ v9(目前处于测试阶段):
go get github.com/go-redis/redis/v9
连接到 Redis 服务器
要连接到 Redis 服务器:
import "github.com/go-redis/redis/v8"
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
另一种流行的方法是使用连接字符串:
opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
if err != nil {
panic(err)
}
rdb := redis.NewClient(opt)
使用 TLS
要启用 TLS/SSL,您需要提供一个空的tls.Config
. 如果您使用的是私有证书,则需要指定在新窗口中打开他们在tls.Config
.
rdb := redis.NewClient(&redis.Options{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
//Certificates: []tls.Certificate{cert}
},
})
如果您得到x509: cannot validate certificate for xxx.xxx.xxx.xxx because it doesn't contain any IP SANs
,请尝试设置ServerName
选项:
rdb := redis.NewClient(&redis.Options{
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
ServerName: "your.domain.com",
},
})
通过 SSH
通过 SSH 通道连接:
sshConfig := &ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{ssh.Password("password")},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: 15 * time.Second,
}
sshClient, err := ssh.Dial("tcp", "remoteIP:22", sshConfig)
if err != nil {
panic(err)
}
rdb := redis.NewClient(&redis.Options{
Addr: net.JoinHostPort("127.0.0.1", "6379"),
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
return sshClient.Dial(network, addr)
},
// Disable timeouts, because SSH does not support deadlines.
ReadTimeout: -1,
WriteTimeout: -1,
})
dial tcp: i/o timeout
当 go-redis 无法连接到 Redis 服务器时,您会收到dial tcp: i/o timeout
错误消息,例如,当服务器关闭或端口受防火墙保护时。要检查 Redis 服务器是否正在侦听端口,请在运行 go-redis 客户端的主机上运行 telnet 命令:
telnet localhost 6379
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection refused
如果您使用 Docker、Istio 或任何其他服务网格/sidecar,请确保应用程序在容器完全可用后启动,例如,通过配置健康检查在新窗口中打开使用 Docker 和holdApplicationUntilProxyStarts
Istio。
Context
每个 Redis 命令都接受一个上下文,您可以使用它来设置超时或传播一些信息,例如跟踪上下文。
ctx := context.Background()
执行命令
执行命令:
val, err := rdb.Get(ctx, "key").Result()
fmt.Println(val)
或者,您可以保存命令并稍后分别访问值和错误:
get := rdb.Get(ctx, "key")
fmt.Println(get.Val(), get.Err())
执行不支持的命令
要执行任意/自定义命令:
val, err := rdb.Do(ctx, "get", "key").Result()
if err != nil {
if err == redis.Nil {
fmt.Println("key does not exists")
return
}
panic(err)
}
fmt.Println(val.(string))
Do
返回一个Cmd在新窗口中打开有一堆助手可以处理interface{}
价值:
// Text is a shortcut for get.Val().(string) with proper error handling.
val, err := rdb.Do(ctx, "get", "key").Text()
fmt.Println(val, err)
助手的完整列表:
s, err := cmd.Text()
flag, err := cmd.Bool()
num, err := cmd.Int()
num, err := cmd.Int64()
num, err := cmd.Uint64()
num, err := cmd.Float32()
num, err := cmd.Float64()
ss, err := cmd.StringSlice()
ns, err := cmd.Int64Slice()
ns, err := cmd.Uint64Slice()
fs, err := cmd.Float32Slice()
fs, err := cmd.Float64Slice()
bs, err := cmd.BoolSlice()
redis.Nil
go-redis 导出redis.Nil
错误并在 Redis 服务器响应时返回它(nil)
。您可以使用 redis-cli 检查 Redis 返回的响应。
在下面的示例中,我们使用redis.Nil
来区分空字符串回复和 nil 回复(key 不存在):
val, err := rdb.Get(ctx, "key").Result()
switch {
case err == redis.Nil:
fmt.Println("key does not exist")
case err != nil:
fmt.Println("Get failed", err)
case val == "":
fmt.Println("value is empty")
}
GET
例如,不是唯一返回 nil 回复的命令,BLPOP
也ZSCORE
可以返回redis.Nil
.
Conn
Conn 表示单个 Redis 连接,而不是连接池。除非特别需要连续的单个 Redis 连接,否则首选从客户端运行命令。
cn := rdb.Conn(ctx)
defer cn.Close()
if err := cn.ClientSetName(ctx, "myclient").Err(); err != nil {
panic(err)
}
name, err := cn.ClientGetName(ctx).Result()
if err != nil {
panic(err)
}
fmt.Println("client name", name)
redis集群
go-redis 自带Redis Cluster客户端在新窗口中打开. 下面,redis.ClusterClient
用于redis.Client
与集群中的每个节点进行通信。每个都redis.Client
维护一个单独的连接池。
要连接到 Redis 集群:
import "github.com/go-redis/redis/v8"
rdb := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
// To route commands by latency or randomly, enable one of the following.
//RouteByLatency: true,
//RouteRandomly: true,
})
迭代分片:
err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
return shard.Ping(ctx).Err()
})
if err != nil {
panic(err)
}
要迭代主节点,请使用ForEachMaster
. 要遍历从节点,请使用ForEachSlave
.
要更改某些分片的选项:
rdb := redis.NewClusterClient(&redis.ClusterOptions{
NewClient: func(opt *redis.Options) *redis.NewClient {
user, pass := userPassForAddr(opt.Addr)
opt.Username = user
opt.Password = pass
return redis.NewClient(opt)
},
})
redis哨兵
Redis 服务器客户端
连接到由Redis Sentinel管理的 Redis 服务器在新窗口中打开:
import "github.com/go-redis/redis/v8"
rdb := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: "master-name",
SentinelAddrs: []string{":9126", ":9127", ":9128"},
})
从 v8 开始,您可以使用实验NewFailoverClusterClient
将只读命令路由到从节点:
import "github.com/go-redis/redis/v8"
rdb := redis.NewFailoverClusterClient(&redis.FailoverOptions{
MasterName: "master-name",
SentinelAddrs: []string{":9126", ":9127", ":9128"},
// To route commands by latency or randomly, enable one of the following.
//RouteByLatency: true,
//RouteRandomly: true,
})
Redis 哨兵客户端
要连接到 Redis Sentinel 本身:
import "github.com/go-redis/redis/v8"
sentinel := redis.NewSentinelClient(&redis.Options{
Addr: ":9126",
})
addr, err := sentinel.GetMasterAddrByName(ctx, "master-name").Result()
redis环
介绍
Ring 是一个 Redis 客户端,它使用一致的哈希在多个 Redis 服务器(分片)之间分配密钥。多个 goroutine 并发使用是安全的。
Ring 监控每个分片的状态并从环中删除死分片。当一个分片上线时,它会被添加回环中。这实现了最大的可用性和分区容错性,但不同分片甚至客户端之间没有一致性。每个客户端使用客户端可用的分片,并且在分片状态更改时不与其他客户端进行任何协调。
当您需要多个 Redis 服务器进行缓存并且可以容忍其中一个服务器死机时丢失数据时,应该使用 Ring。否则你应该使用Redis Cluster或Redis Server。
快速开始
创建一个包含 3 个分片的 Ring 集群:
import "github.com/go-redis/redis/v8"
rdb := redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"shard1": ":7000",
"shard2": ":7001",
"shard3": ":7002",
},
})
然后客户端可以像往常一样使用:
if err := rdb.Set(ctx, "foo", "bar", 0).Err(); err != nil {
panic(err)
}
迭代分片:
err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
return shard.Ping(ctx).Err()
})
if err != nil {
panic(err)
}
每个分片选项
要更改分片连接选项:
rdb := redis.NewRing(&redis.RingOptions{
NewClient: func(opt *redis.Options) *redis.NewClient {
user, pass := userPassForAddr(opt.Addr)
opt.Username = user
opt.Password = pass
return redis.NewClient(opt)
},
})
密钥分配
默认情况下,Ring 使用Rendezvous在新窗口中打开散列在多个分片上分配密钥。但是您可以更改默认的一致哈希实现:
import "github.com/golang/groupcache/consistenthash"
ring := redis.NewRing(&redis.RingOptions{
NewConsistentHash: func() {
return consistenthash.New(100, crc32.ChecksumIEEE)
},
})
Redis 通用客户端
UniversalClient
是一个抽象客户端,基于提供的选项,代表 a ClusterClient
、 aFailoverClient
或 single-node Client
。这对于在本地测试特定于集群的应用程序或在不同环境中具有不同客户端非常有用。
NewUniversalClient
返回一个新的多客户端。返回客户端的类型取决于以下条件:
- 如果指定了该选项,则返回
MasterName
哨兵支持。FailoverClient
- 如果数量
Addrs
为 2 或更多,ClusterClient
则返回 a。 - 否则,返回单节点
Client
。
// rdb is *redis.Client.
rdb := NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{":6379"},
})
// rdb is *redis.ClusterClient.
rdb := NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{":6379", ":6380"},
})
// rdb is *redis.FailoverClient.
rdb := NewUniversalClient(&redis.UniversalOptions{
Addrs: []string{":6379"},
MasterName: "mymaster",
})
Golang Redis 管道、WATCH 和事务
使用管道加速 Redis
Redis 管道允许通过使用单个客户端-服务器-客户端往返执行多个命令来提高性能。您可以将命令排在管道中,然后像单个命令一样使用单个写入 + 读取操作执行排队的命令,而不是一个一个地执行 100 个命令。
要使用单个写入 + 读取操作执行多个命令:
pipe := rdb.Pipeline()
incr := pipe.Incr(ctx, "pipeline_counter")
pipe.Expire(ctx, "pipeline_counter", time.Hour)
cmds, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
// The value is available only after Exec is called.
fmt.Println(incr.Val())
或者,您可以在函数退出时使用Pipelined
which 调用:Exec
var incr *redis.IntCmd
cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
incr = pipe.Incr(ctx, "pipelined_counter")
pipe.Expire(ctx, "pipelined_counter", time.Hour)
return nil
})
if err != nil {
panic(err)
}
// The value is available only after the pipeline is executed.
fmt.Println(incr.Val())
管道还返回执行的命令,因此可以遍历它们以检索结果:
cmds, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
for i := 0; i < 100; i++ {
pipe.Get(ctx, fmt.Sprintf("key%d", i))
}
return nil
})
if err != nil {
panic(err)
}
for _, cmd := range cmds {
fmt.Println(cmd.(*redis.StringCmd).Val())
}
Transactions 和Watch
使用 Redis事务在新窗口中打开,您可以观察键的变化并仅在观察的键没有被另一个客户端更改时才执行管道。这种冲突解决方法也称为乐观锁定
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
TxPipelined
您可以使用and包装带有 MULTI 和 EXEC 命令的管道TxPipeline
,但它本身并不是很有用:
cmds, err := rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
for i := 0; i < 100; i++ {
pipe.Get(ctx, fmt.Sprintf("key%d", i))
}
return nil
})
if err != nil {
panic(err)
}
// MULTI
// GET key0
// GET key1
// ...
// GET key99
// EXEC
相反,您应该使用Watch处理事务管道在新窗口中打开,例如,我们可以正确实现INCR在新窗口中打开使用GET
,SET
和WATCH
的命令 请注意我们如何使用redis.TxFailedErr
来检查事务是否失败。
// Redis transactions use optimistic locking.
const maxRetries = 1000
// Increment transactionally increments the key using GET and SET commands.
func increment(key string) error {
// Transactional function.
txf := func(tx *redis.Tx) error {
// Get the current value or zero.
n, err := tx.Get(ctx, key).Int()
if err != nil && err != redis.Nil {
return err
}
// Actual operation (local in optimistic lock).
n++
// Operation is commited only if the watched keys remain unchanged.
_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
pipe.Set(ctx, key, n, 0)
return nil
})
return err
}
// Retry if the key has been changed.
for i := 0; i < maxRetries; i++ {
err := rdb.Watch(ctx, txf, key)
if err == nil {
// Success.
return nil
}
if err == redis.TxFailedErr {
// Optimistic lock lost. Retry.
continue
}
// Return any other error.
return err
}
return errors.New("increment reached maximum number of retries")
}
Redis 发布订阅
go-redis 允许发布消息和订阅频道。当出现网络错误时,它还会自动重新连接到 Redis Server。
要发布消息:
err := rdb.Publish(ctx, "mychannel1", "payload").Err()
if err != nil {
panic(err)
}
订阅频道:
// There is no error because go-redis automatically reconnects on error.
pubsub := rdb.Subscribe(ctx, "mychannel1")
// Close the subscription when we are done.
defer pubsub.Close()
要接收消息:
for {
msg, err := pubsub.ReceiveMessage(ctx)
if err != nil {
panic(err)
}
fmt.Println(msg.Channel, msg.Payload)
}
但最简单的方法是使用与订阅一起关闭的 Go 频道:
ch := pubsub.Channel()
for msg := range ch {
fmt.Println(msg.Channel, msg.Payload)
}
更多推荐
所有评论(0)