為什麼 RESP3 將成為 Redis 6 唯一支援的協定
[編輯!我正在重新思考這一切,因為來自 Stack Overflow 的 Marc Gravell(馬克·格拉維爾)建議,我們可以針對每個連線分別切換協定以維持向下相容,只要傳送一個指令來啟用 RESP3。這表示不再需要透過全域設定來切換伺服器的行為。這樣的方式對我來說可接受得多,我也正在重新思考這篇部落格文章的核心觀點]
在 Redis 5 發布幾週後,我開始著手實作 RESP3,經過幾天的工作,看到這件事終於發生,感覺非常好。RESP3 是 Redis 從 Redis 6 開始將使用的全新用戶端-伺服器協定。位於 https://github.com/antirez/resp3 的規格文件應該能清楚說明,這個舊協定 RESP2 的演進版本將如何改善 Redis 生態系。但最重要的在於,RESP3 比 RESP2 更具「語意性」。舉例來說,它具備映射(map)、集合(set,無序的元素清單)、回應資料的屬性(attribute)等概念,可用輔助資訊來豐富回應內容,諸如此類。最終目標是讓新的 Redis 用戶端需要做的工作更少,也就是只需決定一組固定的規則,就能將每一種 RESP3 回應型別轉換為用戶端函式庫所使用程式語言中對應的適當型別。
在 Redis 的未來中,我預見用戶端在底層會變得更聰明,盡力處理好連線、管線化(pipelining)與狀態管理,而在面向使用者的那一端,則明顯變得更加簡潔,理想上的 Redis 用戶端甚至會像這樣:
result = redis.call(“GET”,keyname);
當然,在此之上你還可以建構更進階的抽象層,但最底層應該就是這個樣子,而且回傳的回應不應該需要針對特定指令做特設的過濾:RESP3 的回傳型別應該包含足夠的資訊,以回傳適當的資料型別。因此,HGETALL 將回傳 RESP3 的「map」,而 LRANGE 將回傳「array」,EXISTS 則將回傳 RESP3 的「boolean」。
這也讓新指令即使在用戶端函式庫並未*特別*為其設計處理邏輯的情況下,仍能如預期般運作。相反地,在 RESP2 中常見的情況是,指令可能透過類似「method missing」這類機制而得以運作,但之後當該指令在用戶端函式庫中*真正*被實作時,回傳的型別卻改變了,進而引入了微妙的不相容性。
然而,雖然新協定是對舊協定的漸進式改進,它仍會在用戶端函式庫端(當然)以及*應用程式層*引進無法避免的不相容性。舉例來說,ZSCORE 現在將回傳 double,而非字串,因此應用程式碼應該要更新,或者,用戶端函式庫也可以實作一個相容性選項,將 RESP3 回應轉回原本的 RESP2 型別。
Lua 腳本若未針對新協定進行修改,也將無法再正常運作,因為 Lua 同樣會透過 redis.call() 指令看到更多具語意性的回傳型別。同樣地,Lua 也將能夠回傳 RESP3 中實作的所有新資料型別。
正因為這些原因,大家對我的決定感到擔憂:我打算讓 Redis 6 僅支援 RESP3。將不會提供任何相容模式讓 Redis 6 伺服器切換回 RESP2,因此你要嘛升級用戶端函式庫並更新你的應用程式(或使用用戶端函式庫的向下相容模式),要嘛就無法切換到 Redis 6。
我這麼做有充分的理由,我想說明為何會做出這個決定,以及我如何為使用者與用戶端函式庫作者減輕相關問題。先從緩解措施談起:
- Redis 5 在 Redis 6 發布後仍將獲得完整支援,為期 2 年。所有關鍵的修正都會回移植到 Redis 5,並持續提供修補層級的版本更新。
- Redis 6 預計在約 1 至 1.5 年後發布。然而,Redis 6 將在約 1 個月後就切換至 RESP3。因此,人們將會在很長一段時間內使用、實驗並接觸這個採用新協定的不穩定版 Redis。鑑於與許多其他軟體不同,Redis 的不穩定版擁有大量非正式的使用者,這既是因為它是 Github 上的預設分支,也因為傳統上 Redis 的不穩定版其實從未真正那麼不穩定,這將帶來大量的前期曝光。
- 我對此還不是百分之百確定,但 Lua 腳本引擎可能會提供一個相容模式,以回傳與 Redis 5 相同的型別。然而,該相容性預設不會啟用,而是針對每個執行的腳本個別啟用,方式是在呼叫 Redis 指令前先呼叫特殊的 redis.resp2_compat() 函式。因此,每一台 Redis 6 伺服器的行為都將保持一致,不受其設定影響,一如過去 10 年來 Redis 一貫的做法。
以上是緩解措施。而以下則是我不會讓 Redis 6 同時支援兩個版本的原因:
- 這或多或少完全沒有意義。如果人們把 Redis 6 切換到 RESP2 模式,他們仍然停留在過去,只是在等待 Redis 7 推出時不再支援 RESP2 並導致一切中斷。同時,當你面對一個 Redis 6 實例時,你永遠無法確定*它會回應什麼*,取決於它的設定方式。因此,同一個用戶端函式庫對同一個指令可能會回傳 Hash 或 Array。
- 在沒有充分理由的情況下(見「1」),這會帶來更多的工作量與複雜度。許多指令將需要針對舊協定進行檢查,以判斷該以何種格式回應。
- 透過將新的 Redis 6 功能與協定變更綁在一起,我們給了使用者充分的理由去進行切換,並移植他們的用戶端與應用程式。到了某個時間點,一切都會結束,我們就能專注於新的事物。否則,我們將會有一批為了新功能而切換到新伺服器、卻仍使用舊協定的 Redis 6 使用者,而到了 Redis 7 又會重演同樣的難題。
- 如果有人告訴你,調整用戶端函式庫是一項可怕的工作,那麼,我不敢苟同。是的,確實有些更動要做,但現在我正在實作伺服器端,我發現這並沒有那麼糟糕。真正糟糕的是,大多數用戶端的工作完全沒有報酬,僅僅出於熱情與樂於與他人分享的意願而發生。我敢說,我們很快就會看到許多 RESP3 的實作。
- RESP3 的設計讓用戶端可以自動偵測目前是 RESP2 還是 RESP3,並自動切換,因此新的用戶端將同時適用於 Redis <= 5 與 Redis 6。
大概就是這樣了。希望這能闡明我的觀點與背後的原因,同時,在協定切換期間將啟用的緩解措施,也能讓使用者相信,這不會是一次非常「劇烈」的中斷。
隨機一篇部落格