青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

陳碩的Blog

Muduo 網(wǎng)絡(luò)編程示例之八:用 Timing wheel 踢掉空閑連接

Muduo 網(wǎng)絡(luò)編程示例之八:Timing wheel 踢掉空閑連接

陳碩 (giantchen_AT_gmail)

Blog.csdn.net/Solstice  t.sina.com.cn/giantchen

這是《Muduo 網(wǎng)絡(luò)編程示例》系列的第八篇文章,原計(jì)劃講文件傳輸,這里插入一點(diǎn)計(jì)劃之外的內(nèi)容。

Muduo 全系列文章列表: http://blog.csdn.net/Solstice/category/779646.aspx

本文介紹如何使用 timing wheel 來踢掉空閑的連接,一個(gè)連接如果若干秒沒有收到數(shù)據(jù),就認(rèn)為是空閑連接。

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

 

在嚴(yán)肅的網(wǎng)絡(luò)程序中,應(yīng)用層的心跳協(xié)議是必不可少的。應(yīng)該用心跳消息來判斷對方進(jìn)程是否能正常工作,“踢掉空閑連接”只是一時(shí)權(quán)宜之計(jì)。我這里想順便講講 shared_ptr 和 weak_ptr 的用法。

如果一個(gè)連接連續(xù)幾秒鐘(后文以 8s 為例)內(nèi)沒有收到數(shù)據(jù),就把它斷開,為此有兩種簡單粗暴的做法:

  • 每個(gè)連接保存“最后收到數(shù)據(jù)的時(shí)間 lastReceiveTime”,然后用一個(gè)定時(shí)器,每秒鐘遍歷一遍所有連接,斷開那些 (now - connection.lastReceiveTime) > 8s 的 connection。這種做法全局只有一個(gè) repeated timer,不過每次 timeout 都要檢查全部連接,如果連接數(shù)目比較大(幾千上萬),這一步可能會(huì)比較費(fèi)時(shí)。
  • 每個(gè)連接設(shè)置一個(gè) one-shot timer,超時(shí)定為 8s,在超時(shí)的時(shí)候就斷開本連接。當(dāng)然,每次收到數(shù)據(jù)要去更新 timer。這種做法需要很多個(gè) one-shot timer,會(huì)頻繁地更新 timers。如果連接數(shù)目比較大,可能對 reactor 的 timer queue 造成壓力。

使用 timing wheel 能避免上述兩種做法的缺點(diǎn)。timing wheel 可以翻譯為“時(shí)間輪盤”或“刻度盤”,本文保留英文。

連接超時(shí)不需要精確定時(shí),只要大致 8 秒鐘超時(shí)斷開就行,多一秒少一秒關(guān)系不大。處理連接超時(shí)可以用一個(gè)簡單的數(shù)據(jù)結(jié)構(gòu):8 個(gè)桶組成的循環(huán)隊(duì)列。第一個(gè)桶放下一秒將要超時(shí)的連接,第二個(gè)放下 2 秒將要超時(shí)的連接。每個(gè)連接一收到數(shù)據(jù)就把自己放到第 8 個(gè)桶,然后在每秒鐘的 callback 里把第一個(gè)桶里的連接斷開,把這個(gè)空桶挪到隊(duì)尾。這樣大致可以做到 8 秒鐘沒有數(shù)據(jù)就超時(shí)斷開連接。更重要的是,每次不用檢查全部的 connection,只要檢查第一個(gè)桶里的 connections,相當(dāng)于把任務(wù)分散了。

Timing wheel 原理

《Hashed and hierarchical timing wheels: efficient data structures for implementing a timer facility》這篇論文詳細(xì)比較了實(shí)現(xiàn)定時(shí)器的各種數(shù)據(jù)結(jié)構(gòu),并提出了層次化的 timing wheel 與 hash timing wheel 等新結(jié)構(gòu)。針對本文要解決的問題的特點(diǎn),我們不需要實(shí)現(xiàn)一個(gè)通用的定時(shí)器,只用實(shí)現(xiàn) simple timing wheel 即可。

Simple timing wheel 的基本結(jié)構(gòu)是一個(gè)循環(huán)隊(duì)列,還有一個(gè)指向隊(duì)尾的指針 (tail),這個(gè)指針每秒鐘移動(dòng)一格,就像鐘表上的時(shí)針,timing wheel 由此得名。

