ceph的數據存儲之路(5) -----osd數據處理

osd的數據處理

當osd接管了來自rbd client發送的請求,在osd上進行解析,通過一系列的處理,最後保存到了osd的磁盤上。安全

 

上一節中講述了rbdclient端是如何處理請求數據的,下面開始從osd接受數據開始進行講述。服務器

 

1、client如何與osd進行創建通訊的呢?session

1. osd是運行在服務器上的進程,進程的主體在ceph_osd.cc中的main函數開始,而且申請一些用於服務的線程。好比這裏用於接客戶端受鏈接的線程Accepter。若是配對鏈接成功後,會把鏈接成功的socket交給SimpleMessager來處理。app

  

2. 在ceph_osd.cc 中main函數中 會申請一個Messager 消息管理的類,該類是一個基類,最終會選擇SimpleMessager類實例,SimpleMessager類中會有一個叫作Accepter的成員,會申請該對象,而且初始化。socket

而後在main中把它綁定一個固定的ip,這個ip根據配置文件來獲取。如0577行函數

 

3.這個ip最終會綁定在Accepter中。而後在Accepter->bind函數中,會對這個ip初始化一個socket,而且保存爲listen_sd。接着會啓動Accepter->start(),這裏會啓動Accepter的監聽線程,這個線程作的事情放在Accepter->entry()函數中。接下來看看這個函數中實現了什麼。spa

0216:開始一個循環,線程不能結束。知道中止這個線程時。線程

0218:poll監聽一個端口,等待客戶端來進行鏈接。日誌

0219:若是有客戶端來鏈接時,若是來接成功則進行下面的處理對象

0222:msgr 是具體實現類爲SimpleMessager,跟蹤到實現的函數中去。

0343:當接受到一個鏈接時,這時須要創建一個數據通道pipe,。

0344:告知pipe 處理socket爲sd。也就是從這個sd讀取數據。

0346:啓動pipe的讀線程,從sd中讀取message信息。

0349:將pipe加入到SimpleMessager中的accepting_pipes,便於管理。

 

2、消息的傳遞

4. 接下來已經創建了pipe的通道,立刻就要開始進行message的接受, 目前接收message的工做交給了pipe的reader線程。接下來看看pipe->reader()作了什麼事情。

pipe->reader()中會先 read_message(),將傳遞的消息解析出來,而後把這個消息放入in_q->fast_dispatch(m)中處理,繼續發送到OSD::ms_fast_dispatch中處理,在ms_fast_dispatch中會將message 轉化爲OpRequestRef op,後續直接對這個op進行處理。繼續通過dispatch_session_waiting()、dispatch_op_fast()、handle_op()。handle_op()中開始將這個處理由OSD轉化爲PG的處理。

8502:根據message的信息能夠得到pg_id。

8503:再根據pg_id能夠恢復出pool的id。也就是pool的序號。

8508:根據固然獲取到的osdmap從新修正這個pg_id。

8512:根據pg_id信息建立 pg的信息結構spg_t pgid。

 

3、消息的處理

5.上面已經根據message中的信息恢復出pg和pool的相關信息。這些信息對於osd組織數據有很大的幫助。接下來開始要交給PG進行處理了。

8568:根據pg_id在osd->pg_map中查找pg。這裏要說明下PG是一個基類,由ReplicatedPG類實現。這裏的PG實際上使用的就是繼承類的方法。

8573:找到PG之後,這時將這個op交給PG進行繼續處理。已經從OSD處理的message轉化爲了PG處理的op。由這裏開始,最終添加到OSDService->op_wq隊列,該隊列是由OSD->ShardedOpWQ隊列初始化,其實是保存到了OSD-> 的ShardedOpWQ  ,而後保存在結構ShardData中等待被處理,而後喚醒處理這個隊列的線程,線程處理函數OSD::ShardedOpWQ::_process調用到OSD::dequeue_op()、pg->do_request()、ReplicatedPG::do_request()、ReplicatedPG::do_op()。接下來看看do_op中是如何處理的。

 

1809:這裏要建立一個OpContext結構,該結構會接管message中的全部ops的操做,ops的操做就是客戶端將rbd請求拆分紅object的請求。

 

4、造成事務Transaction

1810:獲取一個transaction,由於pgbackend類被ReplicatedBackend類實現,因此get_transaction的實現由ReplicatedBackend::get_transaction() 完成,申請一個叫作RPGTransaction 而後交給了ctx->op_t。這裏想要說明的是,申請了一個事物transaction,下面就要處理這個Transaction須要與咱們的op組合了吧,在一個transaction中完成這些ops的操做,看看他們是如何關聯的execute_ctx(),。

 

7. 首先使用int result = prepare_transaction(ctx); 他會將 ops上要寫入的數據所有都按着結構保存在transaction的data_bl,op_bl中。拿着這個transaction 就已經得到了所有的操做和數據,這也就是將ops與Transaction綁定的結果。由於primary osd不只僅是要本身保存數據,其餘的replica osd也要保存數據的副本,這裏有primary osd將準備好的數據發送給replica osd。

 

5、osd完成統計處理repop

2523:申請repop,這個repop是用來管理髮送給其餘副本以及本身進行數據處理的統計,根據這個結構可知那些osd都完成了數據的讀寫操做,回調比不可少的。

2528:issue_repop()將操做率先發給其餘副本,這裏其實就是與其餘副本通訊,將ops發送給其餘副本,其餘副本操做完成時可以及時的回調找到repop,而後開始處理,在issue_repop中規定了全部osd完成時的on_all_commit與on_all_applied的操做,而且將提交Transaction,使用函數submit_transaction()。這裏pgbackend->submit_transaction()函數中,pgbackend對象已經被ReplicatedBackend進行了繼承,因此最終使用的是ReplicatedBackend::submit_transaction()。

2530:若是數據處理完成了,使用eval_repop()進行收尾的工做,將結果回調給客戶端。

 

8. 接下來繼續查看函數void ReplicatedBackend::submit_transaction()中的實現方式

0590:開始準備一個處理操做的結構InProgressOp,而後將申請的on_all_commit、on_all_applied等回調操做進行統計。

0594:開始統計全部須要applied的副本操做數量,等待副本操做完成回調時進行清除,使用該結構方便統計是否是全部的副本都完成了操做。

0598:做用同0594相同,區別在於統計commit的副本操做數量。

0620:issue_op將ops的信息封裝成message發送給replica osd副本的。這個操做就是在封裝message,這裏就再也不多說了。

0619:開始記錄本端操做object的log。這個log對於數據的恢復存在相當重要的決定。

0624:開始統計本端的sync回調,主要用於快照和克隆等等的數據同步。

0625:開始註冊本端的applied回調函數,這裏回調後會直接向上回調all_applied()

0627:開始註冊本端刪除object的回調操做函數(這個暫時不用考慮)。

0629:開始註冊本端的commit回調函數,這裏回調後直接向上回調all_commited()。

0631:本端開始真正的處理請求。這個parent指的就是ReplicaPG -> queue_transaction,而後在進行最後osd->store->queue_transaction ,這裏的store是ObjectStore 幾經展轉最後調用到繼承類FileStore::queue_transactions()開始處理。

 

6、文件與日誌的處理

9.從這裏開始的 接下來就所有的交給了FileStore來處理了。

2071:在進行存儲數據的時候 確定是須要記錄journal的,也就是當數據進行寫入的時候須要寫到journal中一份,當data數據失敗的時候能夠從journal中進行恢復。因此這裏爲了安全起見,通常都會採用journal的方式進行保存數據。

2082:若是選擇的文件系統(如brfs)時,這種文件系統存在checkpoint機制時,能夠採用的方法,能夠並行的處理日誌與data的寫入操做。

2084:準備寫入日誌的操做_op_journal_transactions。

2086:準備寫入數據的操做queue_op。

2088:當選擇這條路時,這種文件系統不存在checkpoint機制,因此必須先寫入日誌,等待日誌完成後才能夠寫入data。

2091:這時申請一個C_JournaledAhead的回調操做,這個操做會在日誌完成以後進行回調處理處理時會將data寫入磁盤。_op_journal_transactions()這裏開始激發寫入r日誌的操做。

在_op_journal_transactions()中的處理操做以下圖:

0266:判斷天然要進行寫入journal的。

0271:循環處理Transaction,固然此時假設只有一個Transaction。

0274:解析每個Transaction的操做。

0278:獲取每一個操做的數據長度。

0279:獲取每一個操做的數據段偏移。

0281:將數據操做打包到內存tbl中造成日誌數據。

0283:提交日誌數據,完成下發任務journal->submit_entry,這裏的journal由繼承類FileJournal 實現的,因此這裏繼續調用FileJournal::submit_entry()。

1614:將申請的寫日誌完成的回調oncommit添加到completions隊列中,等待寫日誌完成後開始調用,這裏指的是C_JournaledAhead。

1618:若是當前的寫操做隊列沒有能夠處理的日誌操做時,寫操做線程應該處於睡眠的狀態,因此須要喚醒這個寫線程。

1620:開始喚醒寫日誌的線程。

1622:將本次須要寫入的buffer統計到寫線程的隊列writeq中。

