当前位置:网站首页>go-redis之初始化连接
go-redis之初始化连接
2022-07-06 08:59:00 【~庞贝】
目录
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)
}
边栏推荐
- Intel distiller Toolkit - Quantitative implementation 1
- 使用latex导出IEEE文献格式
- LeetCode:剑指 Offer 42. 连续子数组的最大和
- 【文本生成】论文合集推荐丨 斯坦福研究者引入时间控制方法 长文本生成更流畅
- Tdengine biweekly selection of community issues | phase III
- Mongodb installation and basic operation
- 【嵌入式】使用JLINK RTT打印log
- LeetCode:剑指 Offer 03. 数组中重复的数字
- Leetcode: Sword finger offer 42 Maximum sum of continuous subarrays
- What is the role of automated testing frameworks? Shanghai professional third-party software testing company Amway
猜你喜欢
Intel Distiller工具包-量化实现1
LeetCode:124. 二叉树中的最大路径和
LeetCode:236. The nearest common ancestor of binary tree
Advanced Computer Network Review(5)——COPE
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
Advanced Computer Network Review(4)——Congestion Control of MPTCP
vb. Net changes with the window, scales the size of the control and maintains its relative position
Different data-driven code executes the same test scenario
BN折叠及其量化
什么是MySQL?MySql的学习之路是怎样的
随机推荐
LeetCode:214. Shortest palindrome string
Notes 01
Leetcode: Sword finger offer 42 Maximum sum of continuous subarrays
Post training quantification of bminf
Unsupported operation exception
LeetCode:162. Looking for peak
requests的深入刨析及封装调用
Deep anatomy of C language -- C language keywords
BMINF的後訓練量化實現
Li Kou daily question 1 (2)
Excellent software testers have these abilities
Intel distiller Toolkit - Quantitative implementation 1
Ijcai2022 collection of papers (continuously updated)
In depth analysis and encapsulation call of requests
力扣每日一题(二)
Leetcode: Sword Finger offer 42. Somme maximale des sous - tableaux consécutifs
What is an R-value reference and what is the difference between it and an l-value?
不同的数据驱动代码执行相同的测试场景
LeetCode:221. Largest Square
使用latex导出IEEE文献格式