Redigo issue 579
reply.go sliceHelper does not handle Redis errors within the slice#579
一、問題是什麼
在使用了 Envoy 代理 Redis 的場景下,執行MGET
獲取資料,其中某臺 Redis 服務出現了問題,Envoy返回瞭如下的錯誤提示upstream failure
而經過 redigo 返回來的卻是
redigo: unexpected element type for ByteSlices, got type redis.Error
顯然封裝的有問題,導致錯誤提示不夠直接。
二、怎麼解決的
詳見 pr 580
2.1 ByteSlices 與 MGET
我們看 issue 579 的示例程式碼,發生問題的呼叫就在這裡:
values, err := redis.ByteSlices(conn.Do("MGET", params...))
那麼問題根源出在哪裡呢?
這就需要看下 redis.ByteSlices()
都做了什麼。
我直接總結一下:遍歷MGET返回的所有資料,進行型別斷言,如果斷言失敗,返回錯誤資訊。
那麼問題就在於如下兩點:
- 當 Envoy 代理 Redis 後,在 redis 沒有響應或者其它情況下,Envoy 返回自定義的錯誤資訊。(Envoy文件-upstream failure錯誤)
- Redigo 粗暴的判斷,對返回的值進行型別斷言
v.([]byte)
, 切片斷言失敗,返回了自定義的錯誤提示,且錯誤提示中,沒有帶著 Envoy 返回的內容。
2.2 switch 與 v.(type)
if v.([]byte)
型別斷言出是否為位元組切片
改造為
switch v := v.(type)
、 case []byte:
、 case Error:
不再判斷單一的位元組切片型別,而是斷言出是哪種型別。
那 Error
是什麼型別呢,其實就是 String
,這種特性,我們管它叫做“型別定義” type Error string
再有 Envoy 代理 Redis 返回錯誤資訊 場景下返回的資料,型別斷言就會進入到case Error
三、復現這個問題
安裝 Envoy,代理 Redis,kill掉Redis,就可復現出“no upstream host”(雖不是upstream failure,但同樣是String)。
四、擴充套件
4.1 文中提到的 Envoy 是什麼?
Service Mesh 架構的實現框架
4.2 Envoy 和 Redis 怎麼結合的?
在 Service Mesh 架構下,Envoy 對 多 Redis 叢集進行代理。
4.3 Go
4.3.1 型別別名、與型別定義
文中說到的 Error 其實 就是 String,即型別定義,需要注意的是不要和型別別名的概念混淆。
// 將NewInt定義為int型別
type NewInt int
// 將int取一個別名叫IntAlias
type IntAlias = int
參考
Go語言type關鍵字(型別別名)
瞭解 Go 1.9 的型別別名
4.3.2 回撥函式
redis.ByteSlices() 以及其它fun中都運用了回撥函式