1. 程式人生 > >Muduo網路程式設計之使用Timing wheel 踢掉空閒連線

Muduo網路程式設計之使用Timing wheel 踢掉空閒連線

本文記錄自己的理解和部分程式碼註釋。

1.模擬輪盤

通過boost::circular_buffer來模擬輪盤。簡單學習了一下關於這個資料結構的內容。
它有如下特性:

1.支援隨機訪問
2.固定容量
3.插入元素超過容量時會對頭部或者尾部元素彈出

下面看一個簡單示例:

#include <iostream>
#include <limits>
#include <boost/circular_buffer.hpp>


using namespace std;
using namespace boost;


void print(const
circular_buffer<int>& nums) { for (int i = 0; i < nums.size(); ++i) { cout << nums[i] << " "; } cout << endl; } int main(int argc, char* argv[]) { circular_buffer<int> nums(5); //宣告一個總容量為5的circular_buffer nums.push_back(1); nums.push_back(2
); nums.push_back(3); nums.push_back(4); nums.push_back(5); print(nums); nums.push_back(6); print(nums); nums.push_front(7); print(nums); nums[3] = 9; print(nums); nums.pop_back(); print(nums); nums.pop_front(); print(nums); return 0; }

執行結果如下:

這裡寫圖片描述

作者基於circular_buffer會自動彈出元素的特性,做了如下模擬:

1.buffer的元素儲存Bucket,這個Bucket是一個集合,儲存在1秒內所有連線的shared_ptr
2.對buffer進行特定大小的初始化,並用hole填滿
3.當有一個連線的時候,將會把這個連線插入到Bucket裡面
4.每一秒都會往buffer裡面插入空的Bucket
5.這樣基於circular_buffer的特性,現有的連線就會自動往下滾動,這就模擬了輪盤了。

2.智慧指標的使用

這裡作者封裝了一層結構Entry來管理TcpConnection,當circular_buffer將尾部 popback的時候,會依次呼叫其解構函式,並在解構函式主動斷開連線。

當時有兩個疑問:
1.一個是在看原始碼時,每個TcpConnection有一個上下文Context變數儲存Entry的WeakPtr。
所謂上下文,就是變數,因為回撥機制,每個連線都需要有其關聯的Entry,這裡直接用WeakPtr來作為上下文變數,不影響其引用計數。有了上下文,伺服器每當收到客戶端的訊息時(onMessage),可以拿到與該連線關聯的Entry的弱引用,再把它提升到強引用,插入到circular_buffer,這樣就相當於把更新了該連線在時間輪盤裡面的位置了,相應的use_count會加1。

2.一個是執行example時(build/release/bin/idleconnection_echo)每當有新的連線(onConnection),或者有新的訊息(onMessage)時,智慧指標物件的use_count都會先加2,然後再恢復正常的加1。例如圖:

這裡寫圖片描述

括號裡面表示use_count。看程式碼:
EntryPtr entry(new Entry(conn));
/*conn插入時間輪盤*/
connectionBuckets_.back().insert(entry);
dumpConnectionBuckets();

原因找到了,在onConnection回撥中,這裡先建立了一個棧上shared_ptr,之後插入到unordered_set中,這裡就會有引用計數就增加了兩次,之後棧上變數銷燬,引用計數迴歸正常。

寫了一個類似的測試程式:

#include <iostream>
#include <memory>
#include <unordered_set>
using namespace std;
typedef shared_ptr<int> IntPtr;
typedef unordered_set<IntPtr> IntPtrSet;
int main()
{
    IntPtrSet myset;
    {
        IntPtr ptr(new int(42));
        cout<<ptr.use_count()<<endl;
        myset.insert(ptr);
        auto it = myset.begin();
        cout<<it->use_count()<<endl;
    }
    auto it = myset.begin();
    cout<<it->use_count()<<endl;
    return 0;
}
//列印結果
//1
//2
//1

3.參考