一種高效的網絡服務器設計

  最近在作一個有關時空數據查詢的分佈式服務器的原型系統(主要是爲了論文啦),其中須要實現一個比較高效的服務器。現將近一段時間的實驗和服務的大概框架記錄下來,但願能對分佈式服務器感興趣的同窗有所幫助。 同時,但願各位提出各類建議,這樣我可以進一步的改進。代碼已經放在了github上, 寫的比較爛,這裏就不公佈連接了(嘿嘿), 想討論的同窗請私信我~  如下是所有記錄:php

  在咱們實現的分佈式服務器中,每一個節點都存有時空數據,這些節點上的時空數據利用RTree進行索引。每一個節點同時保存着路由(鄰居節點)信息。客戶端能夠向服務器端發送數據查詢請求。服務器負責接收請求,並根據請求的類型進行相應的處理, 包括查詢本地的數據,轉發請求到鄰居節點。html

  時空數據是以數據流的形式源源不斷的插入到服務器中。當某個節點的數據達到系統預約義的閾值時,該節點將分裂成兩個節點。咱們將計算數據的最優劃分策略劃分數據,並將劃分的數據遷移到新的節點上。同時更新當前節點和新建節點的路由信息。react

  針對上面的要求,咱們的服務器須要知足如下兩個目標:linux

  • 知足高併發的數據查詢請求和數據傳輸請求
  • 同時處理長鏈接和短鏈接

  服務器中同時存在兩種鏈接方式:長鏈接和短鏈接。長鏈接是指Client端與Server端先創建通信鏈接,鏈接創建之後不斷開,而後再進行報文的發送和接收。長鏈接經常使用於點對點的通信,在服務器中,數據傳輸採用長鏈接。短鏈接是指Client端和Server端只有在進行一次報文收發操做時才進行通信鏈接,操做完畢之後當即關閉鏈接。短鏈接通常針對一點對多點的通信,如多個Client鏈接一個Server。在服務器中,數據查詢請求採用短鏈接。git

  常見的網絡服務器採用如下幾種方式:github

  1. 一個線程服務一個客戶端,使用阻塞I/O
  2. 一個線程服務多個客戶端,使用非阻塞I/O
  3. 一個線程服務多個客戶端,使用異步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

相關文章
相關標籤/搜索