ceph存储 PG的状态机 源码分析
文章目錄
- PG 的狀態機和peering過程
- 1. PG 狀態機變化的時機
- 2. pg的狀態演化過程
- 3. pg狀態變化實例講解
- 3.1 pg狀態的管理結構
- 3.2 數據的pg狀態變化過程
- 3.2.1 NULL -> initial
- 3.2.2 initial -> reset -> Started
- 3.2.3 Started(start) ->Started( primary(Peering(GetInfo)))
- 3.2.4 GetInfo->GetLog
- 3.2.5 GetLog->GetMissing
- 3.2.6 GetMissing->Active(Activating)
- 3.2.7 Active(Activating)-> WaitLocalRecoveryReserved->WaitRemoteRecoveryReserved-> Active(recovering)
- 3.2.8 recovering->recoverd->clean
PG 的狀態機和peering過程
首先來解釋下什么是pg peering過程?
當最初建立PG之后,那么需要同步這個pg上所有osd中的pg狀態。在這個同步狀態的過程叫做peering過程。同樣當啟動osd的時候,這個osd上所有的pg都要進行peering過程,同步pg的狀態。peering過程結束后進入pg的Active狀態,如果需要進行數據恢復則進行數據的恢復工作。
那么從PG的創建或者掃描開始,PG就開始設置了自己的初始狀態,到最后的完全同步,這期間使用一個叫做state_machine的機制進行標記和處理,然后加上事件機制進行通信。最后達到active+clean。
下面是一個pg的所有狀態,pg的狀態管理全部都交給一個叫做recoverymachine的成員來管理,pg的所有的狀態是一個類似樹形的結構,每個狀態可能存在子狀態,子狀態還可能存在子狀態如下圖
pg的狀態變化是一套狀態機,根據不同的狀態接收到不同的事件進行相互的轉化
1. PG 狀態機變化的時機
a.創建pg后,會經過一系列的PG的狀態變化,由Initial最后演化成Active+clean狀態。
b.就是osd啟動后,會進行PG掃描,掃描后會重新在本osd上建立PG,然后在經過一系列的pg狀態變化,最后達到clean狀態。
c.當其他osd啟動后,如果和本osd處于同一個PG內,會收到pg成員變化事件,處理該事件則本osd上的pg狀態也會重新被設置,再次經歷狀態變化,最后達到平衡的clean狀態
2. pg的狀態演化過程
下面經過一個pg狀態變化的過程說起,這個過程叫做PG的peering過程。peering的過程其實就是pg狀態從初始狀態然后到active+clean的變化過程。一個osd啟動之后,上面的pg開始工作,狀態為initial,這時進行比對所有osd上的pglog和pg_info,對pg的所有信息進行同步,選舉primary osd和replica osd,peering過程結束,然后把peering的結果交給recovering,有recovering過程進行數據的恢復工作(如下圖)。primary osd與replica osd經過一系列的狀態事件的交互,最后達到active狀態
3. pg狀態變化實例講解
3.1 pg狀態的管理結構
-
pg在創建或者掃描時都會重新的把pg的結構創建起來,上一節在pg創建的時候已經說了,由monitor 會發送事件給osd,osd會處理事件,并且完成pg的創建工作。這里和故障恢復有密不可分的關系,下一節講述數據的故障恢復。
-
創建pg的時候會初始化一個叫做recovery_state的成員,該成員就是用來控制pg的狀態變化和處理事件的機制。recover_state中存在一個成員叫做recovery_machie,該成員繼承了boost::statechart::state_machine狀態機,該狀態機就是用來處理狀態變化的。
pg與state_machine的關系如下圖
該途中表明了幾個數據結構之間的關系
3.2 數據的pg狀態變化過程
3.2.1 NULL -> initial
1). 來看一下申請PG的時候 對PG結構的初始化。在PG進行初始化的時候就對recoverystate(this)。
2075:recoverystate的構造函數。
2078:對于machine進行初始化。這時初始化machine的狀態為Initial狀態。
3.2.2 initial -> reset -> Started
2). 創建PG之后,就要對PG的發送一系列的事件了,首先是創建事件,調取函數handle_create()。在handle_create中主要發送了兩個事件
5758:handle_create()處理函數。
5761:申請一個 Initialize d的事件evt。
5762:本端的recoverystate處理該事件。handle_event中調用machine. process_event ()。交由狀態機進行處理。當狀態為Initial的machine遇見Initialize事件會轉化狀態為reset狀態。因為在initial狀態里定義了,如果收到了Initialize事件則將狀態轉化為reset
1567:定義了如果在Initial狀態的時候,收到了Initialize事件,則轉化為Reset狀態。
目前是已經為Reset的狀態了。帶著這個狀態再回到handle_create中
5763:這里定義了第二個事件 ActMap 的evt2.
5764:通過recoverystate.handle_event()再次交給machine。但是這時machine的狀態已經變成了Reset了,由Reset狀態開始處理Actmap事件。在reset的事件處理函數中boost::statechart::result PG::RecoveryState::Reset::react(const ActMap&),最后將狀態轉化為了Started。transit< Started >()。然后來看Started狀態,轉化成該狀態后又繼續處理狀態,在定義Started的時候默認設置了一個子狀態start
3.2.3 Started(start) ->Started( primary(Peering(GetInfo)))
3). 來看一下當進入start時是怎么來處理的
6010:開始處理進入start狀態后的處理。
6016:找到對應處理的PG。
6017:如果當前的osd在本pg里是 primary。
6020:向本狀態發送事件MakePrimary()事件。
6022:如果當前的osd在本pg里是replica。
6025:向本狀態發送事件MakeStray。
4). 接下來看看start狀態如何來處理MakePrimary和MakeStray的事件吧,下面來看。
1653:如果接受到MakePrimary事件,則將狀態start轉化為Primary狀態。
1654:如果接受到Makestray事件,則將狀態start轉化為stray狀態。
接下來按著Primary osd的流程走。在進入Primary狀態之后,在Primary狀態存在一個子狀態叫做Peering狀態。并且Peering也同樣存在一個子狀態叫做GetInfo。在GetInfo的函數PG::RecoveryState::GetInfo::GetInfo()中做了哪些事情。
7375:創建當前pg的OSD集合。為數據的恢復做準備。下一節數據恢復時會講述。
7379:發送請求到所有的副本osd中,請求pg_info信息,發送事件MQuery& query,query的類型是pg_query_t::INFO。然后發送的請求都會記錄在peer_info_request隊列中。
7381:如果想其他的osd發送的查詢pg_info事件,那一定會添加到peer_info_request隊列中,所以這里就不為空,然后就結束了。此時狀態就到這里,目前是GetInfo的狀態。等待replica osd獲取pg_info結束后,再將結果通過事件發送給primary osd。
3.2.4 GetInfo->GetLog
5). 當primary osd接到事件MNotifyRec& infoevt,然后對該事件展開處理。在GetInfo的狀態下處理該事件,在處理該事件的時候會對拉取的pginfo進行處理,最后如果所有的副本都成功的將信息返回了,則會本端再次發起事件post_event(GotInfo());當GetInfo狀態收到事件GotInfo(),則會轉換狀態為GetLog?,F在交給了GetLog狀態來繼續處理,在進入GetLog中,做了如下的操作
7621:這里要進行選擇acting。包括了選擇auth_osd、primary選擇,副本選擇(計算backfill osd,recover osd)。
7635:判斷auth是不是正在處理的本osd上,如果是自己的話,那自己本身就是最全的log,所以不需要拉取log。
7637:因為不用拉取其他osd上的log,所以這里直接發送Gotlog事件,說明不需要拉取log,可以進入下一個狀態了。
7673:由于自己本是auth osd,所以不是最全的log,所以要從其他osd上拉取log,這里準備事件g_query_t::LOG,發送到目標auth osd上拉取
3.2.5 GetLog->GetMissing
6). 本端使用handle_pg_query 處理g_query_t::LOG,將其封裝成為MOSDPGLog消息,該消息發送到目標auth osd后,有auth osd解封消息,并且構造PG::MLogRec事件,發送給auth_osd處理,在auth_osd上形成MQuery& query 交給pg的state_machine處理,處理該事件,pg->fulfill_log(),獲取本地log,然后通過消息MOSDPGLog發還給primary osd。這時primary 接到auth_osd發送過來的消息,并且消息攜帶auth_log的信息。在primary解析成為MLogRec 信息。這時primary osd的狀態為GetLog,開始處理MLogRec 事件。直接出發post_event(GotLog())事件,當GetLog接收到Gotlog事件的時候,先要合并proc_master_log(),然后轉換狀態為GetMissing狀態。
GetMissing的處理,在GetMissing中開始拉取所有副本的log信息,發送事件,等待所有的副本將自己的log和missing準備好發送給primary osd。這時primary osd處于GetMissing狀態處理MLogRec事件,處理時proc_replica_log(),合并副本的log
7974:當接受的log事件時候,將peer_missing_requested 隊列中對應osd的計數刪除。這個隊列方便統計哪些osd沒有及時的反饋消息。
7975:接收事件后,開始處理事件,proc_replica_log()合并副本的INFO,log,missing等信息。
7978:判斷是否需要更新up_thru。
7983:如果需要更新up_thru。則發送NeedupThru事件。
7989:如果不需要更新up_thru,則發送Activate()事件。
3.2.6 GetMissing->Active(Activating)
7). 假設這里發送了Activate()事件,GetMissing狀態會對Activate事件直接將狀態轉化為Active狀態,在進入Active后會調用PG::activate的處理。在Activete的處理中有兩件事兒.。a.準備事務的回調準備工作,申請注冊C_PG_ActivateCommitted。b.準備想其他的osd發送合并后的log,將權威的log封裝成MOSDPGLog,發送給副本,然后提交事務。
這時其他osd副本接收到MOSDPGLog事件,將解釋為本地的MLogRec,副本osd的pg狀態為stray,接收到MLogRec事件后。主要做的有三件事兒:a.合并接收到的log到本地log中,b.準備發送事件Activate,c.轉化到狀態ReplicaActive。最后由ReplicaActive狀態處理事件Activate。這時同樣會調用PG::activate()處理。這里主要準備了兩件事兒,
1.準備申請注冊事務的回調處理函數C_PG_ActivateCommitted
2.準備進行數據恢復時的各種狀態設置。完成后提交事務操作,等待事務完成。
目前,有兩件事兒需要說明一下,primary osd提交了事務等待C_PG_ActivateCommitted處理,replica osd同樣提交了事務等待C_PG_ActivateCommitted處理。接下來就來看看這個里邊做了哪些工作
primary osd處理完成后,提交事務回調進入_activate_committed中,
2109:如果這時判斷本osd是主osd。
2117:判斷是不是所有的osd都返回了提交了結果。
2119:如果是primary osd,并且所有的replica都提交了結果,則進行all_activated_and_committed()處理。
2125:如果不是primary osd,而是replica osd這時就要告訴primary osd,我已經處理好了,發送消息 MOSDPGInfo。
8). 當primary osd接收到MOSDPGInfo消息,解析為MInfoRec事件,這時primary osd的狀態為active,接收MInfoRec開始處理。
6998:當向一個osd發送MOSDPGLog后,會在對應osd的序號添加到peer_active的隊列中,當osd反饋消息MOSDPGInfo后,會將osd的序號添加到actingbackfill隊列中。這里判斷是不是所有的osd都處理完成了事務。
7000:如果所有的osd都完成了事務的處理,接下來進入all_activated_and_committed處理中。在all_activated_and_committed中主要是要發送事件通知PG的狀態,通知告知已經是AllReplicasActivated事件
3.2.7 Active(Activating)-> WaitLocalRecoveryReserved->WaitRemoteRecoveryReserved-> Active(recovering)
active接受到AllReplicasActivated事件后,開始處理,這其中主要調用pg-> on_activate ()函數。由于PG是由ReplicatedPG子類繼承的,所以這里調用ReplicatedPG::on_activate 進行處理。
9079:判斷是否需要進行recovery數據的恢復。
9082:如果需要進行數據恢復,則發送事件DoRecovery()事件。
9085:判斷是否需要進行backfill數據恢復。
9088:如果需要進行backfill數據恢復,則需要發送事件RequestBackfill()事件。
9091:即不需要recovery也不需要backfill等操作,則發送事件AllReplicasRecovered事件。
9).假設這里需要進行數據的恢復,這時發送了DoRecovery事件。activ狀態接受到事件DoRecovery后進入子狀態WaitLocalRecoveryReserved。該狀態是active的子狀態,仍讓處于active狀態中。在WaitLocalRecoveryReserved中會進行reserver處理并且發送LocalRecoveryReserved事件,WaitLocalRecoveryReserved接受到該事件后將轉化為WaitRemoteRecoveryReserved狀態。進入該狀態后發RemoteRecoveryReserved事件,處理該事件時再次發送事件AllRemotesReserved,狀態轉化為Recovering。
6651:進入recovering時要進行的處理。
6658:清除PG的state中的PG_STATE_RECOVERY_WAIT。
6659:設置PG的state中的 PG_STATE_RECOVERING。
6660:準備將pg交給osd的recovery線程處理,進行數據恢復,這里是先添加到recovery_wq,然后等待線程處理隊列中的pg數據恢復請求。
3.2.8 recovering->recoverd->clean
10). 該隊列是由osd->recovery_wq 來實現的,而不是OSDService-> recovery_wq。
該隊列的處理線程直接調用函數OSD::do_recovery()進行處理。在其中主要使用pg-> start_recovery_ops進行處理,在start_recovery_ops中判斷這個pg已經數據恢復完成的時候。
代碼能進行到這里說明已經PG的數據恢復完成。
9510:這時檢測狀態是不是正在進行數據恢復狀態是否為PG_STATE_RECOVERING。
9512:清除狀態PG_STATE_RECOVERING。
9513:判斷是不是要進行backfill處理?
9522:如果不需要進行backfill處理,這時代表所有的數據恢復都完成了,則發送事件AllReplicasRecovered。
11). 這時recovering狀態的pg接收到AllReplicasRecovered事件,則將pg的狀態轉化為Recovered狀態。這時將會再次發送GoClean()事件。PG接收到GoClean()事件,將轉化為clean狀態
最后的PG狀態就是Active+clean的狀態。clean是Active的一個子狀態。最終完成了PG的所有狀態變換
總結
以上是生活随笔為你收集整理的ceph存储 PG的状态机 源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多线程:pthread_cond_wai
- 下一篇: leetcode-24 两两交换链表中的