程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> Muduo 網絡編程示例(六)限制服務器的最大並發連接數

Muduo 網絡編程示例(六)限制服務器的最大並發連接數

編輯:關於C++

本文已以大家都熟悉的 EchoServer 介紹如何限制服務器的並發連接數。

本文的代碼見 http://code.google.com/p/muduo/source/browse/trunk/examples/maxconnection/

《Muduo 網絡 編程示例 系列》計劃中的第六篇文章原本是“用於測試兩台機器的帶寬的 pingpong 程序”, pingpong 協議的程序已經在《muduo 與 boost asio 吞吐量對比》和《muduo 與 libevent2 吞吐量對 比》兩篇文章中介紹過了,所以我改為寫另外一個有點意思的主題。

這篇文章中的“並發連接 數”是指一個 server program 能同時支持的客戶端連接數,連接系由客戶端主動發起,服務端被動接 受(accept)連接。(如果要限制應用程序主動發起的連接,則問題要簡單得多,畢竟主動權和決定權都 在程序本身。)

為什麼要限制並發連接數?

一方面,我們不希望服務程序超載,另一方 面,更因為 file descriptor 是稀缺資源,如果出現 file descriptor 耗盡,很棘手(跟 “malloc 失敗/new() 拋出 std::bad_alloc”差不多同樣棘手)。

我在《分布式系統的工程化開發方法 》一文中曾談到 libev 作者建議的一種應對“accept()ing 時 file descriptor 耗盡”的辦法。

Muduo 的 acceptor 正是這麼實現的,但是,這個做法在多線程下不能保證正確,會有 race condition。(思考題:是什麼 race condition?)

其實有另外一種比較簡單的辦法:file descriptor 是 hard limit,我們可以自己設一個稍低一點的 soft limit,如果超過 soft limit 就 主動關閉新連接,這樣就避免觸及“file descriptor 耗盡”這種邊界條件。比方說當前進程的 max file descriptor 是 1024,那麼我們可以在連接數達到 1000 的時候進入“拒絕新連接”狀態,這樣 留給我們足夠的騰挪空間。

Muduo 中限制並發連接數

Muduo 中限制並發連接數的做法簡 單得出奇。以在《Muduo 網絡編程示例之零:前言》中出場過的 EchoServer 為例,只需要為它增加一 個 int 成員,表示當前的活動連接數。(如果是多線程程序,應該用 muduo::AtomicInt32。)

class EchoServer
{
 public:
  EchoServer(muduo::net::EventLoop* loop,
             const muduo::net::InetAddress& listenAddr,
             int maxConnections);
 
  void start();
 
 private:
  void onConnection(const muduo::net::TcpConnectionPtr& conn);
 
  void onMessage(const muduo::net::TcpConnectionPtr& conn,
                 muduo::net::Buffer* buf,
                 muduo::Timestamp time);
 
  muduo::net::EventLoop* loop_;
  muduo::net::TcpServer server_;
  int numConnected_; // should be atomic_int
  const int kMaxConnections;
};

然後,在 EchoServer::onConnection() 中判斷當前活動連接數,如果超過最大允許數, 則踢掉連接。

void EchoServer::onConnection(const TcpConnectionPtr& conn)
{
  LOG_INFO << "EchoServer - " << conn->peerAddress().toHostPort() << " -> "
    << conn->localAddress().toHostPort() << " is "
    << (conn->connected() ? "UP" : "DOWN");
 
  if (conn->connected())
  {
    ++numConnected_;    if (numConnected_ > kMaxConnections)    {      conn->shutdown();    }
  }
  else
  {
    --numConnected_;
  }
  LOG_INFO << "numConnected = " << numConnected_;
}

這種做法可以積極地防止耗盡 file descriptor。

另外,如果是有業務邏輯的服務 ,可以在 shutdown() 之前發送一個簡單的響應,表明本服務程序的負載能力已經飽和,提示客戶端嘗 試下一個可用的 server(當然,下一個可用的 server 地址不一定要在這個響應裡給出,客戶端可以 自己去 name service 查詢),這樣方便客戶端快速 failover。

後文將介紹如何處理空閒連接 的超時:如果一個連接長時間(若干秒)沒有輸入數據,則踢掉此連接。辦法有很多種,我用 Time Wheel 解決。

查看本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved