1. 程式人生 > >高性能網絡編程(二)

高性能網絡編程(二)

idt read tar tor 需要 ebs 數據 擴展 聯網

C10K問題的解決方案探討
要解決這一問題,從純網絡編程技術角度看,主要思路有兩個:

    一個是對於每個連接處理分配一個獨立的進程/線程;另一個思路是用同一進程/線程來同時處理若幹連接。

8.1 思路一:每個進程/線程處理一個連接
這一思路最為直接。但是由於申請進程/線程會占用相當可觀的系統資源,

同時對於多進程/線程的管理會對系統造成壓力,因此這種方案不具備良好的可擴展性。

因此,這一思路在服務器資源還沒有富裕到足夠程度的時候,是不可行的。

即便資源足夠富裕,效率也不夠高。總之,此思路技術實現會使得資源占用過多,可擴展性差。


8.2 思路二:每個進程/線程同時處理多個連接(IO多路復用)
IO多路復用從技術實現上又分很多種,我們逐一來看看下述各種實現方式的優劣。

● 實現方式1

:傳統思路最簡單的方法是循環挨個處理各個連接,每個連接對應一個 socket,當所有 socket 都有數據的時候,這種方法是可行的。

但是當應用讀取某個 socket 的文件數據不 ready 的時候,整個應用會阻塞在這裏等待該文件句柄,即使別的文件句柄 ready,也無法往下處理。

實現小結:直接循環處理多個連接。
問題歸納:任一文件句柄的不成功會阻塞住整個應用。

● 實現方式2select要解決上面阻塞的問題,思路很簡單,如果我在讀取文件句柄之前,先查下它的狀態,ready 了就進行處理,不 ready 就不進行處理,這不就解決了這個問題了嘛?於是有了 select 方案。用一個 fd_set 結構體來告訴內核同時監控多個文件句柄,當其中有文件句柄的狀態發生指定變化(例如某句柄由不可用變為可用)或超時,則調用返回。之後應用可以使用 FD_ISSET 來逐個查看是哪個文件句柄的狀態發生了變化。這樣做,小規模的連接問題不大,但當連接數很多(文件句柄個數很多)的時候,逐個檢查狀態就很慢了。因此,select 往往存在管理的句柄上限(FD_SETSIZE)。同時,在使用上,因為只有一個字段記錄關註和發生事件,每次調用之前要重新初始化 fd_set 結構體。

[align=right !important][color=initial !important]1 intselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);


實現小結:有連接請求抵達了再檢查處理。
問題歸納:句柄上限 + 重復初始化 + 逐個排查所有文件句柄狀態效率不高。

● 實現方式3:poll 主要解決 select 的前兩個問題:通過一個 pollfd 數組向內核傳遞需要關註的事件消除文件句柄上限,

同時使用不同字段分別標註關註事件和發生事件,來避免重復初始化。

實現小結:設計新的數據結構

提供使用效率。
問題歸納:逐個排查所有文件句柄狀態效率不高。

● 實現方式4poll既然逐個排查所有文件句柄狀態效率不高,很自然的如果調用返回的時候只給應用提供發生了狀態變化(很可能是數據 ready)的文件句柄,進行排查的效率不就高多了麽。epoll 采用了這種設計,適用於大規模的應用場景。實驗表明,當文件句柄數目超過 10 之後,epoll 性能將優於 select 和 poll;當文件句柄數目達到 10K 的時候,epoll 已經超過 select 和 poll 兩個數量級。

實現小結:只返回狀態變化的文件句柄。
問題歸納:依賴特定平臺(Linux)。

因為Linux是互聯網企業中使用率最高的操作系統,Epoll就成為C10K killer、高並發、高性能、異步非阻塞這些技術的代名詞了。FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了IOCP,Solaris推出了/dev/poll。這些操作系統提供的功能就是為了解決C10K問題。epoll技術的編程模型就是異步非阻塞回調,也可以叫做Reactor,事件驅動,事件輪循(EventLoop)。Nginx,libevent,Node.js這些就是Epoll時代的產物。

● 實現方式5由於epoll, kqueue, IOCP每個接口都有自己的特點,程序移植非常困難,於是需要對這些接口進行封裝,以讓它們易於使用和移植,其中libevent庫就是其中之一。跨平臺,封裝底層平臺的調用,提供統一的 API,但底層在不同平臺上自動選擇合適的調用。按照libevent的官方網站,libevent庫提供了以下功能:當一個文件描述符的特定事件(如可讀,可寫或出錯)發生了,或一個定時事件發生了,libevent就會自動執行用戶指定的回調函數,來處理事件。目前,libevent已支持以下接口/dev/poll, kqueue, event ports, select, poll 和 epoll。Libevent的內部事件機制完全是基於所使用的接口的。因此libevent非常容易移植,也使它的擴展性非常容易。目前,libevent已在以下操作系統中編譯通過:Linux,BSD,Mac OS X,Solaris和Windows。使用libevent庫進行開發非常簡單,也很容易在各種unix平臺上移植。一個簡單的使用libevent庫的程序如下:

高性能網絡編程(二)