2016-05-12 110 views
2

我已閱讀了可在此處找到的整個Redigo文檔。 https://godoc.org/github.com/garyburd/redigo/redis#pkg-variables有關Redigo和併發性的一些問題

這裏的文檔明確指出連接不支持對Send(),Flush()或Receive()方法的併發調用。

連接不支持併發調用寫入方法 (發送,刷新)或併發調用讀取方法(接收)。 連接允許併發讀寫器。

然後它聲明由於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) 
     } 
    } 

} 

,然後調用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,我們需要創建一個池並從池中獲取連接,並在完成後釋放它們。

這是否意味着我可以使用,發送(),同花順()和接收(),因爲我想,只要我從池中的連接?換句話說,每次我需要在去例行程序中執行某些操作時,我必須從池中獲取新連接,而不是重新使用全局連接?這是否意味着只要我從池中獲得一個新的連接,就可以將Do()方法用於例如Send()?

所以總結起來:

1)我可以同時使用DO()方法,只要我不發送使用它,同花順和接收方法呢?

2)我可以使用一切,我想只要我從池中獲取一個新的連接,並釋放它,當我做了什麼?

3)如(1)爲真,這是否會影響性能?在我提供的例子中,只使用Do()方法併發使用全局連接,而不是將Sending,Flush和Receive混合起來更好嗎?

回答

2

您可以有一個並行寫入器和一個併發讀取器。由於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()將在應用程序中的其他代碼使用該池之前執行。

還可以撥打電話SubscribePSubscribe

+0

我同時運行redisInit(),像「go redisInit()」,我有兩個連接。一個redisConn redis.Conn執行發佈命令一個redisPsc redis.PubSubConn來訂閱和取消訂閱。 – Alex

+0

查看更新的答案。 –

+0

我目前沒有使用池。但是,如果我理解正確,我不能使用Do()來發布內容,因爲當我發佈內容時,我也同時收到相同的命令? – Alex