背景
什么是配置變更?
說白了,就是動態增減服務器。
假設,一個 Raft 服務器集群的數量剛開始是 3 臺,過了 2 個月,由于要搞類似 618,雙11 大促,領導要增加系統可用性,那么就要增加到 5 臺。
如果增加到 5 臺,就帶來了配置的變化,首當其沖的,就是 “服務器數量” 這個配置的變化,例如這個配置叫做 “server_count = 3”, 你需要將配置改成 “server_count = 5”。同時,每臺服務器還需要知道其他 4 臺服務器的 ip + port,這個配置實際上也是需要變化的。
并且,你需要在不停機的情況下,將新的 2 臺服務器增加,并且將這些配置應用到另外 3 臺舊的服務器上。
這個時候,你該怎么做?
假設是我,我會先將新的 2 臺服務器配置完畢(新的配置)再啟動。然后,將新的配置發送到舊的 3 臺服務器。那 3 臺服務器如果成功收到并提交了這個配置,使這個配置生效,那么整個集群的狀態就一致了。
很完美。
但是,分布式肯定沒有這么簡單。假設,在成功啟動 2 臺新服務器后,老的集群發生了故障,重新開始選舉,這個時候,是怎么樣子的?
看下圖:
上圖中,一共有 5 臺服務器,S1,S2,S3 代表舊的服務器,S4,S5 代表著新的服務器。
綠色代表舊的配置,藍色代表新的配置。
我們模擬一下:
- 我們先啟動了 S4 和 S5,成功啟動。
- 然后我們將新的配置發送到 S1,S2,S3 中,試圖讓他們應用新的配置。
- 在某個時刻,S3 成功應用新的配置,同時,舊的系統發生了故障,并開始選舉。
- 5 臺服務器一起開始選舉,由于 S1 和 S2 還沒有應用新的配置,所以,S1 和 S2 仍然以為只有 3 臺服務器,并且在得到 2 張選票后,成功選出一個領導者;
- 而 S3,S4,S5 應用了新的配置,并且獲得 3 臺服務器的認可,也成功選出了領導人。
- 此時,整個系統出現了 2 個領導人。
那么,是哪里出現了問題呢?
根本原因在于,在同一時刻,有 2 份配置生效了!!!
自然就可以選出 2 個領導人。
所以,要防止這個問題發生,必須不能讓 2 份配置同時生效。
Raft 的實現
Raft 是怎么做的呢?
Raft 使用了一種 2 階段提交的方案。
具體見下圖:
第一階段:
- 發送新配置到舊服務器的 leader。leader 不會直接存儲
新配置
,而是存儲舊配置 + 新配置
。同時,一旦這個配置被提交(成功同步到新舊集群的大部分跟隨者中),那么,所有服務器必須用這個配置來做決定。也就是說,舊配置失效了,不能拿舊配置做任何決定,同時,此時的系統狀態是一致的。這個狀態稱之為共同一致。
第二階段:
- 當
舊配置 + 新配置
被成功提交,這個時候,leader 會創建一條新配置
復制到 followers 中。從而完成配置變更。
從上圖和上文的解釋可以看出,這里不會出現新配置
和 舊配置
同時出現的場景。因為他們都被融合在了 舊配置 + 新配置
中去了。即將兩個配置融合成一個配置。
那么,圖 1 的問題——同時有 2 個 leader 的問題就解決了。
思考:共同一致能確保沒有問題嗎?
簡化起見,我將 新配置 + 舊配置
稱之為 共同配置
。
為了閱讀方便,我將 舊配置
稱之為 old 配置
,將新配置
稱之為 new 配置
從圖 2 可以看出,一共有 2 個關鍵的節點:
- 提交 “共同配置”
- 提交“new 配置”
如果這個兩個地方出現問題了,怎么辦?
假設: 共同配置
提交失敗,會怎么樣?例如提交的時候,老的 leader 崩潰了。
這要分 2 種情況來看:
1.新 leader 已經收到 “共同配置”,那么新 leader 將繼續進行配置變更操作。
- 新 leader 沒有收到 “共同配置”,自然使用 old 配置。
假設:new 配置提交失敗,會怎么樣?例如提交的時候,leader 崩潰了。
這要看 new 配置有沒有同步到大部分節點。
- 假設已經同步到大部分節點,則集群使用“新配置” —— 完成配置變更。
- 假設沒有同步到大部分節點,則集群使用 “共同配置”,繼續進行配置變更。
另一個問題:共同一致階段,如果 leader 崩潰,使用什么策略進行選舉?
答:如果 共同配置 被應用了,那么,由于領導人完全特性(如果某條日志在某個任期號中已經被提交,那那個條目必然出現在更大任期號的所有領導人中
),新的 leader 必然擁有 共同配置。
如下圖:
最后,Raft 論文提出的 3 個問題
1. 新的服務器在初始化時,沒有存儲任何日志條目。
這個帶來的問題是:由于沒有存儲任何日志條目,那么,就需要時間來補充日志條目,這實際上,是會影響可用性的。
Raft 使用了一種方法來避免:這個階段的服務器是沒有投票權力的。只有當器補充完日志條目了,才會加入集群。
2. 集群的領導人可能不是新配置的一員
什么意思?
答: 當舊集群的 leader 提交了新配置,Leader 需要變成 follower。
為什么?
答:當新的配置生效時,舊 leader 還使用的老的配置,例如新配置的服務器數量是 5 臺,而老的 leader 仍然應用的是 3 臺服務器。
新集群應該使用新的配置重新進行選舉(不然使用新配置干嘛?)。
3. 移除不在新配置
中的服務器可能會擾亂集群
當移除他們時,他們會重新進行選舉,導致當前 leader 回退到 follower 狀態。雖然新的 leader 會被選出來,但被移除的服務器會再次請求重新選舉,循環反復,影響可用性。
Raft 的解決方式:
- 當節點確認當前領導人存在時,則忽略請求投票的 RPC 請求。
- 當節點在最小選舉超時時間里收到請求投票請求,他不會更新當前的任期號或者投出選票(從而擾亂集群)。