Ceph網絡模塊(2) - AsyncMessenger組織結構

Ceph網絡模塊(2) - AsyncMessenger數據結構分析


本文主要介紹AsyncMessenger的代碼框架結構和主要使用到的數據結構

這裏寫圖片描述

上圖表示Ceph的AsyncMessenger模塊中各個關鍵類之間的聯繫。在AsyncMessenger模塊中用到的類主要有14個,下面逐一介紹每個類的作用,以及其中包含的主要成員變量和方法。


1、 AsyncMessenger類、SimplePolicyMessenger類和Messenger類

AsyncMessenger類、SimplePolicyMessenger類和Messenger類三者是繼承與被繼承的關係,Messenger是一個抽象的消息管理器,其主要接口在派生類AsyncMessenger中實現,SimplePolicyMessenger類則是對消息管理器的一些連接的策略進行定義的設置,AsyncMessenger中定義和實現了消息管理器的相關成員變量以及方法。

一個AsyncMessenger實例的關鍵成員變量以及類方法如下表所示(包括該類繼承的父類成員變量以及類方法)。AsyncMessenger包含一個WorkerPool對象、一個Processor實例,以及3個AsyncConnectionRef對象列表和1個ConnectionRef對象列表。


AsyncMessenger類中的成員變量:

成員變量名返回值類型描述
*poolWorkerPool通過pool->get_worker()從線程池中獲取工作線程來進行工作
processorProcessorProcessor實例,主要用來監聽連接,綁定socket,接受連接請求等,相當於AsyncMessenger的處理中心
listen_sd int 定義的監聽套接字
conns ceph::unordered_map(entity_addr_t, AsyncConnectionRef) 地址和連接的map表,創建一個新的連接時將連接和和地址信息加入到這個map表中,在發送消息時先根據地址對這個map進行一次查找,如果找到了返回連接,如果沒有找到創建一個新的連接。
accepting_conns set(AsyncConnectionRef) 接收連接的集合,這個集合主要存放那些已經接收的連接。
deleted_conns set(AsyncConnectionRef)已經關閉並且需要清理的連接的集合
local_connectionConnectionRef本地連接的計數器
did_bind bool 初始值爲false,綁定地址後置爲true,stop的時候再次置爲false

AsynsMessenger類中的成員方法:

成員方法名返回值類型描述
bind (const entity_addr_t& bind_addr)int綁定套接字,具體綁定過程是由Processor的bind()函數完成的
start()int註冊一個AsyncMessenger的實例後,啓動這個實例,具體執行過程是WokerPool的start()函數完成的。
wait()void等待停止的信號,如果收到停止的信息後,調用Processor的stop()函數,然後將did_bind置爲false,最後刪除建立的連接
send_message (Message *m, const entity_inst_t& dest)int加了一個鎖,然後調用_send_message(m, dest),將消息發送到目的地址
get_connection (const entity_inst_t& dest)ConnectionRef函數用來建立連接,判定是否爲本地連接,否則再繼續查找連接是否已經存在,如果不存在再創建一個連接
ready()void註冊的AsyncMessenger已經準備好了,啓動事件處理中心,開始工作,啓動工作線程
create_connect(const entity_addr_t& addr, int type)AsyncConnectionRefcreate一個連接並將其加到conns中
submit_message(Message *m, AsyncConnectionRef con,const entity_addr_t& dest_addr, int dest_type)void發送消息的時候會用到,根據目的地址判斷需要發送消息的連接是否存在,以及連接是否是本地連接,如果是本地連接,直接對消息進行dispatch,如果連接不存在,需要根據消息類型創建新的連接
_send_message(Message *m, const entity_inst_t& dest)int從連接中查找目的地址,然後調用submit_message()發送消息
add_accept(int sd)AsyncConnectionRef新建一個連接,然後將其加入到accepting_conns中

2、 Processor類、WorkerPool類和Worker類


  • Processor類相當於AsyncMessenger模塊中的一個處理器,AsyncMessenger需要完成的很多操作(start、ready、bind等)都是通過Processor來完成的。當Messenger完成地址綁定後,Processor啓動,然後監聽即將到來的連接。就是說AsyncMessenger模塊的一些啓動、綁定、就緒等操作是在Processor相應操作的基礎上封裝的。

  • Processor類定義了一個AsyncMessenger的對象,一個NetHandler的實例,一個Worker對象。

