Ceph網絡模塊介紹

1 Socket簡介

Ceph的網絡通信是基於Socket技術實現的,所以要想搞清楚網絡部分,必須先清楚socket的工作機制,這裏介紹一些基本的內容,要深入理解socket請參閱《linux網絡編程》一書和《tcp/ip詳解》。

socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open -> 讀寫write/read -> 關閉close”模式來操作。Socket也是按照這個思想實現的,而我們工作中主要會用到它的以下接口:

  • int socket(int domain, int type, int protocol); 
    這個函數創建一個socket描述符,唯一標識一個socket,對應於普通文件的打開接口。

  • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    該函數把一個具體的地址綁定到相應的socket描述符上。通常服務器在啓動的時候都會綁定一個衆所周知的地址(如ip地址+端口號),用於提供服務,客戶就可以通過它來接連服務器;而客戶端就不用指定,有系統自動分配一個端口號和自身的ip地址組合。

  • int listen(int sockfd, int backlog);
    如果作爲一個服務器,在調用socket()bind()之後就會調用listen()來監聽這個socket。socket()函數創建的socket默認是一個主動類型的,listen函數將socket變爲被動類型的,等待客戶的連接請求。

  • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。

  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    TCP服務器監聽到這個請求之後,就會調用accept()函數取接收請求,這樣連接就建立好了。之後就可以開始網絡I/O操作了,即類同於普通文件的讀寫I/O操作。 

以上這些接口,就可以用於實現客戶端和服務器端的socket連接的建立了。之後就可以通過::send()::recv()接口從socket中讀取數據了。

2 Ceph中的相關實現

在Ceph中,網絡部分主要是在msg目錄中,包括Messenger、Accepter、Pipe和Connection這幾個實體,當然其還要和具體的dispatcher結合起來。單說socket部分,Ceph將其分拆成了兩個部分,socket連接的維護(創建、綁定、監聽、刪除)主要實現在了Accepter 這個文件中了以及I/O部分主要實現在了Pipe中。也就是說可以用一個公式表示就是:

  • Accepter + Pipe = socket 

而Messenger它是Ceph網絡傳輸的抽象體,包括Accepter、Pipe、Dispatch_queue。
Ceph通過OSDSession這個類來維護管理client端和server端之間建立的Session連接,並且維護了Pending在Session中的所有Op。OSDSession暴露出來的接口就是一個ConnectionRef結構的send_message(),而在這個函數裏,它實際上又返回過來找到messenger、pipe然後將請求發送出去。

主要的使用流程是:

1)通過Messenger::create_client_messenger()或者Messenger::create()創建一個messenger,目前支持SimpleMessenger、AsyncMessenger、Xio這三種,可以在ceph的配置文件中配置。
2)創建一個dispatcher,如果是客戶端一般就是創建一個Objecter和Monc,如果是服務端就創建對應的角色實例如OSD、Mon等,並且上一步創建的messenger會被設置爲他們的messenger。
3)設置Messenger的policy,用於配置socket連接的異常處理策略。
4)Dispatcher的初始化,如objecter->init();
5)如果是服務器端(比如OSD),則需要調用messenger的bind()接口,爲messenger綁定一個地址和端口,如果是客戶端(比如Objecter)則不需要。這個bind操作是通過Accepter的bind()接口完成的,它裏面實際上幹了三件事情:創建socket、bind地址和端口、listen地址和端口;
6)通過Messenger的add_dispatcher_head()/add_dispatcher_tail()將初始化後的dispatcher加入的messenger的dispatcher list中。如果是添加的第一個dispatcher,則會調用Messenger的ready()接口,這個接口會調用Accepter的start()接口。而在客戶端和服務端,他們對於start()的處理是不一樣的。服務器端會創建一個線程使用poll機制去處理accept的連接,客戶端則不會處理。服務器accept的socket連接會創建相應的Pipe,丟到messenger裏面。
7)當pipe建立好之後,客戶端就會通過OSDSession中的conn找到messenger的接口,發送數據了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章