半同步半異步高性能網絡編程

網絡編程的模式分爲3種:

1. nginx的全異步方式,使用epoll處理網絡數據,對於請求的處理也完全是異步的。任何一個請求的處理如果花費了較長時間,那麼nginx進程就會被處理操作阻塞,導致無法處理IO事件

2. 簡單的一個連接一個線程方案,這種方案無法處理大量併發的連接,適用mysql這類連接數不多的場景。其中也有一些優化的做法,例如使用線程池避免不斷的創建銷燬線程。

3. 半同步半異步方式,啓動一個IO線程使用epoll處理網絡數據。當收到一個完整的請求包,把請求放到任務隊列,有一個線程池不斷的從隊列裏獲取任務,同步處理,處理完之後再把響應數據由IO線程返回給用戶。


其中半同步半異步方式廣泛應用於服務器端編程,例如taobao開源的tbnet(tair,tfs都使用該庫)。主要原因是nginx的全異步方式編寫程序難度高,開發效率低,而一個連接一個線程方案無法支持大量的併發連接。半同步半異步方式則是開發效率與高性能之間的一個權衡,網絡處理等有框架進行處理,開發者只關注業務邏輯的編寫,只需同步處理請求即可,對於需要訪問mysql等的應用非常方便。


對於tbnet這類的半同步半異步的網絡庫,使用者需要對多個類進行繼承,override相關的handler來處理,一個非常簡單的例子需要不少代碼。

c++11標準出來之後,處理函數可以用匿名函數優雅的解決。handy是一個使用最新的c++11來簡化網絡編程的庫,裏面的例子非常簡潔。

使用handy庫進行半同步半異步編程的例子如下:

#include <handy/handy.h>

using namespace std;
using namespace handy;

int main(int argc, const char* argv[]) {
    EventBase base;
    HSHA hsha(&base, 4);
    int r = hsha.bind("", 99);
    exitif(r, "bind failed");
    Signal::signal(SIGINT, [&]{ base.exit(); hsha.exit(); signal(SIGINT, SIG_DFL);});

    hsha.onMsg(new LineCodec, [](const TcpConnPtr& con, const string& input){
        int ms = rand() % 1000;
        info("processing a msg");
        usleep(ms * 1000);
        return util::format("%s used %d ms", input.c_str(), ms);
    });
    base.loop();
    info("program exited");
}
其中hsha.onMsg調用指定如何處理消息,第一個參數指定如何對消息進行編解碼,這裏傳入的是一個行解碼器,適用於telnet中的應用。第二個參數是個回調函數,指定如何處理消息。

回調函數第一個參數爲連接指針,第二個參數爲消息。返回值爲string,即消息的響應。

HSHA hsha(&base, 4) 創建一個半同步半異步服務器,線程池中線程數量爲4.


這個例子的完整版本見:https://github.com/yedf/handy/blob/master/examples/hsha.cc
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章