接入層(注①):
????反向代理(注②):
? ??????nginx異步非阻塞(注③)IO密集型(注④),適合做反向代理。
????????apache每個請求獨(dú)占一個線程,適合做webserver。(注⑤)
????????webserver中間件:nginx(c)、OpenResty(lua)、apache、tomcat、jetty、tengine
????負(fù)載均衡:
? ? ????一般互聯(lián)網(wǎng)公司使用DNS做一級負(fù)載均衡(注⑥),后端服務(wù)器用Nginx或LVS(Linux Virtual Server)等作為二級負(fù)載均衡。
????????DNS:DNS服務(wù)商配置,多級緩存,更新慢(注⑦)
????????Nginx:自帶多種負(fù)載均衡策略:輪詢(Round-Robin)、最少連接(Least-connected)、最少耗時(Least-time)、會話保持(session persistence)、自定義hash、權(quán)重。(注⑧)
????限流:(注⑨)
????????簡單計(jì)數(shù)法(注⑩)
????????滑動窗口法(滑動統(tǒng)計(jì)最近一段時間的請求量。精度提升)(注⑾)
????????漏桶算法(漏桶以一定的速度出水)(注⑿)
????????令牌桶算法(系統(tǒng)以恒定的速度向桶里放令牌)(注⒀)
????降級:(注⒁)
????熔斷:(注⒂)
????????快速失敗,不會真正去請求外部資源。
????????異常情況超出閾值進(jìn)入熔斷狀態(tài),服務(wù)調(diào)用不會真正去請求外部資源,快速失敗。減少不穩(wěn)定的外部依賴對核心服務(wù)的影響。
????????當(dāng)熔斷器開關(guān)處于打開狀態(tài),經(jīng)過一段時間后,?熔斷器會自動進(jìn)入半開狀態(tài),這時熔斷器只允許一個請求通過。當(dāng)該請求調(diào)用成功時,熔斷器恢復(fù)到關(guān)閉狀態(tài)。若該請求失敗,熔斷器繼續(xù)保持打開狀態(tài),接下來的請求被禁止通過。
? ??????熔斷開關(guān):服務(wù)的健康狀況?=?請求失敗數(shù)?/?請求總數(shù),通過閾值設(shè)定和滑動窗口控制開關(guān)。
????超時?
????防雪崩?:(注⒃)
????????[容錯組件:Hystrix(netflix,java,封裝了熔斷、限流、隔離、降級等能力)]
注釋:
????注① :接入層
?????????通常把網(wǎng)絡(luò)中直接面向用戶連接或訪問網(wǎng)絡(luò)的部分稱為接入層(接入層目的是允許終端用戶連接到網(wǎng)絡(luò))
????注②:反向代理與正向代理
????????正向代理:客戶端?<一>?代理一>服務(wù)端
????????反向代理:客戶端一>代理?<一>?服務(wù)端
????注③:nginx異步非阻塞
????????Nginx通過I/O多路復(fù)用實(shí)現(xiàn)異步非阻塞 (參考明哥之前的博客《IO復(fù)用(select poll epoll)》)
????????web server剛好屬于網(wǎng)絡(luò)io密集型應(yīng)用,不算是計(jì)算密集型。web server的這種性質(zhì)決定了每個request的大部份時間都消耗在網(wǎng)絡(luò)傳輸中,實(shí)際上花費(fèi)在server機(jī)器上的時間片不多。異步非阻塞,使用epoll,和大量細(xì)節(jié)處的優(yōu)化,這就是Nginx幾個進(jìn)程就解決高并發(fā)的秘密所在。????????? ??
????注④:CPU密集型與IO密集型
????????CPU密集型:硬盤、內(nèi)存性 > CPU。I/O在等CPU
????????IO密集型:CPU性能 > 硬盤、內(nèi)存。CPU在等IO
????????CPU密集型 :需要進(jìn)行大量的計(jì)算的任務(wù),消耗很多CPU資源,比如計(jì)算圓周率、對視頻進(jìn)行高清解碼等。由于主要消耗CPU資源,因此代碼運(yùn)行效率至關(guān)重要。像Python這樣的腳本語言運(yùn)行效率很低,完全不適合計(jì)算密集型任務(wù)。對于計(jì)算密集型任務(wù),最好用C語言編寫。
????????IO密集型:對于IO密集型任務(wù),最合適的語言就是開發(fā)效率最高(代碼量最少)的語言,腳本語言是首選,C語言最差。
????注⑤:Nginx與Apache對于高并發(fā)處理上的區(qū)別
????????對于Apache,每個請求都會獨(dú)占一個工作線程,當(dāng)并發(fā)數(shù)到達(dá)幾千時,就同時有幾千的線程在處理請求了。這對于操作系統(tǒng)來說,占用的內(nèi)存非常大,線程的上下文切換帶來的cpu開銷也很大,性能就難以上去,同時這些開銷是完全沒有意義的。
????????對于Nginx來講,一個進(jìn)程只有一個主線程,通過異步非阻塞的事件處理機(jī)制,實(shí)現(xiàn)了循環(huán)處理多個準(zhǔn)備好的事件,從而實(shí)現(xiàn)輕量級和高并發(fā)
? ? 注⑥:DNS負(fù)載均衡
? ??????對某個域的請求分配在不同機(jī)器上的技術(shù)。

