首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >关于Redigo与并发的几个问题

关于Redigo与并发的几个问题
EN

Stack Overflow用户
提问于 2016-05-12 06:29:35
回答 1查看 2.7K关注 0票数 3

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

在这里,文档明确指出,连接不支持发送()、刷新()或接收()方法的并发调用。

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

然后它指出,由于Do方法可以是Send()、刷新()和接收()的组合,所以不能与其他方法同时使用Do()。

因为Do方法结合了发送、刷新和接收的功能,所以不能与其他方法并发调用Do方法。

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

例如:

代码语言:javascript
复制
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()方法,如下所示:

代码语言:javascript
复制
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,我们需要创建一个池,并从池中获取连接,并在完成时释放它们。

这是否意味着只要从池中获得连接,我就可以随意使用、发送()、刷新()和接收()?换句话说,每次我需要在一个go例程中做一些事情时,我必须从池中获得一个新连接,而不是重用一个全局连接?这是否意味着只要我从池中获得一个新的连接,就可以使用Do()方法,例如Send()?

因此,总结一下:

1)只要不与发送、刷新和接收方法一起使用Do()方法,就可以同时使用do ()方法吗?

( 2)只要我从池中得到一个新的连接,并在我完成后释放它,我就能使用我想要的一切吗?

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-05-12 12:40:58

您可以有一个并发作者和一个并发读取器。因为Do结合了读写操作,所以您可以对Do进行当前的一个当前调用。换句话说,您不能同时调用Do。如果不使用互斥保护连接或使用其他机制确保Do不存在多个并发调用方,则无法将连接存储在全局变量中并调用Do

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

用池替换redisConn redis.Conn。在应用程序启动时初始化池:

代码语言:javascript
复制
 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
    },
}

使用池发布到通道:

代码语言:javascript
复制
 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的调用。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37178897

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档