我已经阅读了整个Redigo文档,可以在这里找到。 https://godoc.org/github.com/garyburd/redigo/redis#pkg-variables
这里的文档明确指出连接不支持对Send(),Flush()或Receive()方法的并发调用。
Connections不支持对write方法的并发调用 (Send,Flush)或并发调用read方法(Receive)。 连接确实允许并发读写器。
然后它声明由于Do方法可以是Send(),Flush()和Receive()的组合,我们不能同时(使用)其他方法使用Do()。
因为Do方法结合了Send,Flush和 接收时,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()方法,而不是使用Send,Flush和Receive混合使用?
答案 0 :(得分:3)
您可以拥有一个并发编写器和一个并发阅读器。由于Do
结合了读写操作,因此您可以对Do
进行一次当前当前调用。换句话说,你不能同时打电话给Do
。您无法在全局变量中存储连接,并且在不使用互斥锁保护连接的情况下调用Do
,或使用其他某种机制来确保Do
只有一个并发调用者。
池支持并发访问。池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()
将执行。
同时添加对Subscribe或PSubscribe的调用。