以下是某一時(shí)刻 timing wheel 的狀態(tài),格子里的數(shù)字是倒計(jì)時(shí)(與通常的 timing wheel 相反),表示這個(gè)格子(桶子)中的連接的剩余壽命。

wheel1

一秒鐘以后,tail 指針移動(dòng)一格,原來四點(diǎn)鐘方向的格子被清空,其中的連接已被斷開。

wheel2

連接超時(shí)被踢掉的過程

假設(shè)在某個(gè)時(shí)刻,conn 1 到達(dá),把它放到當(dāng)前格子中,它的剩余壽命是 7 秒。此后 conn 1 上沒有收到數(shù)據(jù)。

wheel3

1 秒鐘之后,tail 指向下一個(gè)格子,conn 1 的剩余壽命是 6 秒。

wheel4

又過了幾秒鐘,tail 指向 conn 1 之前的那個(gè)格子,conn 1 即將被斷開。

wheel5

下一秒,tail 重新指向 conn 1 原來所在的格子,清空其中的數(shù)據(jù),斷開 conn 1 連接。

wheel6

連接刷新

如果在斷開 conn 1 之前收到數(shù)據(jù),就把它移到當(dāng)前的格子里。

wheel4

收到數(shù)據(jù),conn 1 的壽命延長為 7 秒。

wheel7

時(shí)間繼續(xù)前進(jìn),conn 1 壽命遞減,不過它已經(jīng)比第一種情況長壽了。

wheel8

多個(gè)連接

timing wheel 中的每個(gè)格子是個(gè) hash set,可以容納不止一個(gè)連接。

比如一開始,conn 1 到達(dá)。

wheel3

隨后,conn 2 到達(dá),這時(shí)候 tail 還沒有移動(dòng),兩個(gè)連接位于同一個(gè)格子中,具有相同的剩余壽命。(下圖中畫成鏈表,代碼中是哈希表。)

wheel9

幾秒鐘之后,conn 1 收到數(shù)據(jù),而 conn 2 一直沒有收到數(shù)據(jù),那么 conn 1 被移到當(dāng)前的格子中。這時(shí) conn 1 的壽命比 conn 2 長。

wheel10

代碼實(shí)現(xiàn)與改進(jìn)

我們用以前多次出現(xiàn)的 EchoServer 來說明具體如何實(shí)現(xiàn) timing wheel。代碼見 http://code.google.com/p/muduo/source/browse/trunk/examples/idleconnection

在具體實(shí)現(xiàn)中,格子里放的不是連接,而是一個(gè)特制的 Entry struct,每個(gè) Entry 包含 TcpConnection 的 weak_ptr。Entry 的析構(gòu)函數(shù)會(huì)判斷連接是否還存在(用 weak_ptr),如果還存在則斷開連接。

數(shù)據(jù)結(jié)構(gòu):

  typedef boost::weak_ptr<muduo::net::TcpConnection> WeakTcpConnectionPtr;
struct Entry : public muduo::copyable
{
Entry(const WeakTcpConnectionPtr& weakConn)
: weakConn_(weakConn)
{
}
~Entry()
{
muduo::net::TcpConnectionPtr conn = weakConn_.lock();
if (conn)
{
conn->shutdown();
}
}
WeakTcpConnectionPtr weakConn_;
};
typedef boost::shared_ptr<Entry> EntryPtr;
typedef boost::weak_ptr<Entry> WeakEntryPtr;
typedef boost::unordered_set<EntryPtr> Bucket;
typedef boost::circular_buffer<Bucket> WeakConnectionList;

在實(shí)現(xiàn)中,為了簡單起見,我們不會(huì)真的把一個(gè)連接從一個(gè)格子移到另一個(gè)格子,而是采用引用計(jì)數(shù)的辦法,用 shared_ptr 來管理 Entry。如果從連接收到數(shù)據(jù),就把對應(yīng)的 EntryPtr 放到這個(gè)格子里,這樣它的引用計(jì)數(shù)就遞增了。當(dāng) Entry 的引用計(jì)數(shù)遞減到零,說明它沒有在任何一個(gè)格子里出現(xiàn),那么連接超時(shí),Entry 的析構(gòu)函數(shù)會(huì)斷開連接。

Timing wheel 用 boost::circular_buffer 實(shí)現(xiàn),其中每個(gè) Bucket 元素是個(gè) hash set of EntryPtr。

 