接下來就交給了寫日誌的線程FileJournal::write_thread_entry()中開始處理。這個寫操做的線程就是要把數據寫入到文件當中,完成後調取completions中對應的成員進行回調,而後調到C_JournaledAhead->finish() ,再進入FileStore::_journaled_ahead()中,這個函數分爲了兩個部分,一部分是將data操做提交到op_wq隊列,另一個就是對於journal操做完成的回調處理。

2178:將操做的op添加到FileStore:: op_wq中,而後等待寫數據的線程將數據寫入文件。

2181:先解析出在日誌完成以後須要回調的那些操做,造成to_queue隊列。

2187:若是這個to_queue隊列不是空的隊列,則交給ondisk_finisher隊列進行處理。

2185:將日誌保存完成的回調ondisk交給ondisk_finisher,後續有finisher線程處理,這裏的ondisk註冊回調爲C_OSD_OnOpApplied。

 

 

10. 上面說道op_wq隊列,將寫data的操做加入到了op_wq中以後,這裏會觸發寫data的線程。

data的寫操做線程OpWQ::_process(),再調到FileStore::_do_op()函數中,再到FileStore::_do_transactions()函數、FileStore::_do_transaction()函數調用。

 

2529:這裏解析這個操做,確定是一個OP_write的操做。

2531:解析文件目錄。

2532:解析文件的名字。

2533:解析要寫入文件的位置。

2534:解析要寫入文件的數據長度。

2537:解析要寫入文件的數據。

2541:根據上面解析的信息,開始調用FileStore::_write 將數據真正的寫入文件中的正確位置。當完成寫任務後開始進行逐級回調。

OpWQ::_process()操做 對應的就是 FileStore::_finish_op()。在FileStore::_finish_op中主要作的就剩下一件事兒了,就是將請求進行回調處理。

1998:開始回調sync的操做,暫時沒有涉及到快照克隆等 不須要考慮。

2001:準備開始回調onreadable的參數,這裏回調註冊的爲C_OSD_OnOpCommit。

2002:進行其餘操做的回調處理。

 

7、請求的回調處理

11. 到目前爲止已經完成了journal的寫入操做和data的寫入操做。他們分別對應的回調爲C_OSD_OnOpApplied,C_OSD_OnOpCommit。他們作的事情差很少,接下來先看看C_OSD_OnOpApplied的操做是怎樣的

C_OSD_OnOpApplied->finish()  調取pg->op_applied(),最後到ReplicatedBackend::op_applied()

0667:開始刪除在等待隊列中的本端osd序號。這樣表示本端已經處理完成了。

0670:檢查是否是全部的等待在waiting_for_applied隊列上的osd都完成了操做。這時能夠進行徹底回調。這裏的on_applied註冊的是C_OSD_RepopApplied回調。

0676:這裏開始檢查是否是全部的waiting_for_applied、waiting_for_commit隊列中都已經處理完成則done成功,那時就能夠刪除in_progress_ops隊列的ops了。

 

12. 接續C_OSD_RepopApplied 逐漸的想客戶端進行調用。這裏開始調用ReplicatedPG::repop_all_applied(),在這裏會設置repop->all_applied 爲ture。而後調用eval_repop進行處理。

7571:查看是否存在請求須要回調。

7574:開始循環處理這裏的請求,而後將請求發還給客戶端。

7578:恢復開始接收到請求的MOSDOp的操做。

7579:建議對應的應答消息,這個應答消息與接受到的請求相對應。

7582:將請求發還給客戶端。

 

13. 以上就是整理osd來處理數據寫操做請求的所有內容和流程了。

 

 

總結:

1.這裏的流程太多了就再也不細說了,請參考下面的所有結構圖。

2.osd 預先運行進程 ceph_osd.cc中的main開始。

3.osd結構中申請了管理消息的SimpleMessager模塊,該模塊中存在一個叫作Accepter模塊,該模塊專門用來監聽端口等待客戶端進行鏈接的。而後交給reader,這時reader將已經創建的鏈接中開始讀取message。

4.將message轉化爲OpRequest,而且交到dispath隊列中。

5.由message等信息 轉化得知pool信息和PG的信息,而後把message交給PG進行處理。

6.將OpRequest添加到osdservice->op_wq隊列中,該隊列中會有處理的線程,線程會將繼續處理這個OpRequest的。

7.交給ReplicaPG處理 建立repop 全部的applied與commit的管理。

8.解析出OpRequest中的全部op與data。把這些數據用一個叫作Transaction的結構進行管理。

9.有transaction的處理開始,將replica osd的操做發送給副本。

10.本端開始處理journal的寫入操做,加入到writeq隊列,該隊列有獨立線程完成寫入的操做。

11.寫journal完成後,再將要寫入的data添加到op_wq中,該隊列一樣存在一個線程進行處理,將數據最終的保存到文件中。

 

相關文章
相關標籤/搜索