[译]多线程网络服务模型
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
多線程網(wǎng)絡(luò)服務(wù)模型
/*** 謹(jǐn)獻(xiàn)給Yoyo** 原文出處:https://www.toptal.com/software/guide-to-multi-processing-network-server-models* @author dogstar.huang <chanzonghuang@gmail.com> 2016-04-02*/作為多年來一直在編寫高性能網(wǎng)絡(luò)代碼的人(我的博士論文主題是適配多核系統(tǒng)分布式應(yīng)用的高速緩存服務(wù)),現(xiàn)在我看到了很多完全不知道或忽略討論網(wǎng)絡(luò)服務(wù)模型基本原理的教程。因此,本文旨在希望能為大家提供有用的概覽以及網(wǎng)絡(luò)服務(wù)模型的比較,以揭開編寫高性能網(wǎng)絡(luò)代碼的神秘面紗。
本文主要針對(duì)“系統(tǒng)程序員”,即與他們的應(yīng)用程序的底層細(xì)節(jié)工作、實(shí)現(xiàn)網(wǎng)絡(luò)服務(wù)代碼的后端開發(fā)。這通常是在C++或C來完成,雖然時(shí)下大部分現(xiàn)代語言和框架通過各種級(jí)別的效率提供了體面的底層功能。
既然通過增加內(nèi)核更容易擴(kuò)展CPU,我會(huì)把這作為常識(shí),而本質(zhì)卻是調(diào)整軟件以便最大化使用這些內(nèi)核。因此,問題就變成如何在能在多個(gè)CPU上并行執(zhí)行的線程(或進(jìn)程)中分區(qū)軟件。
我也將理所當(dāng)然地認(rèn)為讀者意識(shí)到,“并發(fā)”基本上意味著“多任務(wù)處理”,即一些代碼實(shí)例(無論是相同或不同的代碼,這并不重要),在同一時(shí)間是活躍的。并發(fā)可以在單個(gè)CPU上實(shí)現(xiàn),并且通常前現(xiàn)代時(shí)期是這樣的。具體地,并發(fā)可以通過在單個(gè)CPU上的多個(gè)進(jìn)程或線程之間快速切換來實(shí)現(xiàn)。這是老式的、單CPU系統(tǒng)如何管理在同一時(shí)間運(yùn)行眾多應(yīng)用程序的方式,在某種程度上,用戶會(huì)覺得應(yīng)用程序是在同時(shí)執(zhí)行,盡管實(shí)際上并沒有。另一方面,平行度,從字面上看具體意味著代碼通過多個(gè)CPU或CPU內(nèi)核在同一時(shí)間執(zhí)行。
分區(qū)應(yīng)用程序(到多個(gè)進(jìn)程或線程)
出于這個(gè)討論的目的,假如我們談?wù)摼€程或全過程,它基本上是不相關(guān)的?,F(xiàn)代操作系統(tǒng)(而Windows顯然是個(gè)例外)把進(jìn)程看待像線程一樣輕量級(jí)(或在某些情況下,反之亦然,線程都獲得了功能,這使得它們像進(jìn)程一樣重量級(jí))。如今,進(jìn)程和線程之間的主要區(qū)別是在跨進(jìn)程或跨線程通信和數(shù)據(jù)共享的功能。其中,進(jìn)程和線程之間的區(qū)別是很重要的,我會(huì)進(jìn)行適當(dāng)?shù)膫渥?#xff0c;否則,在這些部分可以安全地考慮“線程”和“過程”是可以互換的。
通用網(wǎng)絡(luò)應(yīng)用任務(wù)與網(wǎng)絡(luò)服務(wù)模型
這篇文章具體處理網(wǎng)絡(luò)服務(wù)代碼,這部分需要實(shí)現(xiàn)以下三個(gè)任務(wù):
- 任務(wù)#1:建立(和拆除)的網(wǎng)絡(luò)連接
- 任務(wù)#2:網(wǎng)絡(luò)通信(IO)
- 任務(wù)#3:有用的工作;例如負(fù)載或者應(yīng)用程序?yàn)槭裁创嬖诘脑?/li>
關(guān)于跨進(jìn)程分區(qū)分區(qū)這三個(gè)任務(wù),這里有幾個(gè)普遍的網(wǎng)絡(luò)服務(wù)模型,即:
- MP:多進(jìn)程
- SPED:單進(jìn)程,事件驅(qū)動(dòng)
- SEDA:分階段的事件驅(qū)動(dòng)架構(gòu)
- AMPED:非對(duì)稱多進(jìn)程事件驅(qū)動(dòng)
- SYMPED:對(duì)稱多處理事件驅(qū)動(dòng)
這些都是在學(xué)術(shù)界使用的網(wǎng)絡(luò)服務(wù)模型的名字,我記得“在野外”的同義詞發(fā)現(xiàn)至少其中的一些。(名字本身,當(dāng)然,并不是那么重要的 -- 真正的價(jià)值是如何洞悉代碼是怎么回事。)
這些網(wǎng)絡(luò)服務(wù)模型,每一個(gè)都會(huì)在下面的部分中進(jìn)一步說明。
多進(jìn)程(MP)模型
MP的網(wǎng)絡(luò)服務(wù)模型是每個(gè)人都會(huì)首選用來學(xué)習(xí)的一個(gè),特別是學(xué)習(xí)多線程的時(shí)候。在MP模型中,有一個(gè)“master”進(jìn)程,接收連接(任務(wù)#1)。一旦建立了連接,主進(jìn)程創(chuàng)建一個(gè)新的進(jìn)程,并把連接的socket傳給它,所以一個(gè)進(jìn)程一個(gè)連接。這個(gè)新的進(jìn)程然后通常和此連接以簡(jiǎn)單、連續(xù)、鎖步的方式工作:進(jìn)程從連接中讀取一些東西(任務(wù)#2),然后做一些計(jì)算(任務(wù)#3),然后寫一些東西給它(再次 任務(wù)#2)。
模型MP是很容易實(shí)現(xiàn)的,而且實(shí)際工作極為出色只要進(jìn)程總數(shù)維持很低很低。有多低?答案取決于任務(wù)#2和任務(wù)#3蘊(yùn)含了什么。經(jīng)驗(yàn)法則,可以說進(jìn)程數(shù)或線程數(shù)不應(yīng)超過CPU內(nèi)核的兩倍。一旦有在同一時(shí)間激活太多進(jìn)程,操作系統(tǒng)則趨于花費(fèi)了太多在于時(shí)間抖動(dòng)(即,圍繞可用的CPU內(nèi)核上平衡進(jìn)程或線程)和這樣的應(yīng)用通常最終花費(fèi)幾乎所有的CPU的一次在“SYS”(或內(nèi)核)代碼,實(shí)際上卻做了一點(diǎn)點(diǎn)真正有用的工作。
優(yōu)點(diǎn):實(shí)現(xiàn)很簡(jiǎn)單,只要連接數(shù)很少可以工作得非常好。
缺點(diǎn):如果進(jìn)程數(shù)增長(zhǎng)太大則趨于使得操作系統(tǒng)過載過重,并且可能會(huì)有延遲抖動(dòng)網(wǎng)絡(luò)IO等待,直到有效載荷(計(jì)算)階段結(jié)束。
單進(jìn)程事件驅(qū)動(dòng)(SPED)模型
該SPED網(wǎng)絡(luò)服務(wù)器模型,因最近一些高調(diào)的網(wǎng)絡(luò)服務(wù)應(yīng)用程序,如Nginx而出名?;旧?#xff0c;它在同一個(gè)進(jìn)程做了這三項(xiàng)任務(wù),在它們之間之間復(fù)用。為了提高效率,它需要像epoll和kqueue的一些相當(dāng)先進(jìn)的核心功能。在這種模型下,代碼是由傳入的連接和數(shù)據(jù)“事件”驅(qū)動(dòng),并且實(shí)現(xiàn)了一個(gè)看起來像這樣的“事件循環(huán)”:
- 問操作系統(tǒng)是否有任何新的網(wǎng)絡(luò)“事件”(如新的連接或輸入數(shù)據(jù))
- 如果有新的可用連接,建立他們(任務(wù)#1)
- 如果有可用的數(shù)據(jù),讀取它(任務(wù)#2)并對(duì)它采取行動(dòng)(任務(wù)3#)
- 重復(fù),直到服務(wù)器退出
所有這一切都在一個(gè)單一的進(jìn)程中完成,并且可以非常有效地完成,因?yàn)樗耆苊饬诉M(jìn)程之間的上下文切換,這通常會(huì)造成MP模型嚴(yán)重的性能問題。這里唯一的上下文切換來自系統(tǒng)調(diào)用,而這些又通過僅作用于有某些事件綁定的具體連接而使得切換最小化。該模型可以同時(shí)處理數(shù)萬的連接,只要有效載荷工作(任務(wù)#3)不是太復(fù)雜或是資源密集型的。
盡管這種方式有兩大缺點(diǎn):
-
1、由于三個(gè)任務(wù)都在一個(gè)單一的循環(huán)迭代中順序進(jìn)行,有效載荷工作(任務(wù)#3)和所有東西都是同步完成的,也就是說,如果它需要很長(zhǎng)的時(shí)間來計(jì)算到由客戶端接收的數(shù)據(jù)的響應(yīng),當(dāng)正在做這點(diǎn)時(shí)其他東西都會(huì)停止,而這會(huì)在延遲中引入潛在的巨大波動(dòng)。
-
2、只使用一個(gè)CPU內(nèi)核。這樣再次是有好處,絕對(duì)限制了來自操作系統(tǒng)要求的上下文切換數(shù)量,從而提高了整體性能,但有明顯的不足就是其他任何可用的CPU內(nèi)核都無事可做。
這是對(duì)于需要更先進(jìn)的模型的理由。
優(yōu)點(diǎn):可以是具有高性能,在操作系統(tǒng)易于實(shí)現(xiàn)(即,需要最少量的OS干預(yù))。只需要一個(gè)CPU內(nèi)核。
缺點(diǎn):僅利用單個(gè)CPU(不管可用的數(shù)量)。如果有效載荷工作不統(tǒng)一,會(huì)導(dǎo)致非均勻的響應(yīng)延遲。
分階段的事件驅(qū)動(dòng)架構(gòu)(SEDA)模型
該SEDA網(wǎng)絡(luò)服務(wù)模型有點(diǎn)復(fù)雜。它把復(fù)雜的,事件驅(qū)動(dòng)的應(yīng)用程序分解到一組由隊(duì)列連接的階段。盡管如果不仔細(xì)實(shí)現(xiàn),它的性能會(huì)跟MP情況中同一問題而受到影響。它的工作原理是這樣的:
-
有效載荷工作(任務(wù)#3)會(huì)盡可能地分成多個(gè)階段,或模塊。每個(gè)模塊實(shí)現(xiàn)了駐留在其自己?jiǎn)为?dú)的進(jìn)程中單個(gè)特定功能(可認(rèn)為是“微服務(wù)”或“微內(nèi)核”),并且這些模塊經(jīng)由消息隊(duì)列相互通信。此架構(gòu)可以表示為節(jié)點(diǎn)圖,其中節(jié)點(diǎn)是進(jìn)程,邊是消息隊(duì)列。
-
一個(gè)單一進(jìn)程執(zhí)行任務(wù)#1(通常遵循SPED模型),它將新連接交付于特定的條目點(diǎn)節(jié)點(diǎn)。這些節(jié)點(diǎn)可以是傳遞數(shù)據(jù)給其他節(jié)點(diǎn)進(jìn)行計(jì)算,或者也可以是實(shí)現(xiàn)有效載荷處理(任務(wù)3#)的純網(wǎng)絡(luò)節(jié)點(diǎn)(任務(wù)#2)。通常沒有“master”進(jìn)程(例如,一個(gè)收集并聚集響應(yīng),并將其通過連接發(fā)送返回),因?yàn)槊恳粋€(gè)節(jié)點(diǎn)都可以通過自身進(jìn)行響應(yīng)。
理論上,這種模式可以是任意復(fù)雜的,因?yàn)楣?jié)點(diǎn)圖可能具有循環(huán),連接到其他類似的應(yīng)用程序,或是連接到實(shí)際上是在遠(yuǎn)程系統(tǒng)上執(zhí)行的節(jié)點(diǎn)。但在實(shí)踐中,即使有定義良好的消息和高效的隊(duì)列,它會(huì)變得笨拙難以思考,并且把系統(tǒng)的行為作為一個(gè)整體來推理。相比于SPED的模式,來往傳遞的消息可能會(huì)破壞該模型的性能,如果每個(gè)節(jié)點(diǎn)的工作都是很簡(jiǎn)短的話。該模型的效率顯然比SPED模型的要低,所以它通常采用在有效載荷的工作復(fù)雜且耗時(shí)的情況。
優(yōu)點(diǎn):軟件架構(gòu)師最終的夢(mèng)想:一切都分割成整齊而又獨(dú)立的模塊。
缺點(diǎn):復(fù)雜度隨模塊數(shù)量而爆炸,并且消息隊(duì)列仍然比直接內(nèi)存共享慢得多。
非對(duì)稱多進(jìn)程事件驅(qū)動(dòng)(AMPED)模型
該AMPED網(wǎng)絡(luò)服務(wù)是SEDA馴服的,更易于模型的一個(gè)版本。沒有過多不同的模塊和進(jìn)程,也沒有多過的消息隊(duì)列。下面是它如何工作的:
- 以SPED風(fēng)格在一個(gè)單一的“master”進(jìn)程中實(shí)現(xiàn)任務(wù)#1和任務(wù)#2。這是網(wǎng)絡(luò)IO的唯一進(jìn)程。
- 在一個(gè)單獨(dú)的“worker”進(jìn)程中實(shí)現(xiàn)任務(wù)#3(可能在多個(gè)實(shí)例中啟動(dòng)),通過一個(gè)隊(duì)列連接到主進(jìn)程(每個(gè)進(jìn)程一個(gè)隊(duì)列)。
- 當(dāng)“master”進(jìn)程接收到數(shù)據(jù),找到一個(gè)沒有被充分利用(或空閑)的工作進(jìn)程,并把數(shù)據(jù)傳遞給它的消息隊(duì)列。當(dāng)響應(yīng)準(zhǔn)備好時(shí)主進(jìn)程由該進(jìn)程發(fā)起消息通知,此時(shí)它通過連接傳遞響應(yīng)。
這里最重要的是,有效負(fù)載工作是在一個(gè)固定的(通常配置的)數(shù)量的進(jìn)程中進(jìn)行,這獨(dú)立于連接的數(shù)量。這樣的好處是,有效負(fù)載可以是任意復(fù)雜,并且也不會(huì)影響網(wǎng)絡(luò)IO(這是很好的等待時(shí)間)。而且還可能帶來更高的安全性,因?yàn)橹挥幸粋€(gè)進(jìn)程在做網(wǎng)絡(luò)IO。
優(yōu)點(diǎn):網(wǎng)絡(luò)IO和有效載荷的工作分離非常清晰。
缺點(diǎn):為在進(jìn)程之間來回傳遞數(shù)據(jù)利用消息隊(duì)列,而這根據(jù)不同協(xié)議的性質(zhì),可能成為瓶頸。
對(duì)稱多處理事件驅(qū)動(dòng)(SYMPED)模型
該SYMPED網(wǎng)絡(luò)服務(wù)模型在許多方面是網(wǎng)絡(luò)服務(wù)模型的“圣杯”,因?yàn)樗拖裼歇?dú)立SPED“worker”進(jìn)程的多個(gè)實(shí)例。它是通過由單一進(jìn)程循環(huán)接收連接,然后將它們傳遞到工作進(jìn)程得以實(shí)現(xiàn),每一個(gè)都有一個(gè)像SPED的事件循環(huán)。這有一些非常有利的后果:
- CPU都為生成的進(jìn)程的準(zhǔn)確數(shù)量而加載,這在每個(gè)時(shí)間點(diǎn)要么做網(wǎng)絡(luò)IO或有效載荷處理。沒有辦法進(jìn)一步提升CPU利用率。
- 如果連接是獨(dú)立的(例如使用HTTP),在工作進(jìn)程之間則沒有間通信。
事實(shí)上,這一點(diǎn),也是最新版Nginx在做的;它們生產(chǎn)出少量工作進(jìn)程,每個(gè)運(yùn)行一個(gè)事件循環(huán)。為了使事情變得更好,大多數(shù)操作系統(tǒng)都提供了一個(gè)可由多個(gè)進(jìn)程在一個(gè)獨(dú)立的TCP端口偵聽傳入連接的功能,省去了為某個(gè)特定進(jìn)程決定與網(wǎng)絡(luò)連接工作的需要。如果你正在使用的應(yīng)用程序可以通過這種方式來實(shí)現(xiàn),我建議這樣做。
優(yōu)點(diǎn):通過像SPED那樣循環(huán)可控制的數(shù)量,嚴(yán)格提高CPU使用率天花板。
缺點(diǎn):由于每個(gè)過程有一個(gè)像SPED那樣的循環(huán),如果有效載荷工作是不均勻的,等待時(shí)間可以再次變化,就像與正常SPED模型那樣。
一些低級(jí)技巧
除了為您的應(yīng)用選擇最佳的構(gòu)架模型外,這里還有可用于進(jìn)一步提高網(wǎng)絡(luò)代碼性能的一些低級(jí)招數(shù)。下面簡(jiǎn)短列出了一些更有效的技巧:
-
1、避免動(dòng)態(tài)內(nèi)存分配。作為一個(gè)解釋,簡(jiǎn)單地看流行的內(nèi)存分配代碼 - 他們使用復(fù)雜的數(shù)據(jù)結(jié)構(gòu),互斥,并其中只是簡(jiǎn)單地這么多的代碼(例如,jemalloc大概是450KiB左右的C代碼!)。上面大部分的模型可用完全靜態(tài)的(或預(yù)先分配)網(wǎng)絡(luò)和/或僅在需要的地方改變線程之間所有權(quán)緩沖器來實(shí)現(xiàn)。
-
2、使用操作系統(tǒng)可以提供最大值。大多數(shù)操作系統(tǒng)允許多個(gè)進(jìn)程監(jiān)聽一個(gè)單一socket,并在套接字直到接收到第一個(gè)字節(jié)(或甚至是第一個(gè)完整的請(qǐng)求!)時(shí)連接將不被接受時(shí)那里實(shí)現(xiàn)功能收到。如果可以請(qǐng)使用sendfile()。
-
3、了解您正在使用的網(wǎng)絡(luò)協(xié)議!例如,禁用Nagle算法通常是有意義的,并且如果(再)連接率高禁止持續(xù)是有意義的。學(xué)習(xí)TCP擁塞控制算法,看看它是否有意義去嘗試較一個(gè)新的。
在未來的博客文章,我可以更多地談?wù)撨@些,以及其他技術(shù)和實(shí)用的技巧。但現(xiàn)在,這里希望能為編寫高性能網(wǎng)絡(luò)代碼提供關(guān)于的架構(gòu)選擇一個(gè)有用的信息基礎(chǔ),和它們的相對(duì)優(yōu)勢(shì)和劣勢(shì)。
------------------------
- 本作品采用知識(shí)共享署名-非商業(yè)性使用-相同方式共享 3.0 未本地化版本許可協(xié)議進(jìn)行許可。
- 本文翻譯作者為:dogstar,發(fā)表于艾翻譯(itran.cc);歡迎轉(zhuǎn)載,但請(qǐng)注明出處,謝謝!
轉(zhuǎn)載于:https://my.oschina.net/dogstar/blog/759120
總結(jié)
以上是生活随笔為你收集整理的[译]多线程网络服务模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 单片微型计算机第三版课后习题答案,单片微
- 下一篇: [剑指offer]面试题第[35]题[L