單機(jī)高并發(fā)模型設(shè)計(jì)

背景

在微服務(wù)架構(gòu)下寸潦,我們習(xí)慣使用多機(jī)器授账、分布式存儲(chǔ)瘪匿、緩存去支持一個(gè)高并發(fā)的請(qǐng)求模型仔戈,而忽略了單機(jī)高并發(fā)模型是如何工作的撼短。這篇文章通過(guò)解構(gòu)客戶(hù)端與服務(wù)端的建立連接和數(shù)據(jù)傳輸過(guò)程,闡述下如何進(jìn)行單機(jī)高并發(fā)模型設(shè)計(jì)腮考。

經(jīng)典C10K問(wèn)題

如何在一臺(tái)物理機(jī)上同時(shí)服務(wù)10K用戶(hù),及10000個(gè)用戶(hù)踩蔚,對(duì)于java程序員來(lái)說(shuō),這不是什么難事馅闽,使用netty就能構(gòu)建出支持并發(fā)超過(guò)10000的服務(wù)端程序。那么netty是如何實(shí)現(xiàn)的福也?首先我們忘掉netty局骤,從頭開(kāi)始分析暴凑。
每個(gè)用戶(hù)一個(gè)連接峦甩,對(duì)于服務(wù)端就是兩件事

  1. 管理這10000個(gè)連接
  2. 處理10000個(gè)連接的數(shù)據(jù)傳輸

TCP連接與數(shù)據(jù)傳輸

連接建立

我們以常見(jiàn)TCP連接為例现喳。

[圖片上傳失敗...(image-12267d-1657446196326)]

一張很熟悉的圖。這篇重點(diǎn)在服務(wù)端分析拿穴,所以先忽略客戶(hù)端細(xì)節(jié)泣洞。
服務(wù)器端通過(guò)創(chuàng)建socket,bind端口忧风,listen準(zhǔn)備好了默色。最后通過(guò)accept和客戶(hù)端建立連接。得到一個(gè)connectFd,即連接套接字(在Linux都是文件描述符),用來(lái)唯一標(biāo)識(shí)一個(gè)連接腿宰。之后數(shù)據(jù)傳輸都基于這個(gè)呕诉。

數(shù)據(jù)傳輸

[圖片上傳失敗...(image-c9c08f-1657446196326)]

為了進(jìn)行數(shù)據(jù)傳輸,服務(wù)端開(kāi)辟一個(gè)線程處理數(shù)據(jù)吃度。具體過(guò)程如下

  1. select應(yīng)用程序向系統(tǒng)內(nèi)核空間,詢(xún)問(wèn)數(shù)據(jù)是否準(zhǔn)備好(因?yàn)橛写翱诖笮∠拗扑Υ欤皇怯袛?shù)據(jù),就可以讀),數(shù)據(jù)未準(zhǔn)備好椿每,應(yīng)用程序一直阻塞伊者,等待應(yīng)答。

  2. read內(nèi)核判斷數(shù)據(jù)準(zhǔn)備好了间护,將數(shù)據(jù)從內(nèi)核拷貝到應(yīng)用程序亦渗,完成后,成功返回汁尺。

  3. 應(yīng)用程序進(jìn)行decode,業(yè)務(wù)邏輯處理,最后encode法精,再發(fā)送出去,返回給客戶(hù)端

因?yàn)槭且粋€(gè)線程處理一個(gè)連接數(shù)據(jù)痴突,對(duì)應(yīng)的線程模型是這樣

[圖片上傳失敗...(image-47a077-1657446196326)]

多路復(fù)用

阻塞vs非阻塞

因?yàn)橐粋€(gè)連接傳輸搂蜓,一個(gè)線程,需要的線程數(shù)太多辽装,占用的資源比較多帮碰。同時(shí)連接結(jié)束,資源銷(xiāo)毀拾积。又得重新創(chuàng)建連接收毫。所以一個(gè)自然而然的想法是復(fù)用線程。即多個(gè)連接使用同一個(gè)線程殷勘。這樣就引發(fā)一個(gè)問(wèn)題此再,
原本我們進(jìn)行數(shù)據(jù)傳輸?shù)娜肟谔帲嵯僭O(shè)線程正在處理某個(gè)連接的數(shù)據(jù)输拇,但是數(shù)據(jù)又一直沒(méi)有好時(shí),因?yàn)?code>select是阻塞的贤斜,這樣即使其他連接有數(shù)據(jù)可讀策吠,也讀不到。所以不能是阻塞的瘩绒,否則多個(gè)連接沒(méi)法共用一個(gè)線程猴抹。所以必須是非阻塞的。

輪詢(xún) VS 事件通知

改成非阻塞后锁荔,應(yīng)用程序就需要不斷輪詢(xún)內(nèi)核空間蟀给,判斷某個(gè)連接是否ready.

for (connectfd fd:  connectFds) {
    if (fd.ready) {
        process();
    }
}

輪詢(xún)這種方式效率比較低择克,非常耗CPU,所以一種常見(jiàn)的做法就是被調(diào)用方發(fā)事件通知告知調(diào)用方肚邢,而不是調(diào)用方一直輪詢(xún)拭卿。這就是IO多路復(fù)用,一路指的就是標(biāo)準(zhǔn)輸入和連接套接字勺鸦。通過(guò)提前注冊(cè)一批套接字到某個(gè)分組中目木,當(dāng)這個(gè)分組中有任意一個(gè)IO事件時(shí)刽射,就去通知阻塞對(duì)象準(zhǔn)備好了。

select/poll/epoll

