Elasticsearch 5.x 源碼分析(11)Shard Allocation 和Cluster Reroute

這幾天剛實操了一把shard 的relocation, 在cerebro 上看著圖標移來晃去覺得很爽,但之前并沒深究,也沒讀過這部分的代碼。前幾晚夜間上線需要重啟所有的ES機器,大家也對ES的relocation 討論了一番,這次就順便讀讀這部分的代碼。

這里不再討論shard 在分配的一些算法,和一些基礎概念,如果對分片如何分到一個節點的算法有興趣,或者對一些概念還不熟悉的話,建議可以看下面文章,或者我之前的文章:

Elasticsearch 5.x 源碼分析(7)Shard Allocation 的一些小細節
elasticsearch源碼分析之Allocation模塊

這次打算用一個例子來貫穿大部分的allocation的入口,如下圖:


先假設我們有三個節點,并創建了一個index,2分片2replica。0p 和1r 坐落在Node1 上,1p 和0r 坐落在Node2, 現在我們先假設Node1 Node2都shutdown了,現在這4個分片都是UNassigned狀態。

1. 重啟,初始化分配

這里先撇開集群把shard 進行飄移的問題,簡單看一下shard是怎么 assign 的,node1 和node2 啟動之前,在集群里面,分片0 和分片1 都會出于 UNassigned 狀態;當Node1啟動完,它會主動去ping Master節點,在前面的文章都知道了,這時會完整data node的注冊的所有步驟,同時Master會下發最新的clusterState 下來。然后在Node1 內部會觸發ClusterStateChangeEvent,這個事件會觸發多個modules或者Service去處理邏輯,這里我們關心的邏輯在 IndicesClusterStateService 里。


當處理到檢測分配到自己的shard是否存在時,Node1 會檢測這個shard的狀態,如沒有,則會去創建一個shard,有的話則會檢測是否需要更新狀態


由于Node1 剛剛啟動,它還沒有去加載 0p 和1r,因此會進入createShard()流程,并在后面跑去加載它本機的shard,這部分的邏輯在createShard() 里面的indicesService.createShard()中


最后Node1 啟動完后會把0p 和1r 標記成 initializing 并上報給Master,這樣Master就知道這個shard已經被assigned 了并標記成initialzing 了。
這里介紹的是一個正向的例子,那么一些反向的例子,假如,在Node1 啟動的時候,其實過了很久了,0p早就已經分配到其他機器了,那么Master 發過來的 ClusterState 中的0p 就已經是assigned狀態了,那么這段邏輯就不是在 createOrUpdateShards(state); 處理了,而是之前的removeUnallocatedIndices(state)failMissingShards(state)removeShards(state)來處理,大家有興趣可以分別過過這兩個代碼。

現在回到這個正向的例子,稍等片刻之后,Node1 已經完全初始化完0p 和1r 了,那么node1 就會把這兩個 shard 置成started狀態,等下一個時間間隔的ClusterState 心跳過來時,顯而易見就會進入updateShard(nodes, shardRouting, shard, routingTable, state);


在這里Node1 就會主動發送一個Action 給master 來請求 做startedShard這個Action(最后一行)。
ShardStateAction 里,有兩種請求需要處理,分別是

  • ShardStartedClusterStateTaskExecutor
  • ShardFailedClusterStateTaskExecutor

分別處理啟用或者mark fail,其中我們看看ShardStartedClusterStateTaskExecutor的邏輯


從代碼看出,這里最終要的一個地方就是,并不是 node上報什么狀態,Master就會不分青紅皂白一味記錄,它需要通過allocationService.applyStartedShards()來自己去校驗和消費這個狀態,最后則生成一個新的ClusterState,這會體現在下一次的心跳下發。

ShardFailedClusterStateTaskExecutor則是處理Node上報的failShard的事情,這種情況我覺得比較少見

經過這一輪后,Node1 上的所有shards都恢復正常,那么Node2 也按這個流程,最后Node1 Node2 啟動完后就是下圖

2. Node1 掛了,Master標記0p,1r

剛剛上面說到,ShardFailedClusterStateTaskExecutor 這種主動上報一般是比較少見的,我們見更多一般是Node1 重啟或者進程掛掉,從

Elasticsearch 5.x 源碼分析(4)走讀發現協議ZenDiscovery

我們曾經提過,ZenDiscovery用于處理一切的Node事件變更,那我們就找找這段代碼

繼續跟下去,這次我們只關心和shard allocation 相關的代碼,處理Node fail 的話會執行一個NodeRemovalClusterStateTaskExecutor.Task在這里我們找到了我們想找到的東西


這里就會把這臺機的所有shard進行 failShard()處理,也就是會mark UNassigned 等等。那么在下一次的ClusterState 同步事件時其他所有節點就會知道了

3. Cluster Reroute

有時候運維需要,比如我們這次需要全部升級ES,那么往往我們需要手工的去鎖住禁止集群進行 allocation,rebalance 等,甚至我們在加機器之后我們希望是手工自己去分配分片,那么就要用到cluster Reroute。相對應的Action是RestClusterRerouteActionTransportClusterRerouteAction,而最后會是由 allocationService.reroute()來承載,在這個例子里,加入我手工吧0p從Node1 移動到Node3 去,看會發生什么事情。


上面代碼最重要就是那句

RoutingExplanations explanations = commands.execute(allocation, explain);

MoveAllocationCommand中會對將要Reroute的操作進行一系列的判斷,比如canAllocate()


仔細留意的話,這里的targetRelocatingShard 其實是一個PeerRecoverySource的shard,這是就會把這個shard標記成INITIALIZING 狀態。

那這個狀態就會在ClusterState 中并下次心跳同步到目標節點去。也就是說Node3 在下次獲取ClusterState時,就會得到新的ShardRouting 請求了。
這時Node3 還是回到 第1章中的方法里,只是這時Node3 并沒有0r這個Shard,并且,Node3就會開始嘗試從Node1 處去回復這個Shard了

后話

我寫這篇文章的初衷是,我當時很好奇,Node3 是如何恢復0p的呢,因為0p好歹是個Primary 呀,并且這時,Node1 會繼續服務嗎?
繼續服務這個是肯定的,至于Node3是如何恢復的,我的一個猜測是首先Node3會像恢復一個replica 那樣把Node1的分片拷貝過去,接著,在這段時間的增量數據將會繼續同步translog 的方式同步到Node3,到最后,直到同步到一個最新的點,這時Node1 和Node3的分片都是同步的,然后就直接做個切換,0p就成功切換到Node3去了,關于Recovery這塊我沒有再繼續讀下去了,知道的朋友歡迎也順便告知我一下。

由于Allocation這部分代碼非常復雜,因此我也沒有完全弄懂,有些點也是我自己的推測而成,如有錯誤歡迎指正和討論。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容