? ??注⑦:DNS負(fù)載均衡還存在一些問題
第一,域名服務(wù)器是一個分布式系統(tǒng),是按照一定的層次結(jié)構(gòu)組織的。當(dāng)用戶將域名解析請求提交給本地的域名服務(wù)器,它會因不能直接解析而向上一級域名服務(wù)器提交,上一級域名服務(wù)器再依次向上提交,直到RR-DNS 域名服務(wù)器把這個域名解析到其中一臺服務(wù)器的IP 地址??梢姡瑥挠脩舻絉R-DNS 間存在多臺域名服務(wù)器,而它們都會緩沖已解析的名字到IP 地址的映射,這會導(dǎo)致該域名服務(wù)器組下所有用戶都會訪問同一Web 服務(wù)器,出現(xiàn)不同Web 服務(wù)器間的負(fù)載不平衡。為了保證在域名服務(wù)器中域名到IP 地址的映射不被長久緩沖,RR-DNS 在域名到IP 地址的映射上設(shè)置一個TTL(Time To Live)值,過了這一段時間,域名服務(wù)器將這個映射從緩沖中淘汰。當(dāng)用戶請求,它會再向上一級域名服務(wù)器提交請求并進(jìn)行重新映射。這就涉及到如何設(shè)置這個TTL值,若這個值太大,在這個TTL 期間,很多請求會被映射到同一臺Web 服務(wù)器上,同樣會導(dǎo)致負(fù)載不平衡。若這個值太小,例如是0,會導(dǎo)致本地域名服務(wù)器頻繁地向RR-DNS提交請求,增加了域名解析的網(wǎng)絡(luò)流量,同樣會使RR-DNS 成為系統(tǒng)中一個新的瓶頸。
第二,用戶機(jī)器會緩沖從名字到IP 地址的映射,而不受TTL 值的影響,用戶的訪問請求會被送到同一臺Web 服務(wù)器上。由于用戶訪問請求的突發(fā)性和訪問方式不同,例如有的人訪問一下就離開了,而有的人訪問可長達(dá)幾個小時,所以各臺服務(wù)器間的負(fù)載仍存在傾斜(Skew)而不能控制。假設(shè)用戶在每個會話中平均請求數(shù)為20,負(fù)載最大的服務(wù)器獲得的請求數(shù)額高于各服務(wù)器平均請求數(shù)的平均比率超過百分之三十。也就是說,當(dāng)TTL 值為0 時,因?yàn)橛脩粼L問的突發(fā)性也會存在著較嚴(yán)重的負(fù)載不平衡。
第三,系統(tǒng)的可靠性和可維護(hù)性不好。若一臺服務(wù)器失效,會導(dǎo)致將域名解析到該服務(wù)器的用戶看到服務(wù)中斷,即使用戶按“Reload”按鈕,也無濟(jì)于事。系統(tǒng)管理員也不能隨時地將一臺服務(wù)器切出服務(wù)進(jìn)行維護(hù),如進(jìn)行操作系統(tǒng)和應(yīng)用軟件升級,這需要修改RR-DNS 服務(wù)器中的IP 地址列表,把該服務(wù)器的IP 地址從中劃掉,然后等上一段時間,等所有域名服務(wù)器將該域名到這臺服務(wù)器的映射淘汰,和所有映射到這臺服務(wù)器的客戶機(jī)不再使用該站點(diǎn)為止。
????????RR-DNS方法只是一個簡單的負(fù)載平衡方案,如果你有更高要求,可以研究LVS集群(IPVS和KTCPVS、TCPHA),實(shí)現(xiàn)基于IP的負(fù)載均衡和基于內(nèi)容的負(fù)載均衡。
? ??注⑧:nginx負(fù)載均衡策略
????????1.輪詢(默認(rèn))——每個請求按時間順序逐一分配到不同的后端服務(wù)器,如果后端服務(wù)器down掉,能自動剔除。
????????2.weight ——指定輪詢幾率,weight和訪問比率成正比,用于后端服務(wù)器性能不均的情況。
????????3. ip_hash ——每個請求按訪問ip的hash結(jié)果分配,這樣每個訪客固定訪問一個后端服務(wù)器。
????????4.backup——其它所有的非backup機(jī)器down或者忙的時候,請求backup機(jī)器。所以這臺機(jī)器壓力會最輕。
????????5.down——表示單前的server暫時不參與負(fù)載
????????6.fair(第三方)按后端服務(wù)器的響應(yīng)時間來分配請求,響應(yīng)時間短的優(yōu)先分配。與weight分配策略類似。
????注⑨:限流
? ??????限流可以認(rèn)為服務(wù)降級的一種,限流就是限制系統(tǒng)的輸入和輸出流量已達(dá)到保護(hù)系統(tǒng)的目的。一般來說系統(tǒng)的吞吐量是可以被測算的,為了保證系統(tǒng)的穩(wěn)定運(yùn)行,一旦達(dá)到的需要限制的閾值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延遲處理,拒絕處理,或者部分拒絕處理等等。
? ???注⑩:簡單計(jì)數(shù)法
? ??????計(jì)數(shù)器是最簡單粗暴的算法。比如某個服務(wù)最多只能每秒鐘處理100個請求。我們可以設(shè)置一個1秒鐘的滑動窗口,窗口中有10個格子,每個格子100毫秒,每100毫秒移動一次,每次移動都需要記錄當(dāng)前服務(wù)請求的次數(shù)。內(nèi)存中需要保存10次的次數(shù)??梢杂脭?shù)據(jù)結(jié)構(gòu)LinkedList來實(shí)現(xiàn)。格子每次移動的時候判斷一次,當(dāng)前訪問次數(shù)和LinkedList中最后一個相差是否超過100,如果超過就需要限流

? ??注⑾:滑動窗口法
? ??????簡單計(jì)數(shù)法算法雖然簡單,但是有一個十分致命的問題,那就是臨界問題
????????假設(shè)有一個惡意用戶,他在900毫秒時,瞬間發(fā)送了10個請求,并且又在1000毫秒時瞬間發(fā)送了10個請求,那么其實(shí)這個用戶在 100毫秒里面,瞬間發(fā)送了20個請求。我們剛才規(guī)定的是1秒最多100個請求,也就是說100毫秒內(nèi)最多10個請求。用戶通過在時間窗口的重置節(jié)點(diǎn)處突發(fā)請求, 可以瞬間超過我們的速率限制。用戶有可能通過算法的這個漏洞,瞬間壓垮我們的應(yīng)用。
????????剛才的問題其實(shí)是因?yàn)槲覀兘y(tǒng)計(jì)的精度太低。那么如何很好地處理這個問題呢?或者說,如何將臨界問題的影響降低呢?

????????比如圖中,我們就將滑動窗口劃成了10格,所以每格代表的是100毫秒。每過100毫秒,我們的時間窗口就會往右滑動一格。每一個格子都有自己獨(dú)立的計(jì)數(shù)器counter,
? ??注⑿:漏桶算法
? ? ? ? 1.一個固定容量的漏桶,按照常量固定速率流出水滴;
? ? ? ? 2.如果桶是空的,則不需流出水滴;
? ? ? ? 3.可以以任意速率流入水滴到漏桶;
? ? ? ? 4.如果流入水滴超出了桶的容量,則流入的水滴溢出了(被丟棄),而漏桶容量是不變的。
????????漏桶算法比較好實(shí)現(xiàn),在單機(jī)系統(tǒng)中可以使用隊(duì)列來實(shí)現(xiàn)(.Net中TPL DataFlow可以較好的處理類似的問題,你可以在這里找到相關(guān)的介紹),在分布式環(huán)境中消息中間件或者Redis都是可選的方案。
? ??注⒀:令牌桶算法
? ? ? ? 1.令牌將按照固定的速率被放入令牌桶中。比如每秒放10個。
? ? ? ? 2.桶中最多存放b個令牌,當(dāng)桶滿時,新添加的令牌被丟棄或拒絕。
? ? ? ? 3.當(dāng)一個n個字節(jié)大小的數(shù)據(jù)包到達(dá),將從桶中刪除n個令牌,接著數(shù)據(jù)包被發(fā)送到網(wǎng)絡(luò)上。
? ? ? ? 4.如果桶中的令牌不足n個,則不會刪除令牌,且該數(shù)據(jù)包將被限流(要么丟棄,要么緩沖區(qū)等待)。
????漏桶和令牌桶的比較
????????令牌桶可以在運(yùn)行時控制和調(diào)整數(shù)據(jù)處理的速率,處理某時的突發(fā)流量。放令牌的頻率增加可以提升整體數(shù)據(jù)處理的速度,而通過每次獲取令牌的個數(shù)增加或者放慢令牌的發(fā)放速度和降低整體數(shù)據(jù)處理速度。而漏桶不行,因?yàn)樗牧鞒鏊俾适枪潭ǖ?,程序處理速度也是固定的?/p>
????????整體而言,令牌桶算法更優(yōu),但是實(shí)現(xiàn)更為復(fù)雜一些。
? ??注⒁:服務(wù)降級
? ??????當(dāng)服務(wù)器壓力劇增的情況下,根據(jù)實(shí)際業(yè)務(wù)情況及流量,對一些服務(wù)和頁面有策略的不處理或換種簡單的方式處理,從而釋放服務(wù)器資源以保證核心交易正常運(yùn)作或高效運(yùn)作。
????????自動降級:
? ??????????超時降級?—— 主要配置好超時時間和超時重試次數(shù)和機(jī)制,并使用異步機(jī)制探測恢復(fù)情況
? ??????????失敗次數(shù)降級?—— 主要是一些不穩(wěn)定的API,當(dāng)失敗調(diào)用次數(shù)達(dá)到一定閥值自動降級,同樣要使用異步機(jī)制探測回復(fù)情況
? ??????????故障降級?—— 如要調(diào)用的遠(yuǎn)程服務(wù)掛掉了(網(wǎng)絡(luò)故障、DNS故障、HTTP服務(wù)返回錯誤的狀態(tài)碼和RPC服務(wù)拋出異常),則可以直接降級
? ??????????限流降級?—— 當(dāng)觸發(fā)了限流超額時,可以使用暫時屏蔽的方式來進(jìn)行短暫的屏蔽
? ? ? ? 例:當(dāng)我們?nèi)ッ霘⒒蛘邠屬徱恍┫拶徤唐窌r,此時可能會因?yàn)樵L問量太大而導(dǎo)致系統(tǒng)崩潰,此時開發(fā)者會使用限流來進(jìn)行限制訪問量,當(dāng)達(dá)到限流閥值,后續(xù)請求會被降級;降級后的處理方案可以是:排隊(duì)頁面(將用戶導(dǎo)流到排隊(duì)頁面等一會重試)、無貨(直接告知用戶沒貨了)、錯誤頁(如活動太火爆了,稍后重試)。
? ???注⒂:服務(wù)熔斷
????????服務(wù)熔斷和電路熔斷是一個道理,如果一條線路電壓過高,保險(xiǎn)絲會熔斷,防止出現(xiàn)火災(zāi),但是過后重啟仍然是可用的。
????????而服務(wù)熔斷則是對于目標(biāo)服務(wù)的請求和調(diào)用大量超時或失敗,這時應(yīng)該熔斷該服務(wù)的所有調(diào)用,并且對于后續(xù)調(diào)用應(yīng)直接返回,從而快速釋放資源,確保在目標(biāo)服務(wù)不可用的這段時間內(nèi),所有對它的調(diào)用都是立即返回,不會阻塞的。再等到目標(biāo)服務(wù)好轉(zhuǎn)后進(jìn)行接口恢復(fù)。
????注⒃:防雪崩
? ? ? ? 雪崩過程

????????上面是一組簡單的服務(wù)依賴關(guān)系A(chǔ),B服務(wù)同時依賴于基礎(chǔ)服務(wù)C,基礎(chǔ)服務(wù)C又調(diào)用了服務(wù)D

????????服務(wù)D是一個輔助類型服務(wù),整個業(yè)務(wù)不依賴于D服務(wù),某天D服務(wù)突然響應(yīng)時間變長,導(dǎo)致了核心服務(wù)C響應(yīng)時間變長,其上請求越積越多,C服務(wù)也出現(xiàn)了響應(yīng)變慢的情況,由于A,B強(qiáng)依賴于服務(wù)C,故而一個無關(guān)緊要的服務(wù)卻影響了整個系統(tǒng)的可用。

????????雪崩是系統(tǒng)中的蝴蝶效應(yīng)導(dǎo)致其發(fā)生的原因多種多樣,有不合理的容量設(shè)計(jì),或者是高并發(fā)下某一個方法響應(yīng)變慢,亦或是某臺機(jī)器的資源耗盡。從源頭上我們無法完全杜絕雪崩源頭的發(fā)生,但是雪崩的根本原因來源于服務(wù)之間的強(qiáng)依賴,所以我們可以提前評估,做好熔斷,隔離,限流。
????Hystrix理論(豪豬):? ??????
????????在一個分布式系統(tǒng)里,許多依賴不可避免的會調(diào)用失敗,比如超時、異常等,如何能夠保證在一個依賴出問題的情況下,不會導(dǎo)致整體服務(wù)失敗,這個就是Hystrix需要做的事情。Hystrix提供了熔斷、隔離、Fallback、cache、監(jiān)控等功能,能夠在一個、或多個依賴同時出現(xiàn)問題時保證系統(tǒng)依然可用。