最近在做一個有關時空數據查詢的分布式服務器的原型系統(主要是為了論文啦),其中需要實現一個比較高效的服務器。現將近一段時間的實驗和服務的大概框架記錄下來,希望能對分布式服務器感興趣的同學有所幫助。 同時,希望各位提出各種建議,這樣我能夠進一步的改進。代碼已經放在了github上, 寫的比較爛,這里就不公布鏈接了(嘿嘿), 想討論的同學請私信我~ 以下是全部記錄:
在我們實現的分布式服務器中,每個節點都存有時空數據,這些節點上的時空數據利用RTree進行索引。每個節點同時保存着路由(鄰居節點)信息。客戶端可以向服務器端發送數據查詢請求。服務器負責接收請求,並根據請求的類型進行相應的處理, 包括查詢本地的數據,轉發請求到鄰居節點。
時空數據是以數據流的形式源源不斷的插入到服務器中。當某個節點的數據達到系統預定義的閾值時,該節點將分裂成兩個節點。我們將計算數據的最優划分策略划分數據,並將划分的數據遷移到新的節點上。同時更新當前節點和新建節點的路由信息。
針對上面的要求,我們的服務器需要滿足以下兩個目標:
- 滿足高並發的數據查詢請求和數據傳輸請求
- 同時處理長連接和短連接
服務器中同時存在兩種連接方式:長連接和短連接。長連接是指Client端與Server端先建立通訊連接,連接建立以后不斷開,然后再進行報文的發送和接收。長連接常用於點對點的通訊,在服務器中,數據傳輸采用長連接。短連接是指Client端和Server端只有在進行一次報文收發操作時才進行通訊連接,操作完畢以后立即關閉連接。短連接一般針對一點對多點的通訊,如多個Client連接一個Server。在服務器中,數據查詢請求采用短連接。
常見的網絡服務器采用以下幾種方式:
- 一個線程服務一個客戶端,使用阻塞I/O
- 一個線程服務多個客戶端,使用非阻塞I/O
- 一個線程服務多個客戶端,使用異步I/O
第1種方式缺點在於當並發連接數過高時,操作系統同時處理幾百個線程會有性能問題。而且在服務器硬件配置不改變的情況下,隨着連接數的增加,將會導致性能急劇下降。第3種方式目前在Unix上還沒有普遍的應用,因此,在我們的系統中並沒有采用。第2種方式是將網絡句柄設置為非阻塞模式,然后使用select或者poll IO多路復用技術來告知那個句柄已經有數據在等待處理。在此模型下,由系統內核來告知應用程序某個網絡句柄的狀態。這種模型也就是傳統的IO多路復用模型,是目前大部分網絡服務器的解決方案。
我們的服務器是基於Linux開發的,在Linux上,實現IO多路復用有幾個主要的技術,分別是select模型,poll模型和epoll模型。
select模型最大的缺點是最大並發數限制,因為一個進程所打開的socket FD(文件描述符)是有限制的,默認是1024,因此select模型的最大並發數就被限制了。當然,我們可以通過修改系統參數增大最大並發數,然而由於select的每次調用都會線性掃描全部的socket FD集合。當socket FD增大時,會導致服務器效率線性下降。
poll模型基本上和select在效率上是一樣的,select的問題在poll模型中也沒有被解決。
epoll模型是目前比較優秀的IO多路復用模型,首先,epoll沒有最大並發連接的限制,上限是整個系統最大可以打開的socket FD數目,這個數遠大於2048,一般這個數目和系統內存關系很大,1G內存的機器的最大sockef FD數目可以達到10萬左右。其次,epoll最大的優點在於它只關心活躍的連接,而跟連接總數無關。因此在實際的網絡環境中,epoll的效率會遠高於select和poll。
鑒於epoll的優點,我們的服務器采用了epoll作為IO多路復用的基本技術。
常見的基於epoll的設計模式主要為單線程的事件循環,用於一些非阻塞的業務邏輯開發是很高效的,然而,在我們的服務器開發中,涉及傳輸數據。轉發請求的需求,耗時比較長。因此,單線程的epoll並不能滿足我們的需要。下面用一個簡單的例子來說明單線程模式下epoll的缺點。

由於是單線程模型,當某個客戶端的請求處理時間較長時,會影響服務器接收來自其他客戶端的連接請求,進而影響整個服務器的並發性能。
因此,單線程的epoll模型在我們的分布式服務器中並不適用。下面是我們服務器的設計方案:

圖2 圖3
上面這種設計模式一般稱為Reactor模式,Reactor模式是處理並發I/O比較常見的一種模式,中心思想是首先將所有要處理的I/O事件添加到一個中心的多路復用器上(epoll), 同時主線程阻塞在多路復用器上;一旦有連接到來或者是准備就緒,多路復用器將返回並將相應的I/O事件分發到對應的處理線程中。
我們的服務器采用了多個Reactor,也就是多線程的epoll。 Reactor被分為main reactor和sub reactors,分別對應圖2 和圖3。每個reactor中都有獨立的epoll來作為多路復用器。其中main reactor中的epoll負責監聽連接請求,一旦有連接到來,利用一定的分發策略將連接socket加入到sub reactors的epoll中。對於每個sub reactor的epoll,主要的工作就是監聽連接socket,一旦某個連接socket的I/O准備就緒,則通知相應的handler來接收數據並處理請求。
通過使用非阻塞I/O的多路復用技術epoll,並將連接請求與連接建立的之后的邏輯分離,我們設計了基於Reactor設計模式的服務器,滿足了高並發的處理數據查詢請求與數據傳輸請求。並能夠同時處理長連接與短連接。 同時,還有一些細節我們可以改進:
- main reactor 到sub reactors的分發策略
目前我們采用了Round-robin的方式,這樣有可能產生負載不均衡的現象。 后面我們可以使用一定的策略,將main reactor接收到的連接請求分發到相對空閑的sub
reactor中。保證整個系統的負載均衡。
- sub reactor的多線程化
對於每個sub reactor來說,這是單線程的。 我們同樣可以將sub reactor進一步划分, 將數據的接收與請求的處理分離,請求的處理采用線程池的方式。這將進一步提高服務 器的並發能力。
特此感謝ChinaUnix論壇的@linux_c_py_php的一篇帖子給我帶來的巨大幫助,友情鏈接:http://bbs.chinaunix.net/thread-4067753-1-1.html
轉載請注明出處: http://www.cnblogs.com/meibenjin/p/3604389.html
