天天看點

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的接口,發送資料了。      

繼續閱讀