Processor類的成員變量(方法):

成員變量(方法)名返回值類型描述
*msgrAsyncMessengerAsyncMessenger的指針實例,用於調用AsyncMessenger中的成員變量(方法),用的最多的還是綁定時獲取的地址信息等。
netNetHandler綁定套接字後將其設置爲非阻塞,然後這是套接字選項。
*workerWorker工作線程
listen_sdint獲取套接口描述字的值,非負表示套接字創建成功,-1表示出錯
nonceuint64_t構造函數中用於entity_addr_t的唯一標識ID
bind(const entity_addr_t &bind_addr, const set& avoid_ports)int執行綁定套接字的具體過程
start(Worker *w)int執行消息模塊的start,具體就是啓動線程,讓其處於工作狀態
accept()void建立連接的過程,如果連接建立成功,則通過add_accept()函數將連接加入到accepting_conns集合中
stop()void關閉套接字
rebind(const set& avoid_port)int如果第一次沒有綁定成功或者其它原因導致的綁定失敗,執行重新綁定

  • WorkerPool類是一個線程池,主要作用是創建worker線程,然後將其放入自己的worker容器中,每次創建worker線程的時候根據配置文件的參數ms_async_op_threads來指定worker線程的數量,創建是在WorkerPool的構造函數中進行的。

  • 在WorkerPool類中定義了一個worker集合,用於存放worker線程,還定義了一個coreids,用戶存放cpu id的集合,提供指定cpu運行單個線程的作用。在配置文件中有一個參數是ms_async_affinity_cores,將創建的worker綁定到指定的cpu core上。如果創建了2個線程,綁定的cpu core是0、1,默認的ms_async_affinity_cores值是空的,即使用全部的cpu資源,如果cpu的資源不夠用的時候可以將worker指定cpu core。

WorkerPool類的成員變量(方法)

成員變量(方法)名返回值類型描述
coreidsvector用於存放CPU id的集合
WorkerPool(CephContext *c)構造函數WorkerPool的構造函數,根據ms_async_op_threads的值創建相應數量的worker線程,同時完成worker和cpu core的綁定。
start()void創建worker集合中的worker線程,啓動線程開始工作
*get_worker()Worker獲取worker集合中的worker線程
get_cpuid(int id)int獲取cpu的id
workersWorker*Worker線程的集合,WorkerPool在構造函數中創建的worker線程放入到這個集合中

  • Worker類是具體的工作線程,Worker線程的主要工作是一個循環,調用epoll_wait獲取需要處理的事件,用循環來處理這個事件,當外部有操作時,比如讀取消息,註冊一個回調類,創建一個文件事件,然後啓動回調操作即可處理請求。消息模塊啓動時,用一個線程在Worker類中定義了一個WorkerPool對象,一個EventCenter的實例。

Worker類的成員變量(方法)

成員變量(方法)名返回值類型描述
*poolWorkerPoolWorkerPool的實例,在entry()函數中用於獲取cpu的id
donebool如果線程的工作完成置爲true,否則false
centerEventCenterEventCenter的實例,在Worker的構造函數中執行EventCenter的初始化工作
*entry()void工作線程的入口函數,啓動一個while循環來執行事件的處理,在整個消息模塊中就使用了這一個工作線程
stop()void將done置爲true,然後調用EventCenter的wakeup函數,即停止socket工作

3、 AsyncConnection類

  • AsyncConnection是整個Async消息模塊的核心,連接的創建和刪除、數據的讀寫指令、連接的重建、消息的處理等都是在這個類中進行的。本小節重點分析了其中的重要成員變量和24個成員函數。

    AsyncConnection類的成員變量

成員變量名返回值類型描述
*async_msgrAsyncMessengerAsyncMessenger對象,調用一些環境變量等
out_qmap(int, list(pair(bufferlist, Message*)) )存放消息和消息map信息的數據結構
sentlist(Message*)存放那些需要發送的消息
local_messageslist(Message*)存放本地傳輸的消息
outcoming_blbufferlist臨時存放消息的bl
read_handlerEventCallbackRef處理讀請求的回調指令
write_handlerEventCallbackRef處理寫請求的回調指令
connect_handlerEventCallbackRef處理連接請求的回調指令
local_deliver_handlerEventCallbackRef處理本地連接請求的回調指令
data_bufbufferlist存放數據的bl
data_blpbufferlist::iteratordata_buf的指針
front, middle, databufferlist頭部,中間部分和數據部分
connect_msgceph_msg_connect消息連接
netNetHandlerNetHandler的實例,處理網絡連接
*centerEventCenterEventCenter的對象,用來調用事件中心的操作
*recv_bufchar用於從套接字中接收消息的buf