IO多路復(fù)用技術(shù)實(shí)現(xiàn)常見(jiàn)有select懈息,poll摹恰。select與poll區(qū)別不大,主要就是poll沒(méi)有最大文件描述符的限制姑宽。

從輪詢(xún)變成事件通知闺阱,使用多路復(fù)用IO優(yōu)化后,雖然應(yīng)用程序不用一直輪詢(xún)內(nèi)核空間了瘦穆。但是收到內(nèi)核空間的事件通知后赊豌,應(yīng)用程序并不知道是哪個(gè)對(duì)應(yīng)的連接的事件,還得遍歷一下

onEvent() {
// 監(jiān)聽(tīng)到事件
    for (connectfd fd:  registerConnectFds) {
        if (fd.ready) {
            process()熙兔;
        }
    }
}

可預(yù)見(jiàn)的,隨著連接數(shù)增加黔姜,耗時(shí)在正比增加蒂萎。相比較與poll返回的是事件個(gè)數(shù),epoll返回是有事件發(fā)生的connectFd數(shù)組纳寂,這樣就避免了應(yīng)用程序的輪詢(xún)泻拦。

onEvent() {
// 監(jiān)聽(tīng)到事件
    for (connectfd fd: readyConnectFds) {
       process();
    }
}

當(dāng)然epoll的高性能不止是這個(gè)腋粥,還有邊緣觸發(fā)(edge-triggered),就不在本篇闡述了架曹。

非阻塞IO+多路復(fù)用整理流程如下:

[圖片上傳失敗...(image-b5192c-1657446196326)]

  1. select應(yīng)用程序向系統(tǒng)內(nèi)核空間,詢(xún)問(wèn)數(shù)據(jù)是否準(zhǔn)備好(因?yàn)橛写翱诖笮∠拗瓢笮郏皇怯袛?shù)據(jù),就可以讀),直接返回罗珍,非阻塞調(diào)用脚粟。

  2. 內(nèi)核空間中有數(shù)據(jù)準(zhǔn)備好了,發(fā)送ready read給應(yīng)用程序

  3. 應(yīng)用程序讀取數(shù)據(jù)通殃,進(jìn)行decode,業(yè)務(wù)邏輯處理,最后encode厕宗,再發(fā)送出去,返回給客戶(hù)端

線程池分工

上面我們主要是通過(guò)非阻塞+多路復(fù)用IO來(lái)解決局部的selectread問(wèn)題曲聂。我們?cè)僦匦率崂硐抡w流程佑惠,看下整個(gè)數(shù)據(jù)處理過(guò)程可以如何進(jìn)行分組齐疙。這個(gè)每個(gè)階段使用不同的線程池來(lái)處理贞奋,提高效率穷绵。
首先事件分兩種

  1. 連接事件accept動(dòng)作來(lái)處理
  2. 傳輸事件 selectread,send 動(dòng)作來(lái)處理勾缭。

連接事件處理流程比較固定俩由,無(wú)額外邏輯癌蚁,不需要進(jìn)一步拆分。傳輸事件 read碘梢,send是相對(duì)比較固定的,每個(gè)連接的處理邏輯相似洽洁,可以放在一個(gè)線程池處理。而具體邏輯decode,logic,encode 各個(gè)連接處理邏輯不同汰翠。整體可以放在一個(gè)線程池處理昭雌。

服務(wù)端拆分成3部分

  1. reactor部分,統(tǒng)一處理事件佛纫,然后根據(jù)類(lèi)型分發(fā)
  2. 連接事件分發(fā)給acceptor总放,數(shù)據(jù)傳輸事件分發(fā)給handler
  3. 如果是數(shù)據(jù)傳輸類(lèi)型,handler read完再交給processorc處理

因?yàn)?,2處理都比較快甥啄,放在線程池處理炬搭,業(yè)務(wù)邏輯放在另外一個(gè)線程池處理穆桂。

以上就是大名鼎鼎的reactor高并發(fā)模型享完。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末有额,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子倒源,更是在濱河造成了極大的恐慌句狼,老刑警劉巖热某,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昔馋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡丘薛,警方通過(guò)查閱死者的電腦和手機(jī)邦危,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)倦蚪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人裁僧,你說(shuō)我怎么就攤上這事慕购。” “怎么了沪悲?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵可训,是天一觀的道長(zhǎng)捶枢。 經(jīng)常有香客問(wèn)我烂叔,道長(zhǎng)固歪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任逢防,我火速辦了婚禮蒲讯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘局嘁。我一直安慰自己晦墙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布但指。 她就那樣靜靜地躺著棋凳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贫橙。 梳的紋絲不亂的頭發(fā)上反粥,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天才顿,我揣著相機(jī)與錄音,去河邊找鬼幅垮。 笑死尾组,一個(gè)胖子當(dāng)著我的面吹牛示弓,可吹牛的內(nèi)容都是我干的呵萨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼囱皿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼嘱腥!你這毒婦竟也來(lái)了拘悦?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤愧驱,失蹤者是張志新(化名)和其女友劉穎椭盏,沒(méi)想到半個(gè)月后吻商,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乌叶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年准浴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捎稚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡葡公,死狀恐怖催什,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒲凶,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布裆悄,位于F島的核電站臂聋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏孩等。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一冰垄、第九天 我趴在偏房一處隱蔽的房頂上張望权她。 院中可真熱鬧,春花似錦蝴罪、人聲如沸步清。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谴轮。三九已至,卻和暖如春第步,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背斩个。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工受啥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滚局。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓藤肢,卻偏偏與公主長(zhǎng)得像太闺,于是被迫代替她去往敵國(guó)和親省骂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子最住,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容