我已经阅读了整个Redigo文档,可以在这里找到。https://godoc.org/github.com/garyburd/redigo/redis#pkg-variables
在这里,文档明确指出,连接不支持发送()、刷新()或接收()方法的并发调用。
连接不支持对写方法(发送、刷新)或对读方法的并发调用(接收)的并发调用。连接确实允许并发的读取器和作者。
然后它指出,由于Do方法可以是Send()、刷新()和接收()的组合,所以不能与其他方法同时使用Do()。
因为Do方法结合了发送、刷新和接收的功能,所以不能与其他方法并发调用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,我们需要创建一个池,并从池中获取连接,并在完成时释放它们。
这是否意味着只要从池中获得连接,我就可以随意使用、发送()、刷新()和接收()?换句话说,每次我需要在一个go例程中做一些事情时,我必须从池中获得一个新连接,而不是重用一个全局连接?这是否意味着只要我从池中获得一个新的连接,就可以使用Do()方法,例如Send()?
因此,总结一下:
1)只要不与发送、刷新和接收方法一起使用Do()方法,就可以同时使用do ()方法吗?
( 2)只要我从池中得到一个新的连接,并在我完成后释放它,我就能使用我想要的一切吗?
3)如果(1)是真的,这是否会影响业绩?与我提供的示例中的Do()方法同时使用全局连接,而不是将其与发送、刷新和接收混为一谈,这样更好吗?
发布于 2016-05-12 12:40:58
您可以有一个并发作者和一个并发读取器。因为Do结合了读写操作,所以您可以对Do进行当前的一个当前调用。换句话说,您不能同时调用Do。如果不使用互斥保护连接或使用其他机制确保Do不存在多个并发调用方,则无法将连接存储在全局变量中并调用Do。
池支持并发访问。池Get方法返回的连接遵循上述并发规则。要获得对数据库的完全并发访问,应用程序应该在单个goroutine中执行以下操作:从池中Get连接;在连接上执行Redis命令;将连接返回到池中的底层资源。
用池替换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的调用。
https://stackoverflow.com/questions/37178897
复制相似问题