AsyncConnection類的成員方法

編號成員方法名返回值類型描述
1do_sendmsg(struct msghdr &msg, int len, bool more)int返回的是需要被髮送的消息的長度
2try_send(bufferlist &bl, bool send=true)int加上一個write_lock,然後調用_try_send來真正發送消息
3_try_send(bufferlist &bl, bool send=true)int如果send的值爲false,會將bl添加到send buffer中,這麼做的目的是避免messenger線程外發生錯誤
4prepare_send_message(uint64_t features, Message *m, bufferlist &bl)void將m中的數據encode和copy到bl中
5read_until(uint64_t needed, char *p)int循環讀,調用read_bulk,如果r的值不爲0,一直循環下去
6_process_connection()int處理連接,根據不同的state狀態執行不同的操作,關鍵點是state的值不同
7_connect()void首先將STATE_CONNECTING的值賦給state,然後調用dispatch_event_external將read_handler事件添加到external_events集合中
8_stop()void註銷連接,然後將STATE_CLOSED賦給state,關閉套接字,清理事件
9handle_connect_reply(ceph_msg_connect &connect, ceph_msg_connect_reply &r)int根據reply.tag值的不同執行不同的操作
10handle_connect_msg(ceph_msg_connect &m, bufferlist &aubl, bufferlist &bl)int處理消息的連接,如果成功則接收這個連接
11discard_out_queue()void清除AsyncConnection的消息隊列
12requeue_sent()void重新將send隊列入隊
13handle_ack(uint64_t seq)void處理確認信息,刪除send隊列中的message
14write_message(Message *m, bufferlist& bl)int將消息寫到complete_bl中,調用_try_send發送消息
15_reply_accept(char tag, ceph_msg_connect &connect, ceph_msg_connect_reply &replybufferlist authorizer_reply)int有一個bufferlist結構的reply_bl,調用try_send將reply_bl發送出去
16is_queued()bool判斷是否入隊列,主要是out_q和outcoming_bl這兩個隊列
17shutdown_socket()void關閉套接字
18connect(const entity_addr_t& addr, int type)void在AsyncConnection第一次構造的時候使用,然後調用_connect()函數
19accept(int sd)void將state的值設置爲STATE_ACCEPTING,然後調用create_file_event函數創建文件事件,調用dispatch_event_external函數將回調指令分發出去
20send_message(Message *m)int一般需要發送消息的時候都會調用這個函數進行具體的發送操作,在此之前已經完成了連接
21handle_write()void使用一個while循環調用write_message將data寫入到m中
22process()void還是根據不同的state值做不同的處理
23local_deliver()void這個函數主要用來處理本地的消息傳遞
24cleanup_handler()void清理事件處理助手,將其重置

4、 EventCenter類和EventCallback類

  • AsyncMessenger消息模塊是基於epoll的事件驅動方式,取代了之間每個連接需要建立一個Pipe,然後創建兩個線程,一個用來處理消息的接收,另一個用來處理消息的發送,其它操作也是藉助線程的方式。不同於SimpleMessenger消息模塊,AsyncMessenger消息模塊使用了事件,所以需要一個處理事件的數據結構,即EventCenter,用一個線程專門用來循環處理,所有的操作都是通過回調函數來進行的,避免了大量線程的使用。在EventCenter定義了一個FileEvent的數據結構和一個TimeEvent的數據結構,大部分事件都是FileEvent。下面介紹EventCenter中的主要成員變量和方法。
