• go-redis之初始化连接


    go-redis之初始化连接

    安装

    如果您使用的是Redis 6,请安装 go-redis/ v8

    go get github.com/go-redis/redis/v8
    
    • 1

    如果您使用的是Redis 7,请安装 go-redis/ v9(目前处于测试阶段):

    go get github.com/go-redis/redis/v9
    
    • 1

    连接到 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
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    另一种流行的方法是使用连接字符串:

    opt, err := redis.ParseURL("redis://<user>:<pass>@localhost:6379/<db>")
    if err != nil {
    	panic(err)
    }
    
    rdb := redis.NewClient(opt)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用 TLS

    要启用 TLS/SSL,您需要提供一个空的tls.Config. 如果您使用的是私有证书,则需要指定在新窗口中打开他们在tls.Config.

    rdb := redis.NewClient(&redis.Options{
    	TLSConfig: &tls.Config{
    		MinVersion: tls.VersionTLS12,
    		//Certificates: []tls.Certificate{cert}
    	},
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果您得到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",
    	},
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    通过 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,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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
    
    • 1
    • 2
    • 3

    如果您使用 Docker、Istio 或任何其他服务网格/sidecar,请确保应用程序在容器完全可用后启动,例如,通过配置健康检查在新窗口中打开使用 Docker 和holdApplicationUntilProxyStartsIstio。

    Context

    每个 Redis 命令都接受一个上下文,您可以使用它来设置超时或传播一些信息,例如跟踪上下文

    ctx := context.Background()
    
    • 1

    执行命令

    执行命令:

    val, err := rdb.Get(ctx, "key").Result()
    fmt.Println(val)
    
    • 1
    • 2

    或者,您可以保存命令并稍后分别访问值和错误:

    get := rdb.Get(ctx, "key")
    fmt.Println(get.Val(), get.Err())
    
    • 1
    • 2

    执行不支持的命令

    要执行任意/自定义命令:

    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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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)
    
    • 1
    • 2
    • 3

    助手的完整列表:

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    GET例如,不是唯一返回 nil 回复的命令,BLPOPZSCORE可以返回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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    迭代分片:

    err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
        return shard.Ping(ctx).Err()
    })
    if err != nil {
        panic(err)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    要迭代主节点,请使用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)
        },
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    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"},
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从 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,
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    redis环

    介绍

    Ring 是一个 Redis 客户端,它使用一致的哈希在多个 Redis 服务器(分片)之间分配密钥。多个 goroutine 并发使用是安全的。

    Ring 监控每个分片的状态并从环中删除死分片。当一个分片上线时,它会被添加回环中。这实现了最大的可用性和分区容错性,但不同分片甚至客户端之间没有一致性。每个客户端使用客户端可用的分片,并且在分片状态更改时不与其他客户端进行任何协调。

    当您需要多个 Redis 服务器进行缓存并且可以容忍其中一个服务器死机时丢失数据时,应该使用 Ring。否则你应该使用Redis ClusterRedis 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",
        },
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    然后客户端可以像往常一样使用:

    if err := rdb.Set(ctx, "foo", "bar", 0).Err(); err != nil {
        panic(err)
    }
    
    • 1
    • 2
    • 3

    迭代分片:

    err := rdb.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
        return shard.Ping(ctx).Err()
    })
    if err != nil {
        panic(err)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    每个分片选项

    要更改分片连接选项:

    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)
        },
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    密钥分配

    默认情况下,Ring 使用Rendezvous在新窗口中打开散列在多个分片上分配密钥。但是您可以更改默认的一致哈希实现:

    import "github.com/golang/groupcache/consistenthash"
    
    ring := redis.NewRing(&redis.RingOptions{
        NewConsistentHash: func() {
            return consistenthash.New(100, crc32.ChecksumIEEE)
        },
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Redis 通用客户端

    UniversalClient是一个抽象客户端,基于提供的选项,代表 a ClusterClient、 aFailoverClient或 single-node Client。这对于在本地测试特定于集群的应用程序或在不同环境中具有不同客户端非常有用。

    NewUniversalClient返回一个新的多客户端。返回客户端的类型取决于以下条件:

    1. 如果指定了该选项,则返回MasterName哨兵支持。FailoverClient
    2. 如果数量Addrs为 2 或更多,ClusterClient则返回 a。
    3. 否则,返回单节点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",
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    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())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    或者,您可以在函数退出时使用Pipelinedwhich 调用: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())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    管道还返回执行的命令,因此可以遍历它们以检索结果:

    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())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Transactions 和Watch

    使用 Redis事务在新窗口中打开,您可以观察键的变化并仅在观察的键没有被另一个客户端更改时才执行管道。这种冲突解决方法也称为乐观锁定

    WATCH mykey
    
    val = GET mykey
    val = val + 1
    
    MULTI
    SET mykey $val
    EXEC
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    相反,您应该使用Watch处理事务管道在新窗口中打开,例如,我们可以正确实现INCR在新窗口中打开使用GET,SETWATCH的命令 请注意我们如何使用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")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    Redis 发布订阅

    go-redis 允许发布消息和订阅频道。当出现网络错误时,它还会自动重新连接到 Redis Server。

    要发布消息:

    err := rdb.Publish(ctx, "mychannel1", "payload").Err()
    if err != nil {
    	panic(err)
    }
    
    • 1
    • 2
    • 3
    • 4

    订阅频道:

    // 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()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    要接收消息:

    for {
    	msg, err := pubsub.ReceiveMessage(ctx)
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(msg.Channel, msg.Payload)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    但最简单的方法是使用与订阅一起关闭的 Go 频道:

    ch := pubsub.Channel()
    
    for msg := range ch {
    	fmt.Println(msg.Channel, msg.Payload)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    【linux操作系统】基础指令(入门必备)
    毕业设计- 基于单片机与GPS+GSM的车辆定位跟踪系统
    python安装davisinteractive模块
    深度优先搜索(dfs)--矩阵部分-leetcode以及常见题
    Ajax--初识Ajax--接口和案例 - 图书管理
    JavaScript教程第三篇(作者原创)
    [Spring Boot]09 Spring Boot集成和使用Redis
    java计算机毕业设计html5大众汽车网站MyBatis+系统+LW文档+源码+调试部署
    算法 比较版本号-(同向双指针)
    Qt开发 入门
  • 原文地址:https://blog.csdn.net/qq_53267860/article/details/125604904