在構(gòu)造函數(shù)中,注冊每秒鐘的回調(diào)(EventLoop::runEvery() 注冊 EchoServer::onTimer() ),然后把 timing wheel 設(shè)為適當(dāng)?shù)拇笮 ?/p>

EchoServer::EchoServer(EventLoop* loop,
const InetAddress& listenAddr,
int idleSeconds)
: loop_(loop),
server_(loop, listenAddr, "EchoServer"),
connectionBuckets_(idleSeconds)
{
server_.setConnectionCallback(
boost::bind(&EchoServer::onConnection, this, _1));
server_.setMessageCallback(
boost::bind(&EchoServer::onMessage, this, _1, _2, _3));
loop->runEvery(1.0, boost::bind(&EchoServer::onTimer, this));
connectionBuckets_.resize(idleSeconds);
}

其中 EchoServer::onTimer() 的實(shí)現(xiàn)只有一行:往隊(duì)尾添加一個(gè)空的 Bucket,這樣 circular_buffer 會(huì)自動(dòng)彈出隊(duì)首的 Bucket,并析構(gòu)之。在析構(gòu) Bucket 的時(shí)候,會(huì)依次析構(gòu)其中的 EntryPtr 對象,這樣 Entry 的引用計(jì)數(shù)就不用我們?nèi)ゲ傩模珻++ 的值語意會(huì)幫我們搞定一切。

void EchoServer::onTimer()
{
connectionBuckets_.push_back(Bucket());
}

在連接建立時(shí),創(chuàng)建一個(gè) Entry 對象,把它放到 timing wheel 的隊(duì)尾。另外,我們還需要把 Entry 的弱引用保存到 TcpConnection 的 context 里,因?yàn)樵谑盏綌?shù)據(jù)的時(shí)候還要用到 Entry。(思考題:如果 TcpConnection::setContext 保存的是強(qiáng)引用 EntryPtr,會(huì)出現(xiàn)什么情況?)

void EchoServer::onConnection(const TcpConnectionPtr& conn)
{
LOG_INFO << "EchoServer - " << conn->peerAddress().toHostPort() << " -> "
<< conn->localAddress().toHostPort() << " is "
<< (conn->connected() ? "UP" : "DOWN");
if (conn->connected())
{
EntryPtr entry(new Entry(conn));
connectionBuckets_.back().insert(entry);
WeakEntryPtr weakEntry(entry);
conn->setContext(weakEntry);
}
else
{
assert(!conn->getContext().empty());
WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));
LOG_DEBUG << "Entry use_count = " << weakEntry.use_count();
}
}

在收到消息時(shí),從 TcpConnection 的 context 中取出 Entry 的弱引用,把它提升為強(qiáng)引用 EntryPtr,然后放到當(dāng)前的 timing wheel 隊(duì)尾。(思考題,為什么要把 Entry 作為 TcpConnection 的 context 保存,如果這里再創(chuàng)建一個(gè)新的 Entry 會(huì)有什么后果?)

void EchoServer::onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp time)
{
string msg(buf->retrieveAsString());
LOG_INFO << conn->name() << " echo " << msg.size() << " bytes at " << time.toString();
conn->send(msg);
assert(!conn->getContext().empty());
WeakEntryPtr weakEntry(boost::any_cast<WeakEntryPtr>(conn->getContext()));
EntryPtr entry(weakEntry.lock());
if (entry)
{
connectionBuckets_.back().insert(entry);
}
}

然后呢?沒有然后了,程序已經(jīng)完成了我們想要的功能。(完整的代碼會(huì)打印 circular_buffer 變化的情況,運(yùn)行一下即可理解。)

希望本文有助于您理解 shared_ptr 和 weak_ptr。

改進(jìn)

在現(xiàn)在的實(shí)現(xiàn)中,每次收到消息都會(huì)往隊(duì)尾添加 EntryPtr (當(dāng)然,hash set 會(huì)幫我們?nèi)ブ?。)一個(gè)簡單的改進(jìn)措施是,在 TcpConnection 里保存“最后一次往隊(duì)尾添加引用時(shí)的 tail 位置”,然后先檢查 tail 是否變化,若無變化則不重復(fù)添加 EntryPtr。這樣或許能提高效率。

以上改進(jìn)留作練習(xí)。

posted on 2011-05-04 21:19 陳碩 閱讀(4078) 評論(5)  編輯 收藏 引用 所屬分類: muduo

