首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Muduo 實戰:用 Timing Wheel 高效踢掉空閒連接

     編輯:關於C++

問題背景

嚴肅網絡程序需應用層心跳,但「踢掉空閒連接」是權宜之計。若連接 8 秒無數據則斷開,兩種暴力做法:

  1. 全局定時器每秒掃描所有連接 lastReceiveTime,連接數大時開銷高。
  2. 每連接一個 One-shot Timer,頻繁更新 Timer Queue 壓力大。

Timing Wheel(時間輪)原理

利用固定大小循環隊列(8 格桶),每秒指針前移一格:

  • 新連接/收到數據 → 放入第 8 格(剩餘壽命 7 秒)。
  • 每秒 Callback:彈出第 1 格,斷開其中連接,將空桶移至隊尾。
  • 僅檢查當前桶,O(1) 操作,無需掃描全量連接。

圖示狀態演進:tail 指針如時鐘指針旋轉,連接在桶間隨時間遷移或被刷新回尾部。

代碼實現關鍵點

// 數據結構
struct Entry : copyable {
    Entry(const WeakTcpConnectionPtr& w) : weakConn_(w) {}
    ~Entry() { if (auto c = weakConn_.lock()) c->shutdown(); }
    WeakTcpConnectionPtr weakConn_;
};
using EntryPtr = shared_ptr<Entry>;
using Bucket = unordered_set<EntryPtr>;
using WeakConnectionList = circular_buffer<Bucket>;

// 構造函數
EchoServer(..., int idleSeconds)
: connectionBuckets_(idleSeconds) {
    loop->runEvery(1.0, boost::bind(&EchoServer::onTimer, this));
    connectionBuckets_.resize(idleSeconds);
}

// 定時器 Callback:極簡一行
void onTimer() { connectionBuckets_.push_back(Bucket()); }

// 連接建立:創建 Entry 放入尾桶,弱引用存入 TcpConnection::context
void onConnection(const TcpConnectionPtr& conn) {
    if (conn->connected()) {
        EntryPtr entry(new Entry(conn));
        connectionBuckets_.back().insert(entry);
        conn->setContext(WeakEntryPtr(entry));
    }
}

// 收到消息:從 context 取弱引用升級為強引用,重新插入尾桶
void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp) {
    // ... echo 邏輯 ...
    WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));
    if (auto entry = weakEntry.lock())
        connectionBuckets_.back().insert(entry);
}

利用 shared_ptr 引用計數自動管理 Entry 生命週期:桶裡無引用 → Entry 析構 → ~Entry() 呼叫 shutdown()。weak_ptr 避免循環引用,circular_buffer 自動彈出舊桶觸發析構。

優化方向

在 TcpConnection 記錄「上次插入時的 tail 位置」,若未變則跳過重複插入,減少 Hash Set 操作。

教學價值

展示 shared_ptr/weak_ptr 結合值語義、RAII、容器特性解決工程問題的優雅範例。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved