關(guān)于高性能高并發(fā)服務(wù)這個概念大家應(yīng)該也都比較熟悉了,今天為大家?guī)砣绾巫鲆粋€高性能高并發(fā)服務(wù)架構(gòu)的實踐和思考力奋。
本次分享主要包括三個部分:
?服務(wù)的瓶頸有哪些
?如何提升整體服務(wù)的性能及并發(fā)?
如何提升單機服務(wù)的性能及并發(fā)?
服務(wù)的瓶頸有哪些
?通常來說程序的定義是算法+數(shù)據(jù)結(jié)構(gòu)+數(shù)據(jù),算法簡單的理解就是一種計算方式秘狞,數(shù)據(jù)結(jié)構(gòu)顧名思義是一種存儲組織數(shù)據(jù)的結(jié)構(gòu)忍疾。
?這兩者體現(xiàn)了程序需要用到的計算機資源问窃,涉及到 CPU 資源、內(nèi)存資源傻寂,而數(shù)據(jù)部分除了內(nèi)存資源息尺,往往還可能涉及到硬盤資源,甚至是彼此之間傳輸數(shù)據(jù)時會消耗網(wǎng)絡(luò)(網(wǎng)卡)資源疾掰。
?當(dāng)我們搞清楚程序運行起來時涉及哪些資源后搂誉,就可以更好地分析我們的服務(wù)中哪些可能是臨界資源。
?所謂臨界資源就是多個進程(線程)并發(fā)訪問某個資源時静檬,該資源同時只能服務(wù)某個或者某些進程(線程)炭懊。
服務(wù)的瓶頸主要就是在這些臨界資源上,還有一些資源原本并不是臨界資源拂檩。
?比如內(nèi)存在一開始是夠的侮腹,但是因為連接數(shù)或者線程數(shù)不斷地增多,最終導(dǎo)致其成為臨界資源广恢,其他的 CPU凯旋、磁盤、網(wǎng)卡其實和內(nèi)存一樣钉迷,在訪問量增大以后一樣都可能會成為瓶頸至非。
所以怎么做到高性能高并發(fā)的服務(wù),簡單地說就是找到服務(wù)的瓶頸糠聪,在合理的范圍內(nèi)盡可能的消除瓶頸或者降低瓶頸帶來的影響荒椭。
?再通俗一點的說就是資源總量不夠就加資源,什么資源不夠就加什么資源舰蟆,同時盡量降低單次訪問的資源消耗趣惠,做到在資源總量一定的情況下有能力支撐更多的訪問狸棍。
?如何提升整體服務(wù)的性能及并發(fā)
數(shù)據(jù)拆分
最典型的一個臨界資源就是數(shù)據(jù)庫,數(shù)據(jù)庫在一個大訪問量的系統(tǒng)中往往是最薄弱的一環(huán)味悄,因為數(shù)據(jù)庫本身的服務(wù)能力是有限的草戈。
?以 MySQL 為例,MySQL 可以支持的并發(fā)連接數(shù)可能也就幾千個侍瑟,假設(shè)是 3000 個唐片,一個服務(wù)對其數(shù)據(jù)庫的并發(fā)訪問如果超過了 3000 個,有部分訪問可能在建立連接的時候就失敗了涨颜。
在這種情況下费韭,需要考慮的是如何將數(shù)據(jù)進行分片,引入多個 MySQL 實例庭瑰,增加資源星持,如圖 1 所示。
???????????????????????????????? 圖1:單數(shù)據(jù)實例改成數(shù)據(jù)庫集群
數(shù)據(jù)庫這個臨界資源通過數(shù)據(jù)拆分的方式弹灭,由原來的一個 MySQL 實例變成了多個 MySQL 實例督暂。
這種情況下數(shù)據(jù)庫資源的整體并發(fā)服務(wù)能力自然提升了,同時由于服務(wù)壓力被分散鲤屡,整個數(shù)據(jù)庫集群表現(xiàn)出來的性能也會比單個數(shù)據(jù)庫實例高很多损痰。
存儲類的解決思路基本是類似的,都是將數(shù)據(jù)拆分酒来,通過引入多個存儲服務(wù)實例提升整體存儲服務(wù)的能力卢未,不管對于 SQL 類的還是 NoSQL 類的或文件存儲系統(tǒng)等都可以采用這個思路。
?服務(wù)拆分
?應(yīng)用程序自身的服務(wù)需要根據(jù)業(yè)務(wù)情況進行合理的細(xì)化堰汉,讓每個服務(wù)只負(fù)責(zé)某一類功能辽社,這個思想和微服務(wù)思想類似。
?一句話就是盡量合理地將服務(wù)拆分翘鸭,同時有一個非常重要的原則是讓拆分以后的同類服務(wù)盡量是無狀態(tài)或弱關(guān)聯(lián)滴铅,這樣就可以很容易進行水平擴展。
如果拆分以后的同類服務(wù)的不同實例之間本身是有一些狀態(tài)引起彼此非常強的依賴就乓,比如彼此要共享一些信息這些信息又會彼此影響汉匙,那這種拆分可能就未必非常的合理,需要結(jié)合業(yè)務(wù)重新進行審視生蚁。
?當(dāng)然生產(chǎn)環(huán)節(jié)上下游拆分以后不同的服務(wù)彼此之間的關(guān)聯(lián)又是另外一種情形噩翠,因為同一個生產(chǎn)環(huán)節(jié)上往往是走完一個服務(wù)環(huán)節(jié)才能進入下一個服務(wù)環(huán)節(jié)。
?相當(dāng)于有多個串行的服務(wù),任何一個環(huán)節(jié)的服務(wù)都有可能出現(xiàn)瓶頸,所以需要拆分以后針對相應(yīng)的服務(wù)進行單獨優(yōu)化惰说,這是拆分以后服務(wù)與服務(wù)之間的關(guān)系 汹押。
假設(shè)各個同類服務(wù)本身是無狀態(tài)或者弱依賴的情況下屯援,針對應(yīng)用服務(wù)進行分析猛们,不同的應(yīng)用服務(wù)不太一樣,但是通常都會涉及到內(nèi)存資源以及計算資源狞洋。
以受內(nèi)存資源限制為例弯淘,一個應(yīng)用服務(wù)能承受的連接數(shù)是有限的(連接數(shù)受限),另外如果涉及上傳下載等大量數(shù)據(jù)傳輸?shù)那闆r徘铝,網(wǎng)絡(luò)資源很快就會成為瓶頸(網(wǎng)卡打滿)耳胎。
?這種情況下最簡單的方式就是同樣的應(yīng)用服務(wù)實例部署多份,達到水平擴展惕它,如圖 2 所示。
???????????????????????????????????????????????????????圖 2:服務(wù)拆分
?實際在真正拆分的時候需要考慮具體的業(yè)務(wù)特點废登,比如像京東主站這種類型的網(wǎng)站淹魄,用戶在訪問的時候除了加載基本信息以外,還有商品圖片信息堡距、價格信息甲锡、庫存信息、購物車信息以及訂單信息羽戒、發(fā)票信息等缤沦。
?以及下單完成以后對應(yīng)的分揀配送等配套的物流服務(wù),這些都可以拆成單獨的服務(wù)易稠,拆分以后各個服務(wù)各司其職也能做更好的優(yōu)化缸废。
?服務(wù)拆分這件事情,打個不是特別恰當(dāng)?shù)谋确绞簧纾秃帽壬蠈W(xué)時都是學(xué)習(xí)企量,但是分了很多的科目,高考的時候要看總分亡电,有些同學(xué)會有偏科的現(xiàn)象届巩,有些科成績好有些科成績差一點。
因為分很多科目所以很容易知道自己哪科是比較強的份乒、哪科是比較弱的恕汇,為了保證總體分?jǐn)?shù)最優(yōu),一般在弱的科目上都需要多花點精力努力提高一下分?jǐn)?shù)或辖,不然總體分?jǐn)?shù)不會太高瘾英。
?服務(wù)拆分也是同樣的道理,拆分以后可以很容易知道哪個服務(wù)是整體服務(wù)的瓶頸孝凌,針對瓶頸服務(wù)再進行重點優(yōu)化就可以比較容易的提升整體服務(wù)的能力方咆。
想要更多的Java視頻資料的同學(xué)請加?? 731661047
?增長服務(wù)鏈路
?在大型的網(wǎng)站服務(wù)方案上,在各種合理拆分以后蟀架,數(shù)據(jù)拆分以及服務(wù)拆分支持?jǐn)U展只是其中的一部分工作瓣赂,之后還要根據(jù)需求看看是否需要引入緩存 CDN 之類的服務(wù)榆骚。
?我把這個叫做增長服務(wù)鏈路,原來直接打到數(shù)據(jù)庫的請求煌集,現(xiàn)在可能變成了先打到緩存再打到數(shù)據(jù)庫妓肢,對整個服務(wù)鏈路長度來說是變長的。
?增長服務(wù)鏈路的原則主要是將越脆弱或者說越容易成為瓶頸的資源(比如數(shù)據(jù)庫)放置在鏈路的越末端苫纤。
?在增長完服務(wù)鏈路之后碉钠,還要盡量的縮短訪問鏈路,比如可以在 CDN 層面就返回的就盡量不要繼續(xù)往下走了卷拘。
如果可以在緩存層面返回的就不要去訪問數(shù)據(jù)庫了喊废,盡可能地讓每次的訪問鏈路變短。
可以一步解決的事情就一步解決栗弟,可以兩步解決的事情就不要走第三步污筷,本質(zhì)上是降低每次訪問的資源消耗,尤其是越到鏈路的末端訪問資源的消耗會越大乍赫。
比如獲取一些產(chǎn)品的圖片信息可以在訪問鏈路的最前端使用 CDN瓣蛀,將訪問盡量擋住。
如果 CDN 上沒有命中雷厂,就繼續(xù)往后端訪問惋增,利用 Nginx 等反向代理將訪問打到相應(yīng)的圖片服務(wù)器上,而圖片服務(wù)器本身又可以針對性的做一些訪問優(yōu)化等改鲫。
?比如像價格等信息比較敏感诈皿,如果有更改可能需要立即生效,需要直接訪問最新的數(shù)據(jù)钩杰,但是如果讓訪問直接打到數(shù)據(jù)庫中纫塌,數(shù)據(jù)庫往往直接就打掛了。
?所以可以考慮在數(shù)據(jù)庫之前引入 Redis 等緩存服務(wù)讲弄,將訪問打到緩存上措左,價格服務(wù)系統(tǒng)本身保證數(shù)據(jù)庫和緩存的強一致,降低對數(shù)據(jù)庫的訪問壓力避除。
在極端情況下怎披,數(shù)據(jù)量雖然不是特別大,幾十臺緩存機器就可以抗住瓶摆,但訪問量可能會非常大凉逛,可以將所有的數(shù)據(jù)都放在緩存中,如果緩存有異常甚至都不用去訪問數(shù)據(jù)庫直接返回訪問失敗即可群井。
?因為在訪問量非常大的情況下状飞,如果緩存掛了,訪問直接打到數(shù)據(jù)庫上,可能瞬間就把數(shù)據(jù)庫打趴下了诬辈。
所以在特定場景下可以考慮將緩存和數(shù)據(jù)庫切開酵使,服務(wù)只訪問緩存,緩存失效重新從數(shù)據(jù)庫中加載數(shù)據(jù)到緩存中再對外服務(wù)也是可以的焙糟,所以在實踐中是可以靈活變通的口渔。
小結(jié)
如何提升整體服務(wù)的性能及并發(fā),一句話概括就是:在合理范圍內(nèi)盡可能的拆分穿撮,拆分以后同類服務(wù)可以通過水平擴展達到整體的高性能高并發(fā)缺脉。
?同時將越脆弱的資源放置在鏈路的越末端,訪問的時候盡量將訪問鏈接縮短悦穿,降低每次訪問的資源消耗攻礼。
?如何提升單機服務(wù)的性能及并發(fā) 前面說的這些情況可以解決大訪問量情況下的高并發(fā)問題,但是高性能最終還是要依賴單臺應(yīng)用的性能咧党。
?如果單臺應(yīng)用性能在低訪問量情況下性能已經(jīng)成渣了秘蛔,那部署再多機器也解決不了問題,所以接下來聊一下單臺服務(wù)本身如果支持高性能高并發(fā)傍衡。
多線程/線程池方式
以 TCP server 為例來展開說明,最簡單的一個 TCP server 代碼负蠕,版本一示例如圖 3 所示蛙埂。
????????????????????????????????????????????????????????圖 3:版本一
?這種方式純粹是一個示例,因為這個 server 啟動以后只能接受一條連接遮糖,也就是只能跟一個客戶端互動绣的,且該連接斷開以后,后續(xù)就連不上了欲账,也就是這個 server 只能服務(wù)一次屡江。
這個當(dāng)然是不行的,于是就有了版本二赛不,如圖 4 所示惩嘉,版本二可以一次接受一條連接,并進行一些交互處理踢故,當(dāng)這條連接全部處理完以后才能繼續(xù)下一條連接文黎。
???????????????????????????????????????????????????? ?圖 4:版本二
這個 server 相當(dāng)于是串行的,沒有并發(fā)可言殿较,所以在版本二的基礎(chǔ)上又演化出了版本三耸峭,如圖 5 所示
???????????????????????????????????????????????????? ?圖 5:版本三
這其實是我們經(jīng)常會接觸到的一種模型,這種模型的特點是每連接每線程淋纲,MySQL 5.5 以前用的就是這種模型劳闹,這種模型的特點是當(dāng)有大量連接的時候會創(chuàng)建大量的線程。
?所以往往需要限制連接總數(shù),如果不做限制可能會出現(xiàn)創(chuàng)建了大量的線程本涕,很快就會將內(nèi)存等資源耗干业汰。
?另一個是當(dāng)出現(xiàn)了大量的線程的時候,操作系統(tǒng)會有大量的 CPU 資源花費在線程間的上下文切換上偏友,導(dǎo)致真正給業(yè)務(wù)提供服務(wù)的 CPU 資源比例反倒很小蔬胯。
同時,考慮到大多數(shù)時候即使有很多連接也并不代表所有的連接在同一個時刻都是活躍的位他,所以版本三又演化出了版本四氛濒,如圖 6 所示。
??????????????????????????????????????????????????? ?圖 6:版本四
版本四的時候是很多的連接共享一個線程池鹅髓,這些線程池里的線程數(shù)是固定的舞竿,這樣就可以做到線程池里的一個線程同時服務(wù)多條連接了,MySQL 5.6 之后采用的就是這種方式窿冯。
在絕大多數(shù)的開發(fā)中骗奖,線程池技術(shù)就已經(jīng)足夠了,但是線程池在充分榨干 CPU 計算資源或者說提供有效計算資源方面并不是最完美的醒串。
以一核的計算資源為例执桌,線程池里假設(shè)有 x 個線程,這 x 個線程會被操作系統(tǒng)依據(jù)具體調(diào)度策略進行調(diào)度芜赌,但是線程上下文切換本身是會消耗一定的 CPU 資源的仰挣。
假設(shè)這部分消耗代價是 w,而實際有效服務(wù)的能力是 c缠沈,那么理論上來說 w+c 就是總的 CPU 實際提供的計算資源膘壶,同時假設(shè)一核 CPU 理論上提供計算資源假設(shè)為 t,這個是固定的洲愤。
所以就會出現(xiàn)一種情況:當(dāng)線程池中線程數(shù)量較少的時候并發(fā)度較低颓芭,w 雖然小了,但是 c 也是比較小的柬赐,也就是 w+c < t亡问,甚至是遠(yuǎn)遠(yuǎn)小于 t,如果線程數(shù)很多躺率,又會出現(xiàn)上下文切換代價太大玛界,即 w 變大了。
雖然 c 也隨之提升了一些悼吱,但因為 t 是固定的慎框,所以 c 的上限值一定是小于 t-w 的,而且隨著 w 越大后添,c 的上限值反倒降低了笨枯,因此使用線程池的時候,線程數(shù)的設(shè)置需要根據(jù)實際情況進行調(diào)整。
基于事件驅(qū)動的模式
多線程(線程池)的方式可以較為方便地進行并發(fā)編程馅精,但是多線程的方式對 CPU 的有效利用率并不是最高的严嗜,真正能夠充分利用 CPU 的編程方式是盡量讓 CPU 一直在工作,同時又盡量避免線程的上下文切換等開銷洲敢。
基于事件驅(qū)動的模式(也稱 I/O 多路復(fù)用)在充分利用 CPU 有效計算能力這件事件上是非常出色的漫玄。
比較典型的有 select/poll/epoll/kevent(這些機制本身之間的優(yōu)劣今天先不展開說明,后續(xù)以 epoll 為例說明)压彭。
這種模式的特點是將要監(jiān)聽的 socket fd 注冊在 epoll 上睦优,等這個描述符可讀事件或者可寫事件就緒了,那么就會觸發(fā)相應(yīng)的讀操作或者寫操作壮不。
?可以簡單地理解為需要 CPU 干活的時候就會告知 CPU 需要做什么事情汗盘,實際使用時示例,如圖 7 所示询一。
?圖 7:epoll 示例
這個事情拿一個經(jīng)典的例子來說明:假如在餐廳就餐隐孽,餐廳里有很多顧客(訪問),每連接每線程的方式相當(dāng)于每個客戶一個服務(wù)員(線程相當(dāng)于一個服務(wù)員)健蕊。
服務(wù)的過程中一個服務(wù)員一直為一個客戶服務(wù)菱阵,那就會出現(xiàn)這個服務(wù)員除了真正提供服務(wù)以外有很大一段時間可能是空閑的,且隨著客戶數(shù)越多服務(wù)員數(shù)量也會越多缩功,可餐廳的容量是有限的送粱。
因為要同時容納相同數(shù)量的服務(wù)員和顧客,所以餐廳服務(wù)顧客的數(shù)量將變成理論容量的 50%掂之。 那這件事件對于老板(老板相當(dāng)于開發(fā)人員,希望可以充分利用 CPU 的計算能力脆丁,也就是在 CPU 計算能力<成本>一定的情況下希望盡量的多做一些事情)來說代價就會很大世舰。
線程池的方式是雇傭固定數(shù)量的服務(wù)員,服務(wù)的時候一個服務(wù)員服務(wù)好幾個客戶槽卫,可以理解為一個服務(wù)員在客戶 A 面前站 1 分鐘跟压,看看 A 客戶是否需要服務(wù)。
如果不需要就到 B 客戶那邊站 1 分鐘歼培,看看 B 客戶是否需要服務(wù)震蒋,以此類推。
這種情況會比之前每個客戶一個服務(wù)員的情況節(jié)省一些成本躲庄,但是還是會出現(xiàn)一些成本上的浪費查剖。
還有一種模式也就是 epoll 的方式,相當(dāng)于服務(wù)員就在總臺等著噪窘,客戶有需要的時候就會在桌上的呼叫器上按一下按鈕表示自己需要服務(wù)笋庄,服務(wù)員每次看一下總臺顯示的信息。
比如一共有 100 個客戶,一次可能有 10 個客戶呼叫直砂,這個服務(wù)員就會過去為這 10 個客戶服務(wù)(假設(shè)服務(wù)每個客戶的時候不會出現(xiàn)停頓且可以在較短的時間內(nèi)處理完)菌仁。
等這個服務(wù)員為這 10 個客戶服務(wù)員完以后再重新回到總臺查看哪些客戶需要服務(wù),依此類推静暂。在這種情況下济丘,可能只需要一個服務(wù)員,而餐廳剩余的空間可以全部給客戶使用洽蛀。
Nginx 服務(wù)器性能非常好摹迷,也能支撐非常多的連接,其網(wǎng)絡(luò)模型使用的就是 epoll 的方式辱士,且在實現(xiàn)的時候采用了多個子進程的方式泪掀。
相當(dāng)于同時有多個 epoll 在工作,充分利用了 CPU 多核的特性颂碘,所以并發(fā)及性能都會比單個 epoll 的方式會有更大的提升异赫。
另外 Redis 緩存服務(wù)器大家應(yīng)該也非常熟悉,用的也是 epoll 的方式头岔,性能也是非常好塔拳。
?通過這些現(xiàn)成的經(jīng)典開源項目,大家就可以直觀地理解基于事件驅(qū)動這一方式在實際生產(chǎn)環(huán)境中的性能是非常高的峡竣,性能提升以后并發(fā)效果一般都會隨之提升靠抑。 但是這種方式在實現(xiàn)的時候是非常考驗編程功底以及邏輯嚴(yán)謹(jǐn)性适掰,換句話編程友好性是非常差的颂碧。
因為一個完整的上下文邏輯會被切成很多片段,比如“客戶端發(fā)送一個命令-服務(wù)器端接收命令進行操作-然后返回結(jié)果”這個過程类浪。
這個過程至少會包括一個可讀事件载城、一個可寫事件》丫停可讀事件诉瓦,簡單地理解就是指這條命令已經(jīng)發(fā)送到服務(wù)器端的 tcp 緩存區(qū)了,服務(wù)器去讀取命令(假設(shè)一次讀取完力细,如果一次讀取的命令不完整睬澡,可能會觸發(fā)多次讀事件)。
服務(wù)器再根據(jù)命令進行操作獲取到結(jié)果眠蚂,同時注冊一個可寫事件到 epoll 上煞聪,等待下一次可寫事件觸發(fā)以后再將結(jié)果發(fā)送出去。
想象一下當(dāng)有很多客戶端同時來訪問時河狐,服務(wù)器就會出現(xiàn)一種情況——一會兒在處理某個客戶端的讀事件米绕,一會兒在處理另外的客戶端的寫事件瑟捣。
?總之都是在做一個完整訪問的上下文中的一個片段,其中任何一個片段有等待或者卡頓都將引起整個程序的阻塞栅干。
?當(dāng)然這個問題在多線程編程時也是同樣是存在的迈套,只不過有時候大家習(xí)慣將線程設(shè)置成多個,有些線程阻塞了碱鳞,但可能其他線程并沒有在同一時刻阻塞桑李。
?所以問題不是特別嚴(yán)重,更嚴(yán)謹(jǐn)?shù)淖龇ㄊ窃诙嗑€程編程時窿给,將線程池的數(shù)量調(diào)整到最小進行測試贵白。
如果確實有卡頓,可以確保程序在最快的時間內(nèi)出現(xiàn)卡頓崩泡,從而快速確認(rèn)邏輯上是否有不足或者缺陷禁荒,確認(rèn)這種卡頓本身是否是正常現(xiàn)象角撞。
語言層提供協(xié)程支持
多線程編程的方式明顯是支持了高并發(fā)呛伴,但因為整個程序線程間上下文調(diào)度可能造成 CPU 的利用率不是那么高,而基于事件驅(qū)動的編程方式效果是非常好的谒所。
但對編程功底要求非常高热康,而且在實現(xiàn)的時候需要花費的時間也是最多的,所以一種比較折中的方式是考慮采用提供協(xié)程支持的語言比如 golang 這種的劣领。
?簡單說就是語言層面抽象出了一種更輕量級的線程姐军,一般稱為協(xié)程,在 golang 里又叫 goroutine尖淘。
這些底層最終也是需要用操作系統(tǒng)的線程去跑奕锌,在 golang 的 runtime 實現(xiàn)時底層用到的操作系統(tǒng)的線程數(shù)量相對會少一點。
?而上層程序里可以跑很多的 goroutine村生,這些 goroutine 會在語言層面進行調(diào)度歇攻,看該由哪個線程來最終執(zhí)行這個 goroutine。
因為 goroutine 之間的切換代價是遠(yuǎn)小于操作系統(tǒng)線程之間的切換代價梆造,而底層用到的操作系統(tǒng)數(shù)量又較少,線程間的上下文切換代價也會大大降低葬毫。
這類語言能比其他語言的多線程方式提供更好的并發(fā)镇辉,因為它將操作系統(tǒng)的線程間切換的代價在語言層面盡可能擠壓到最小,同時編程復(fù)雜度大大降低贴捡,在這類語言中上下文邏輯可以保持連貫忽肛。
因為降低了線程間上下文切換的代價,而 goroutine 之間的切換成本相對來說是遠(yuǎn)遠(yuǎn)小于線程間切換成本烂斋。
?所以 CPU 的有效計算能力相對來說也不會太低屹逛,可以比較容易的獲得了一個高并發(fā)且性能還可以的服務(wù)础废。
小結(jié)
?如何提升單機服務(wù)的性能及并發(fā),如果對性能或者高并發(fā)的要求沒有達到非澈蹦#苛刻的要求评腺,選型的時候基于事件驅(qū)動的方式可以優(yōu)先級降低一點,選擇普通的多線程編程即可(其實多數(shù)場景都可以滿足了)淑掌。
?如果想單機的并發(fā)程度更好一點蒿讥,可以考慮選擇有協(xié)程支持的語言,如果還嫌不夠抛腕,那就將邏輯理順芋绸,考慮采用基于事件驅(qū)動的模式,這個在 C/C++ 里直接用 select/epoll/kevent 等就可以了担敌。
在 Java 里可以考慮采用 NIO 的方式摔敛,而從這點上來說像 golang 這種提供協(xié)程支持的語言一般是不支持在程序?qū)用孀约簩崿F(xiàn)基于事件驅(qū)動的編程方式的。
總結(jié)
其實并沒有一刀切的萬能法則全封,大體原則是根據(jù)實際情況具體問題具體分析马昙,找到服務(wù)瓶頸,資源不夠加資源售貌,盡可能降低每次訪問的資源消耗给猾,整體服務(wù)每個環(huán)節(jié)盡量做到可以水平擴展。
?同時盡量提高單機的有效利用率颂跨,從而確保在扛住整個服務(wù)的同時盡量降低資源消耗成本敢伸。
原文作者:作者:張成遠(yuǎn)來源:51CTO技術(shù)棧
【責(zé)任編輯:武曉燕 TEL:(010)68476606】
分享一張架構(gòu)圖片