評論

# re: Muduo 網(wǎng)絡(luò)編程示例之八:用 Timing wheel 踢掉空閑連接[未登錄] 2011-05-05 08:01 by

Timing wheel 是個(gè)好東西。
  回復(fù)  更多評論   

# re: Muduo 網(wǎng)絡(luò)編程示例之八:用 Timing wheel 踢掉空閑連接 2011-05-05 10:02 百思寒

Timing wheel 是個(gè)好東西  回復(fù)  更多評論   

# re: Muduo 網(wǎng)絡(luò)編程示例之八:用 Timing wheel 踢掉空閑連接[未登錄] 2011-05-05 15:57 jejer

真是太有才了  回復(fù)  更多評論   

# re: Muduo 網(wǎng)絡(luò)編程示例之八:用 Timing wheel 踢掉空閑連接 2011-05-14 11:21 kzjay

在很多用況下超時(shí)時(shí)間都差不多,而且精確度要求低,俺一般會(huì)直接用最簡單的一兩條鏈表來實(shí)現(xiàn)定時(shí)器。
TimingWhell或有序堆(NGINX)的共同點(diǎn)是超時(shí)時(shí)間可以隨意定義,而且實(shí)現(xiàn)起來比鏈表復(fù)雜。
當(dāng)時(shí)給我掃盲的文章:http://www.ibm.com/developerworks/cn/linux/l-cn-timers/  回復(fù)  更多評論   

# re: Muduo 網(wǎng)絡(luò)編程示例之八:用 Timing wheel 踢掉空閑連接[未登錄] 2012-12-21 17:15 春秋十二月

看了代碼實(shí)現(xiàn),使用引用計(jì)數(shù)和unordered_set,這會(huì)造成每個(gè)桶內(nèi)都可能存在對某相同連接的entry對象,以致空間占用較大,但換來了時(shí)間上的效率,如果保存臨時(shí)tail,則是常數(shù)時(shí)間。如果不用這種方法,而采用連接從所在桶內(nèi)移到tail桶內(nèi),則至少是對數(shù)級的時(shí)間。  回復(fù)  更多評論   

<2010年4月>
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

導(dǎo)航

統(tǒng)計(jì)

常用鏈接

隨筆分類

隨筆檔案

相冊

搜索

最新評論

閱讀排行榜

