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