3

我已经阅读了可以在此处找到的整个 Redigo 文档。 https://godoc.org/github.com/garyburd/redigo/redis#pkg-variables

这里的文档明确指出连接不支持并发调用 Send()、Flush() 或 Receive() 方法。

连接不支持并发调用写入方法(发送、刷新)或并发调用读取方法(接收)。连接确实允许并发读取器和写入器。

然后它指出,由于 Do 方法可以是 Send()、Flush() 和 Receive() 的组合,我们不能同时使用 Do()(与)其他方法。

由于 Do 方法结合了 Send、Flush 和 Receive 的功能,因此 Do 方法不能与其他方法同时调用。

这是否意味着我们可以使用存储在全局变量中的单个连接单独同时使用 Do(),只要我们不将它与其他方法混合使用?

例如像这样:

var (

    // Redis Conn.
    redisConn redis.Conn

    // Redis PubSubConn wraps a Conn with convenience methods for subscribers.
    redisPsc redis.PubSubConn
)

func redisInit() {

    c, err := redis.Dial(config.RedisProtocol, config.RedisAddress)
    if err != nil {
        log.Fatal(err)
    }
    c.Do("AUTH", config.RedisPass)
    redisConn = c

    c, err = redis.Dial(config.RedisProtocol, config.RedisAddress)
    if err != nil {
        log.Fatal(err)
    }
    c.Do("AUTH", config.RedisPass)
    redisPsc = redis.PubSubConn{c}

    for {
        switch v := redisPsc.Receive().(type) {
        case redis.Message:
            // fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
            socketHub.broadcast <- v.Data
        case redis.Subscription:
            // fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
        case error:
            log.Println(v)
        }
    }

}

然后在一些 go 例程中调用 Do() 方法,如下所示:

if _, err = redisConn.Do("PUBLISH", fmt.Sprintf("user:%d", fromId), message); err != nil {
    log.Println(err)
}
if _, err = redisConn.Do("PUBLISH", fmt.Sprintf("user:%d", toId), message); err != nil {
    log.Println(err)
}

后来文档说,为了对 Redis 进行完全并发访问,我们需要创建一个池并从池中获取连接,并在完成后释放它们。

这是否意味着我可以根据需要使用 Send()、Flush() 和 Receive(),只要我从池中获得连接?所以换句话说,每次我需要在 go 例程中做某事时,我都必须从池中获取新连接,而不是重用全局连接?这是否意味着只要我从池中获得新连接,我就可以将 Do() 方法与例如 Send() 一起使用?

所以总结一下:

1) 只要不将 Do() 方法与 Send、Flush 和 Receive 方法一起使用,我可以同时使用它吗?

2) 只要​​我从池中获得新的连接并在完成后释放它,我就可以使用我想要的一切吗?

3) 如果 (1) 为真,这会影响性能吗?在我提供的示例中,仅使用 Do() 方法同时使用全局连接,而不是与发送、刷新和接收混为一谈,是否更好?

4

1 回答 1

4

您可以有一个并发写入器和一个并发读取器。因为Do结合了读取和写入操作,您可以对Do. 换句话说,您不能Do同时调用。您不能将连接存储在全局变量中并在Do不使用互斥锁保护连接或使用其他机制来确保不超过一个并发调用者的情况下调用Do.

池支持并发访问。pool 方法返回的连接Get遵循上述并发规则。为了获得对数据库的完全并发访问,应用程序应该在单个 goroutine 中执行以下操作: Get来自池的连接;在连接上执行 Redis 命令; Close将基础资源返回到池的连接。

换成redisConn redis.Conn水池。在应用启动时初始化池:

 var redisPool *redis.Pool

 ...

redisPool = &redis.Pool{
    MaxIdle: 3,  // adjust to your needs
    IdleTimeout: 240 * time.Second,  // adjust to your needs
    Dial: func () (redis.Conn, error) {
        c, err := redis.Dial(config.RedisProtocol, config.RedisAddress)
        if err != nil {
            return nil, err
        }
        if _, err := c.Do("AUTH", config.RedisPass); err != nil {
            c.Close()
            return nil, err
        }
        return c, err
    },
}

使用池发布到频道:

 c := redisPool.Get()
 if _, err = c.Do("PUBLISH", fmt.Sprintf("user:%d", fromId), message); err != nil {
    log.Println(err)
 }
 if _, err = c.Do("PUBLISH", fmt.Sprintf("user:%d", toId), message); err != nil {
    log.Println(err)
 }
 c.Close()

不要在redisInit(). 不能保证redisInit()会在应用程序中的其他代码使用池之前执行。

还要添加对订阅PSubscribe的调用。

于 2016-05-12T12:40:58.590 回答