這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Head of head
在golang的整個生態裡,redis client lib全部都使用多串連或者串連池。這是讓人難以理解的,所以我和xiaofei一起寫了一個同時支援同步和非同步redis client lib:RedisGo-Async。
github地址:https://github.com/gistao/RedisGo-Async。
qq群:131958277。
同步模式
A -> B
A <- B
A請求,並擷取結果,經曆1個RTT,這裡稱之為同步模式。
為了實現高QPS,需要M個AB來處理任務,這樣可以得到一個公式:M/RTT。
非同步模式
A -> -> -> B
A <- <- <- B
A請求,不等待應答繼續請求,並擷取全部結果,經曆1個RTT,這裡稱之為非同步模式。
為了實現高QPS,同樣可以得到一個公式:C,C表示發包頻率。
複雜度
非同步模式一般使用回調,較同步方式複雜的多。但GoRedis-Async提供的兩種模式的使用是一樣的。
conn := pool.Get()
ret, err := conn.Do()
doSomething(ret)
當你想在兩種模式下切換時,這些代碼都不用更改。
值得注意的是pipelining,同步模式的使用如下
conn.Send(A)
conn.Send(B)
conn.Flush()
conn.Recive() // ret <-A
conn.Recive() // ret <-B
而非同步模式是天然支援pipelining的,所以使用還是
conn.Do(A)
conn.Do(B)
有時你會希望同時向Redis serverA,B,C請求,最後一次性來處理結果。同步模式的使用如下
connA.Send()
connA.Flush()
connB.Send()
connB.Flush()
connC.Send()
connC.Flush()
connA.Recive()
connB.Recive()
connC.Recive()
非同步模式使用如下
connB.AsyncDo()
retC := connC.AsyncDo()
retA.Get()
retC.Get()
retB.Get()
擴充性
同步模式:M/RTT
在GoRedis-Async裡,M表示
Pool.MaxActive
這個值是固定的,取決於當前測試環境:
- 應用程式和Redis server的網路RTT
- 應用程式裡的並發度
- Redis server的串連數上限
- 原生串連數上限
- 原生CPU核心數。
舉個關於RTT的例子:
測試環境的網路RTT為1ms,應用程式經測試達到QPS要求後,M被確定到代碼中,然後上線到生產環境,而生產環境的RTT為10ms,那麼生產環境的QPS就會比預期要低10倍。或者在生產環境裡,由於目標Redis server發生故障而被切到了備機,此時備機和應用程式極有可能會跨機房,這也會帶來同樣的問題。
非同步模式:C
在GoRedis-Async裡,C只和應用程式的routine數量有關,上邊所述的RTT問題在非同步模式下並不存在。
當然,在特定範圍內都可以被應用程式接受的話,同步和非同步模式選擇哪種都是適合的。
MM(Min cost Max payload)
基礎庫好壞的一個重要衡量標準就是MM。
同步模式從理論上來說,相比較非同步模式需要更多的線程和串連資源。下邊是RedisGo-Async、redigo、官方redisclient的對比基準測試報告。Y軸是耗時ms,X軸為各對比庫,測試資料為1千萬條。