成員變量(方法)名返回值類型描述
FileEventstruct文件事件類
TimeEventstruct時間事件類
external_eventsdeque(EventCallbackRef)用於存放外部事件的隊列
*file_eventsFileEventFileEvent的實例
*driverEventDriverEventDriver的實例
time_eventsmap(utime_t, list(TimeEvent))事件事件的容器
net NetHandlerNetHandler的實例
process_time_events()int處理時間事件
*_get_file_event(int fd)FileEvent獲取文件事件
init(int nevent)int根據不同的宏創建不同的事件處理器;調用create_file_event創建事件。
create_file_event(int fd, int mask, EventCallbackRef ctxt)int根據fd和mask創建文件事件,調用add_event函數將創建的事件加入到事件處理器中去處理
create_time_event(uint64_t milliseconds, EventCallbackRef ctxt)uint64_t創建time event,然後將其加入到time_events中
delete_file_event(int fd, int mask)void刪除file event
delete_time_event(uint64_t id)void刪除time event
process_events(int timeout_microseconds)int如果事件是read_cb或者write_cb則調用相應的回調函數來進行處理(由do_request函數來完成);如果不是這兩種事件,則將external_events隊列中的事件取出放入cur_process中,調用一個while一個循環來處理。
dispatch_event_external(EventCallbackRef e)void將創建的外部事件放入external_events隊列中
  • EventCallback是一個接口類,根據操作不同會定義其子類,使用方式用一個虛函數do_request()來回調處理不同的事件,具體的處理是在do_request()中進行的。

5、 EventDriver類、EpollDriver類、KqueueDriver類和SelectDriver類

  • 事件中心相當於一個事件處理的容器,它本身並不真正去處理事件,通過回調函數的方式來完成事件的處理。同樣,如何獲取需要處理的事件也不是事件中心來完成的,它只負責處理,具體對需要處理的事件的獲取是通過EventDriver來完成的,EventDriver是一個接口類,其實現主要是由EpollDriver、KqueueDriver和SelectDriver三個類操作的。Ceph支持多種操作系統的使用,如果使用的是Linux操作系統,使用EpollDriver,如果是BSD,使用KqueueDriver,如果都不是的情況下再使用SelectDriver(系統定義爲最壞狀況下)。事件驅動的執行主要依賴於epoll的方式,其中主要有三個函數:epoll_create(在epoll文件系統建立了個file節點,並開闢epoll自己的內核高速cache區,建立紅黑樹,分配好想要的size的內存對象,建立一個list鏈表,用於存儲準備就緒的事件); epoll_ctl(把要監聽的socket放到對應的紅黑樹上,給內核中斷處理程序註冊一個回調函數,通知內核,如果這個句柄的數據到了,就把它放到就緒列表);epoll_wait(觀察就緒列表裏面有沒有數據,並進行提取和清空就緒列表,非常高效)。由於本項目運行在Linux中,所以下面以EpollDriver爲例對Ceph的底層事件驅動進行描述。

EpollDriver的成員變量(方法)

成員變量(方法)名返回值類型描述
epfdintepoll的文件描述符
*eventsstruct epoll_eventepoll_event的一個對象
sizeint在執行初始化時獲取文件數量
init(int nevent)int執行EpollDriver的初始化,主要是調用epoll_create,建立epoll對象
add_event(int fd, int cur_mask, int add_mask)int根據事件的mask執行不同的操作,如果是EVENT_READABLE,表示對應的文件描述符可讀,如果是EVENT_WRITABLE,表示文件描述符可寫,然後調用epoll_ctl添加事件
del_event(int fd, int cur_mask, int del_mask)int調用epoll_ctl執行事件的修改或者刪除
resize_events(int newsize)int清空事件數量
event_wait(vector &fired_events, struct timeval *tp)int調用epoll_wait循環處理事件

6、NetHandler類

  • NetHandler是AsyncMessenger模塊中用於網絡處理的類,其中定義了6個關鍵成員方法,其中的NetHandler::generic_connect()是每個連接都需要使用到的,創建socket、將socket設置爲非阻塞、設置socket選項等都是經常使用的方法,下表對其詳細分析。
成員方法名返回值類型描述
create_socket(int domain, bool reuse_addr=false)int創建socket
generic_connect(const entity_addr_t& addr, bool nonblock)int通信雙方通過該函數產生連接,首先調用create_socket()創建一個socket,然後將創建的socket設置爲非阻塞,完成以後調用系統socket:: connect()建立連接
set_nonblock(int sd)int將Socket設置爲非阻塞的
set_socket_options(int sd)void調用系統的socket::setsockopt函數,設置套接字的一些關鍵選項
connect(const entity_addr_t &addr)int對NetHandler::generic_connect()進行了一個簡單的封裝
nonblock_connect(const entity_addr_t &addr)int接口函數,設置非阻塞的連接

上文描述了AsyncMessenger基本數據結構及框架,下一章描述代碼流程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章