評論排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            一本久久a久久精品亚洲| 欧美天天视频| 老司机一区二区| 亚洲国产欧美久久| 国产精品高清一区二区三区| 亚洲第一毛片| 欧美在线免费视频| 一本一本a久久| 亚洲激情成人网| 午夜视频一区二区| 久久精品免费看| 欧美精品在线视频观看| 欧美主播一区二区三区美女 久久精品人 | 亚洲美女精品久久| 亚洲精品视频在线看| 欧美激情一区在线观看| 亚洲一线二线三线久久久| 一区精品在线| 香蕉av福利精品导航| 亚洲欧美变态国产另类| 亚洲一区综合| 久久久亚洲综合| 免费不卡在线观看av| 欧美高清hd18日本| 国产九九精品视频| 国内精品久久久久久影视8| 国产日韩一区二区三区| 亚洲国产一二三| 在线视频一区观看| 欧美亚洲一区二区三区| 亚洲高清视频一区| 亚洲精品乱码久久久久久日本蜜臀 | 亚洲在线一区二区| 一区二区欧美亚洲| 一本大道久久a久久综合婷婷| 一道本一区二区| 亚洲欧美日韩国产中文| 欧美77777| 国产精品免费观看视频| 国外视频精品毛片| 亚洲欧洲99久久| 久久九九免费视频| 欧美激情无毛| 亚洲电影毛片| 亚洲视频二区| 亚洲级视频在线观看免费1级| 久久精品亚洲一区二区| 欧美成人蜜桃| 欧美在线一级视频| 免费日韩视频| 欧美美女视频| 看欧美日韩国产| 欧美亚洲在线| 美女黄毛**国产精品啪啪| 亚洲天堂av在线免费| 久久免费高清视频| 国产精品都在这里| 亚洲一级高清| 亚洲国产精品电影在线观看| 亚洲性感美女99在线| 欧美色精品在线视频| 亚洲黄色视屏| 亚洲一区二区影院| 一区二区三区国产盗摄| 美日韩丰满少妇在线观看| 伊人伊人伊人久久| 欧美伊人久久久久久久久影院 | 欧美一区二视频| 国产精品一区二区在线| 亚洲精品国产精品国自产在线 | 日韩一区二区免费高清| 老**午夜毛片一区二区三区| 欧美一级久久| 午夜视频一区二区| 狠狠久久五月精品中文字幕| 99亚洲视频| 亚洲人精品午夜| 亚洲视频视频在线| 欧美日韩亚洲高清| 亚洲愉拍自拍另类高清精品| 亚洲激情成人| 欧美色道久久88综合亚洲精品| 亚洲伊人一本大道中文字幕| 欧美精品一区二| 日韩视频一区二区三区在线播放| 蜜臀99久久精品久久久久久软件| 久久精品噜噜噜成人av农村| 韩国成人福利片在线播放| 99精品热视频只有精品10| 亚洲大胆女人| 久久激情五月婷婷| 欧美成人亚洲成人日韩成人| 国产亚洲一级高清| 国语自产精品视频在线看一大j8| 一区二区高清在线观看| 久久成人国产| 蘑菇福利视频一区播放| av不卡在线观看| av成人免费| 亚洲国产欧美日韩| 久久精品国产亚洲aⅴ| 亚洲国产欧美久久| 亚洲欧美激情四射在线日| 国产日韩欧美另类| 亚洲高清色综合| 国产精品五区| 国产在线拍偷自揄拍精品| 99成人在线| 毛片基地黄久久久久久天堂| 中文一区字幕| 欧美va亚洲va日韩∨a综合色| 有码中文亚洲精品| 欧美激情影音先锋| 国产精品yjizz| 久久精品道一区二区三区| 欧美激情网友自拍| 亚洲高清免费| 国产精品一区久久久久| 久久国产主播精品| 国产精品www网站| 亚洲欧美一区二区视频| 欧美在线看片a免费观看| 蜜臀av性久久久久蜜臀aⅴ四虎| 男人的天堂成人在线| 香蕉亚洲视频| 欧美精品成人| 久久久久一区二区| 国产欧美精品一区二区三区介绍 | 欧美国产亚洲另类动漫| 极品尤物av久久免费看| 国产精品网站视频| 欧美激情综合| 亚洲国产视频直播| 性欧美video另类hd性玩具| 在线视频精品一区| 午夜精品三级视频福利| 99精品欧美一区| 免费国产一区二区| 久久综合久久综合久久| 欧美激情麻豆| 久久综合久久88| 国产欧美精品日韩精品| 日韩亚洲视频| 中文精品视频一区二区在线观看| 久久精品91久久久久久再现| 亚洲影视在线| 国产精品视频一区二区高潮| 亚洲高清毛片| 一区二区三区国产| 免费观看在线综合色| 久久蜜桃资源一区二区老牛| 韩国福利一区| 欧美一区成人| 99视频精品免费观看| 欧美日韩理论| 亚洲欧洲一区二区三区| 在线亚洲+欧美+日本专区| 欧美激情精品久久久久久免费印度| 久久夜色精品| 亚洲欧洲精品一区二区三区波多野1战4| 性色av一区二区三区红粉影视| 亚洲精品三级| 欧美午夜久久久| 日韩一区二区精品| 亚洲欧美精品| 国产欧美日韩专区发布| 亚洲精品1区2区| 久久精品二区| 亚洲国产精品va在看黑人| 欧美日韩成人| 亚洲天堂av高清| 99视频精品全部免费在线| 国产免费成人av| 久久精品一二三区| 亚洲第一视频| 亚洲专区国产精品| 国产视频在线观看一区| 久久日韩粉嫩一区二区三区| 夜夜嗨av色综合久久久综合网| 久久精品在这里| 欧美中文在线观看| 日韩亚洲欧美高清| 国产精品久久一级| 欧美wwwwww| 这里只有精品在线播放| 亚洲日本成人女熟在线观看| 国产精品一区二区三区成人| 久久精品女人| 亚洲中字在线| 欧美国产视频在线观看| 亚洲国产精品高清久久久| 国产精品免费一区二区三区观看| 午夜精品免费在线| 日韩视频免费| 美日韩免费视频| 亚洲一区二区3| 日韩一级黄色大片| 国产亚洲a∨片在线观看| 国产精品扒开腿做爽爽爽视频| 久久精品99无色码中文字幕 |