當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中,該隊列一樣存在一個線程進行處理,將數據最終的保存到文件中。