英文版原名:Release It! Design and Depoly Producation-Ready Software
不太習(xí)慣這本書的翻譯梗掰,讀起來(lái)令人略感不適嵌言,:(
總結(jié):
這本書比較全面的介紹了建設(shè)穩(wěn)定系統(tǒng)的反模式與模式桩撮,涵蓋了軟件系統(tǒng)開發(fā)的方方面面侥蒙,當(dāng)讀到一些例子時(shí)能讓人聯(lián)想到工作中遇到的一些故障案例。這些模式與反模式往往是我們?cè)谶M(jìn)行系統(tǒng)的設(shè)計(jì)中容易忽略的稀蟋,我們可能更關(guān)注了功能性設(shè)計(jì)而忽略了一些影響系統(tǒng)穩(wěn)定性的功能設(shè)計(jì)埂陆。架構(gòu)設(shè)計(jì)除了考慮系統(tǒng)開發(fā)過(guò)程的成本苛白,也應(yīng)該考慮后期的運(yùn)維成本,通過(guò)全面的評(píng)估來(lái)進(jìn)行設(shè)計(jì)的決策焚虱,而不是僅僅關(guān)注開發(fā)而導(dǎo)致后期維護(hù)的痛苦购裙。
以下為摘抄及讀書筆記,主要為反模式與模式相關(guān)部分鹃栽。
第1章 生產(chǎn)環(huán)境的生存法則
- 瞄準(zhǔn)正確的目標(biāo):系統(tǒng)的設(shè)計(jì)和構(gòu)建目的不應(yīng)該僅僅是為了通過(guò)QA(quality assurance)的測(cè)試躏率,而是應(yīng)該“為生產(chǎn)環(huán)境而設(shè)計(jì)”,并滿足能以低成本、高質(zhì)量的方式進(jìn)行運(yùn)維工作薇芝。
- 應(yīng)對(duì)不斷擴(kuò)大的挑戰(zhàn)范圍:由于系統(tǒng)用戶的規(guī)模和范圍越來(lái)越大蓬抄,對(duì)系統(tǒng)正常運(yùn)行的時(shí)間要求也提高了。
- 重視運(yùn)維成本:如果不取消或廢止系統(tǒng)夯到,在系統(tǒng)的整個(gè)生命周期中嚷缭,運(yùn)維時(shí)間遠(yuǎn)遠(yuǎn)超過(guò)開發(fā)時(shí)間。如果在做成本決策時(shí)忽視了運(yùn)維成本黄娘,那這為了節(jié)省一次性的開發(fā)成本峭状,卻耗費(fèi)無(wú)盡的運(yùn)維成本。而創(chuàng)建不停機(jī)發(fā)布的構(gòu)建流水線和部署過(guò)程將可以避免停機(jī)發(fā)布(按系統(tǒng)使用5年且每月發(fā)布停機(jī)5分鐘逼争,那系統(tǒng)停機(jī)時(shí)間共計(jì)300分鐘优床,乘以系統(tǒng)的收入則可以計(jì)算出停機(jī)發(fā)布造成的損失)帶來(lái)的損失,而且大有可能提供系統(tǒng)部署頻率誓焦,以便于占領(lǐng)更多的市場(chǎng)份額胆敞。
- 重視早期決策對(duì)系統(tǒng)的長(zhǎng)期影響:雖然敏捷開發(fā)強(qiáng)調(diào)應(yīng)該盡快交付和漸進(jìn)式改進(jìn)以便于軟件能快速上線,但即使在敏捷項(xiàng)目中杂伟,也最好要有遠(yuǎn)見的制定系統(tǒng)架構(gòu)設(shè)計(jì)的決策移层。雖然不同的設(shè)計(jì)方案通常具有相近的實(shí)施成本,但這些方案在整個(gè)軟件生命周期中的總成本截然不同赫粥。因此观话,考慮每個(gè)方案對(duì)系統(tǒng)可用性、系統(tǒng)容量和靈活性的影響至關(guān)重要越平。
- 設(shè)計(jì)務(wù)實(shí)的架構(gòu):務(wù)實(shí)的架構(gòu)設(shè)計(jì)應(yīng)該綜合考慮軟件频蛔、硬件、用戶三者之間的關(guān)系秦叛,并充分考慮系統(tǒng)在后續(xù)的運(yùn)維中的問(wèn)題晦溪,包括部署、監(jiān)控等等挣跋。
第一部分
第2章 案例研究:讓航空公司停飛的代碼異常
本章節(jié)分享了一個(gè)實(shí)際的故障案例的發(fā)現(xiàn)三圆、處理過(guò)程、事后分析避咆,最后找到的原因竟然是一個(gè)未被捕獲的 SQLException 異常舟肉。
雖然當(dāng)我們找到問(wèn)題的原因之后可以比較方便的重現(xiàn)這個(gè)缺陷,但是如果按常規(guī)的測(cè)試用例有辦法全方位的避免這樣的問(wèn)題嗎牌借?很顯然度气,我們沒(méi)辦法把每一個(gè)這樣的缺陷都找出來(lái)。而我們更應(yīng)該關(guān)注的是系統(tǒng)中的一個(gè)缺陷可能會(huì)傳播到所有的其他相關(guān)聯(lián)或者相互依賴的系統(tǒng)中膨报,所以我們需要探討防止這類問(wèn)題蔓延的設(shè)計(jì)模式。
第3章 讓系統(tǒng)穩(wěn)定運(yùn)行
當(dāng)構(gòu)建系統(tǒng)的架構(gòu)、設(shè)計(jì)甚至底層實(shí)現(xiàn)時(shí)现柠,許多決策點(diǎn)對(duì)系統(tǒng)的最終穩(wěn)定性具有很大的影響力院领。而面對(duì)這些決策點(diǎn),高度穩(wěn)定的設(shè)計(jì)與不穩(wěn)定的設(shè)計(jì)投入的成本通常是相同的够吩。
- 什么是穩(wěn)定性:
- 事務(wù):是系統(tǒng)處理的抽象工作單元比然,這與數(shù)據(jù)庫(kù)事務(wù)不同,一個(gè)工作單元可能包含許多數(shù)據(jù)庫(kù)事務(wù)周循、外部系統(tǒng)集成等强法。一個(gè)系統(tǒng)可以是只能處理一種事務(wù)的專用系統(tǒng),也可以是能處理不同種類事務(wù)的組合的混合工作負(fù)載湾笛。
- 系統(tǒng):是指為用戶處理事務(wù)所需的一套完備且相互依賴的硬件饮怯、應(yīng)用程序和服務(wù)。
- 穩(wěn)定性:即使在瞬時(shí)沖擊(對(duì)系統(tǒng)快速施加大量的訪問(wèn)流量)嚎研、持續(xù)壓力(長(zhǎng)時(shí)間持續(xù)地對(duì)系統(tǒng)施加訪問(wèn)流量)或正常處理工作被失效的組件破壞的情況下蓖墅,穩(wěn)健的系統(tǒng)也能夠持續(xù)的處理事務(wù)。這不僅僅是指服務(wù)器或應(yīng)用程序仍能保持運(yùn)行临扮,更多的是指用戶仍然可以完成工作论矾。
- 壽命長(zhǎng)的系統(tǒng)會(huì)長(zhǎng)時(shí)間處理事務(wù),“長(zhǎng)時(shí)間”是指兩次代碼部署的間隔時(shí)間杆勇。
- 系統(tǒng)壽命測(cè)試(書上是“延長(zhǎng)系統(tǒng)壽命”贪壳,?)蚜退。威脅系統(tǒng)壽命的主要敵人是內(nèi)存泄漏和數(shù)據(jù)增長(zhǎng)闰靴。可以通過(guò)負(fù)載測(cè)試工具(JMeter关霸、Marathon或其他)持續(xù)向系統(tǒng)發(fā)送請(qǐng)求來(lái)模擬長(zhǎng)時(shí)間運(yùn)行的場(chǎng)景以便于測(cè)試系統(tǒng)壽命传黄。(也可以每天有幾個(gè)小時(shí)不怎么向系統(tǒng)發(fā)送請(qǐng)求來(lái)模擬半夜低峰時(shí)段)
- 系統(tǒng)失效方式
- 突發(fā)的壓力和過(guò)度的壓力
- 我們需要接受“系統(tǒng)必然失效”這一事實(shí),然后針對(duì)性進(jìn)行相應(yīng)的設(shè)計(jì)队寇。
- 系統(tǒng)失效鏈
- 術(shù)語(yǔ):
- 失誤:軟件出現(xiàn)內(nèi)部錯(cuò)誤膘掰。出現(xiàn)失誤的原因既可能是潛在的軟件缺陷,也可能是在邊界或外部接口處發(fā)生的不受控制的狀況佳遣。
- 錯(cuò)誤:明顯的錯(cuò)誤行為识埋。
- 失效:系統(tǒng)不再響應(yīng)。
- 失誤一旦被觸發(fā)零渐,就會(huì)產(chǎn)生裂紋窒舟。失誤會(huì)變成錯(cuò)誤,錯(cuò)誤會(huì)引發(fā)失效诵盼。這就是裂紋的蔓延方式惠豺。
- 共識(shí):第一银还,失誤總會(huì)發(fā)生,且永遠(yuǎn)無(wú)法杜絕洁墙,必須防止失誤轉(zhuǎn)變?yōu)殄e(cuò)誤蛹疯;第二,即使在盡力防止系統(tǒng)出現(xiàn)失效和錯(cuò)誤時(shí)热监,也必須決定承擔(dān)失效或錯(cuò)誤的風(fēng)險(xiǎn)是否利大于弊捺弦。
- 術(shù)語(yǔ):
第4章 穩(wěn)定性的反模式
一、集成點(diǎn)
集成點(diǎn)是系統(tǒng)的頭號(hào)殺手孝扛。每一個(gè)傳入的連接都存在穩(wěn)定性風(fēng)險(xiǎn)列吼。每個(gè)套接字、進(jìn)程苦始、管道或RPC都會(huì)停止響應(yīng)寞钥。即使是對(duì)數(shù)據(jù)庫(kù)的調(diào)用,也可能會(huì)以明顯而微妙的方式停止響應(yīng)盈简。系統(tǒng)收到的每一份數(shù)據(jù)凑耻,都可能令系統(tǒng)停止響應(yīng)、崩潰柠贤,甚至產(chǎn)生其他的沖擊香浩。
- 套接字協(xié)議
- 基于三次握手的連接的問(wèn)題:a. SYN; b. SYN/ACK; c. ACK
- reset拒絕連接
- 網(wǎng)絡(luò)丟包
- 因各種原因(比如突發(fā)大量連接請(qǐng)求)服務(wù)端監(jiān)聽隊(duì)列滿了
- read()調(diào)用阻塞
- 基于三次握手的連接的問(wèn)題:a. SYN; b. SYN/ACK; c. ACK
- (案例)防火墻對(duì)空閑連接的處理
- 案例中防火墻會(huì)自動(dòng)刪除空閑連接導(dǎo)致JDBC連接失效
- 解決辦法:Oracle數(shù)據(jù)庫(kù)增加無(wú)效連接檢測(cè)主動(dòng)探測(cè)有效客戶端
- HTTP協(xié)議
- 問(wèn)題:
- 服務(wù)提供方可能接受TCP連接,但不會(huì)響應(yīng)HTTP請(qǐng)求
- 服務(wù)提供方可以接受連接但不能讀取請(qǐng)求臼勉。如果請(qǐng)求體很大邻吭,它可能會(huì)填滿服務(wù)提供方的TCP窗口,導(dǎo)致調(diào)用方的TCP緩沖區(qū)一直去填充宴霸,從而阻塞套接字寫操作囱晴。在這種情況下,即使發(fā)送請(qǐng)求也永遠(yuǎn)不會(huì)完成瓢谢。
- 服務(wù)提供方可能返回調(diào)用方不知道如何處理的響應(yīng)狀態(tài)畸写。如:418 我是茶壺;451 資源被刪除
- 服務(wù)提供方返回的響應(yīng)的內(nèi)容類型是調(diào)用方不知道如何處理的
- 服務(wù)提供方可能聲稱要發(fā)送JSON氓扛,但實(shí)際發(fā)送了純文本枯芬,或者是二進(jìn)制文件,或者其他格式文件
- 解決辦法:
- 客戶端對(duì)超時(shí)時(shí)間進(jìn)行細(xì)粒度控制(包括連接超時(shí)時(shí)間和讀取超時(shí)時(shí)間)采郎,并能對(duì)響應(yīng)進(jìn)行處理
- 應(yīng)該先確認(rèn)響應(yīng)內(nèi)容是否符合預(yù)期再處理響應(yīng)內(nèi)容千所,而不是直接把響應(yīng)映射成對(duì)象
- 問(wèn)題:
- 供應(yīng)商的API程序庫(kù)
- 警惕供應(yīng)商的API程序庫(kù)的穩(wěn)定性,包括質(zhì)量蒜埋、風(fēng)格和安全性等方面的不穩(wěn)定性淫痰。---- 注 (現(xiàn)身說(shuō)法):我今年2月份就因?yàn)閲?guó)內(nèi)某大廠提供的sdk的默認(rèn)超時(shí)時(shí)間設(shè)置踩過(guò)坑。
- 阻塞是影響供應(yīng)商API程序庫(kù)穩(wěn)定性的首要問(wèn)題整份,包括內(nèi)部資源池待错、套接字讀取指令籽孙、HTTP連接、Java序列化等
- 所以我們?cè)谑褂霉?yīng)商的API程序庫(kù)前朗鸠,最好能夠獲取到源碼或者反編譯審視一下里面的實(shí)現(xiàn)
- 總結(jié)
- 每個(gè)集成點(diǎn)最終都會(huì)以某種方式發(fā)生系統(tǒng)失效蚯撩,所以需要為系統(tǒng)失效做好準(zhǔn)備
- 為各種形式的系統(tǒng)失效做好準(zhǔn)備础倍,包括網(wǎng)絡(luò)錯(cuò)誤烛占、語(yǔ)義錯(cuò)誤
- 由于調(diào)試集成點(diǎn)有時(shí)比較困難,所以可以考慮通過(guò)數(shù)據(jù)包嗅探器和其他網(wǎng)絡(luò)診斷工具來(lái)排查問(wèn)題
- 如果系統(tǒng)的代碼缺乏一定的防御性沟启,集成點(diǎn)失效的影響會(huì)迅速蔓延忆家。所以系統(tǒng)代碼需要考慮通過(guò)斷路器、超時(shí)德迹、中間件解耦和握手等模式來(lái)進(jìn)行防御性編程芽卿,以防止集成點(diǎn)出現(xiàn)問(wèn)題
二、同層連累反應(yīng)
這種情況對(duì)應(yīng)于通過(guò)負(fù)載均衡器實(shí)現(xiàn)水平擴(kuò)展的集群胳搞。
當(dāng)負(fù)載均衡組中的一個(gè)節(jié)點(diǎn)發(fā)生故障時(shí)卸例,其他節(jié)點(diǎn)必須額外分擔(dān)該節(jié)點(diǎn)的負(fù)載。
比如一個(gè)8個(gè)節(jié)點(diǎn)的集群肌毅,每個(gè)節(jié)點(diǎn)分擔(dān)12.5%的負(fù)載筷转。
當(dāng)1個(gè)節(jié)點(diǎn)故障時(shí)剩余的7個(gè)節(jié)點(diǎn)每個(gè)節(jié)點(diǎn)需要處理總負(fù)載的14.3%,雖然每個(gè)節(jié)點(diǎn)只增加了1.8%的負(fù)載分擔(dān)悬而,但單節(jié)點(diǎn)的負(fù)載比之前增加了14.4%呜舒。
當(dāng)2個(gè)節(jié)點(diǎn)故障時(shí)剩余的6個(gè)節(jié)點(diǎn)每個(gè)節(jié)點(diǎn)需要處理總負(fù)載的16.7%,雖然每個(gè)節(jié)點(diǎn)只增加了4.2%的負(fù)載分擔(dān)笨奠,但單節(jié)點(diǎn)的負(fù)載比之前增加了33.3%袭蝗。
- 一臺(tái)服務(wù)器的停機(jī)會(huì)波及其余的服務(wù)器
- 一臺(tái)服務(wù)器的內(nèi)存泄漏的停機(jī)會(huì)導(dǎo)致其他服務(wù)器的負(fù)載增加,增加的負(fù)載又會(huì)加速其他服務(wù)的內(nèi)存泄露速度
- 一臺(tái)服務(wù)器陷入死鎖般婆,其他服務(wù)器所增加的負(fù)載會(huì)導(dǎo)致它們也更容易陷入死鎖
- (措施)采用自動(dòng)擴(kuò)展到腥。應(yīng)該為云端每個(gè)自動(dòng)擴(kuò)展組創(chuàng)建健康檢查機(jī)制。自動(dòng)擴(kuò)展將關(guān)閉未通過(guò)健康狀況檢查的服務(wù)器實(shí)例蔚袍,并啟動(dòng)新的實(shí)例乡范。只要自動(dòng)擴(kuò)展機(jī)制的響應(yīng)速度比同層連累反應(yīng)的蔓延速度快,那么系統(tǒng)服務(wù)就依然可用页响。
- 利用“艙壁模式”進(jìn)行保護(hù)
三篓足、層疊失效
原因:
- 內(nèi)存泄露
- 某個(gè)組件超負(fù)荷運(yùn)行
- 依賴的數(shù)據(jù)庫(kù)失效
- 等等
層疊失效有一個(gè)將系統(tǒng)失效從一個(gè)層級(jí)傳到另一個(gè)層級(jí)的機(jī)制。
層疊失效通常源于枯竭的資源池闰蚕。資源池枯竭的原因往往是較低層級(jí)所發(fā)生的系統(tǒng)失效栈拖。沒(méi)有設(shè)置超時(shí)時(shí)間的集成點(diǎn),必定會(huì)導(dǎo)致層疊失效没陡。
正如集成點(diǎn)是裂紋的頭號(hào)來(lái)源涩哟,層疊失效是裂紋的頭號(hào)加速器索赏。防止發(fā)生層疊失效,是保障系統(tǒng)韌性的關(guān)鍵贴彼。斷路器和超時(shí)是克服層疊失效最有效的模式潜腻。
總結(jié):
阻止裂紋跨層蔓延。當(dāng)裂紋從一個(gè)系統(tǒng)或?qū)蛹?jí)跳到另一個(gè)系統(tǒng)或?qū)蛹?jí)時(shí)器仗,會(huì)發(fā)生層疊失效融涣。這通常是因?yàn)榧牲c(diǎn)沒(méi)有完善自我防護(hù)措施。較低層級(jí)中的同層連累反應(yīng)也可能引發(fā)層疊失效精钮。一個(gè)系統(tǒng)肯定需要調(diào)用其他系統(tǒng)威鹿,但當(dāng)后者失效時(shí),需要確保前者能夠保持運(yùn)轉(zhuǎn)轨香。
仔細(xì)檢查資源池忽你。層疊失效通常是由枯竭的資源池(例如連接池)所導(dǎo)致的。當(dāng)任何資源調(diào)用都沒(méi)有響應(yīng)時(shí)臂容,資源就會(huì)耗盡科雳。此時(shí)獲得連接的線程會(huì)永遠(yuǎn)阻塞,其他所有等待連接的線程也被阻塞脓杉。安全的資源池糟秘,總是會(huì)限制線程等待資源檢出的時(shí)間。
用超時(shí)模式和斷路器模式實(shí)現(xiàn)保護(hù)丽已。層疊失效在其他系統(tǒng)已經(jīng)出現(xiàn)故障之后發(fā)生蚌堵。斷路器模式通過(guò)避免向已經(jīng)陷入困境的集成點(diǎn)發(fā)出調(diào)用請(qǐng)求,進(jìn)而保護(hù)系統(tǒng)沛婴。使用超時(shí)模式吼畏,可以確保對(duì)有問(wèn)題的集成點(diǎn)的調(diào)用能及時(shí)返回。
四嘁灯、用戶
一泻蚊、網(wǎng)絡(luò)流量
-
堆內(nèi)存:如果用戶的會(huì)話保存在內(nèi)存中的話,那么每增加一個(gè)額外的用戶丑婿,就會(huì)增加更多的內(nèi)存性雄。
解決辦法:
盡可能少保留內(nèi)存中的會(huì)話
-
使用弱引用(用弱引用完成這些操作。這種做法在不同程序庫(kù)中的叫法不同羹奉,比如在C#中叫System.WeakReference秒旋,在Java中叫java.lang.ref.SoftReference,在Python中叫weakref)诀拭。其基本思想是迁筛,在垃圾收集器需要回收內(nèi)存之前,弱引用都可以持有另一個(gè)對(duì)象耕挨,后者稱為前者的有效載荷细卧。當(dāng)該對(duì)象的引用只剩下軟引用[插圖]時(shí)尉桩,則軟引用就可以被回收。
當(dāng)內(nèi)存不足時(shí)贪庙,垃圾收集器可以回收任何弱可達(dá)對(duì)象[插圖]蜘犁。換句話說(shuō),如果對(duì)象不存在強(qiáng)引用止邮,那么有效載荷就可以被回收这橙。關(guān)于何時(shí)回收弱可達(dá)對(duì)象、回收多少這樣的對(duì)象农尖,以及可以釋放出多少內(nèi)存析恋,這些都完全由垃圾收集器決定。必須非常仔細(xì)地閱讀編程語(yǔ)言系統(tǒng)運(yùn)行時(shí)的文檔盛卡,但通常唯一的保證,是在發(fā)生內(nèi)存不足錯(cuò)誤之前筑凫,弱可達(dá)對(duì)象都可以回收滑沧。
堆外內(nèi)存和主機(jī)外內(nèi)存
redis、memcache服務(wù)器上的套接字?jǐn)?shù)量
一般人可能不太會(huì)關(guān)注服務(wù)器上的套接字?jǐn)?shù)量巍实。但在流量過(guò)大時(shí)滓技,這也可能會(huì)造成限制。每個(gè)處于活動(dòng)狀態(tài)的請(qǐng)求都對(duì)應(yīng)著一個(gè)開放式套接字棚潦,操作系統(tǒng)會(huì)將進(jìn)入的連接令漂,分配給代表連接接收端的“臨時(shí)”端口。如果查看TCP數(shù)據(jù)包的格式丸边,就能看到端口號(hào)長(zhǎng)16位叠必,這表示端口號(hào)最大只能到65535。不同的操作系統(tǒng)會(huì)對(duì)臨時(shí)套接字使用不同的端口范圍妹窖,但互聯(lián)網(wǎng)數(shù)字分配機(jī)構(gòu)的建議范圍是49152~65535纬朝。這樣一來(lái),服務(wù)器最多可以打開16383個(gè)連接骄呼。但是機(jī)器可能用來(lái)處理專門的服務(wù)共苛,而不是如用戶登錄這樣的通用服務(wù),所以可以將端口范圍擴(kuò)展為1024~65535蜓萄,這樣最多可以有64511個(gè)連接隅茎。
然而,一些服務(wù)器能夠處理100多萬(wàn)個(gè)并發(fā)連接嫉沽。有人會(huì)把上千萬(wàn)的連接推給單獨(dú)一臺(tái)服務(wù)器辟犀。
如果只有64511個(gè)端口可用于連接,那么一臺(tái)服務(wù)器如何能有100萬(wàn)個(gè)連接耻蛇?秘訣在于虛擬IP地址踪蹬。操作系統(tǒng)將多個(gè)IP地址綁定到同一個(gè)網(wǎng)絡(luò)接口胞此。每個(gè)IP地址都有自己的端口號(hào)范圍,所以要處理上百萬(wàn)個(gè)連接跃捣,總共只需要16個(gè)IP地址漱牵。
這不是一個(gè)容易解決的問(wèn)題。應(yīng)用程序可能需要進(jìn)行一些更改疚漆,從而監(jiān)聽多個(gè)IP地址酣胀,處理它們之間的連接,并且保證所有監(jiān)聽隊(duì)列的正常運(yùn)行娶聘。100萬(wàn)個(gè)連接也需要很多內(nèi)核緩沖區(qū)闻镶。此時(shí)需要花一些時(shí)間,了解操作系統(tǒng)的TCP調(diào)優(yōu)參數(shù)丸升。已關(guān)閉的套接字
TIME_WAIT
二铆农、 難伺候的用戶
—— 真正下單的用戶,這些用戶相對(duì)而言涉及的事務(wù)處理更多更復(fù)雜(從下單到結(jié)算等等)狡耻,所以需要關(guān)注這些用戶相關(guān)的操作墩剖,因?yàn)樗麄儠r(shí)網(wǎng)站創(chuàng)造收入的來(lái)源。
通常零售系統(tǒng)的轉(zhuǎn)化率為2%左右夷狰,所以我們可以按4%或者6%或者10%進(jìn)行系統(tǒng)負(fù)載測(cè)試岭皂。
三、不受歡迎的用戶
可能是爬蟲之類的程序產(chǎn)生的請(qǐng)求沼头,這些請(qǐng)求可能會(huì)對(duì)系統(tǒng)產(chǎn)生較大的負(fù)載爷绘。
解決辦法:
- 使用技術(shù)手段識(shí)別并屏蔽這種請(qǐng)求(一些CDN廠商就會(huì)提供這種服務(wù)),或者在對(duì)外的防火墻上執(zhí)行屏蔽进倍,最好可以讓被屏蔽的IP定期過(guò)期
- 法律手段:為網(wǎng)站寫一些使用條款土至,聲明用戶僅能以個(gè)人或非商業(yè)用途查看網(wǎng)站內(nèi)容。
四背捌、不受歡迎的用戶
惡意攻擊的用戶毙籽,即黑客
五、線程阻塞
● 線程阻塞反模式是大多數(shù)系統(tǒng)失效的直接原因毡庆。應(yīng)用程序的失效大多與線程阻塞相關(guān)坑赡。系統(tǒng)失效形式包括常見的系統(tǒng)逐漸變慢和服務(wù)器停止響應(yīng)。線程阻塞反模式會(huì)導(dǎo)致同層連累反應(yīng)和層疊失效么抗。
● 仔細(xì)檢查資源池毅否。就像層疊失效一樣,線程阻塞反模式通常發(fā)生在資源池(特別是數(shù)據(jù)庫(kù)連接池)周圍蝇刀。數(shù)據(jù)庫(kù)內(nèi)發(fā)生死鎖以及不當(dāng)?shù)漠惓L幚矸绞綍?huì)造成連接永久丟失螟加。
● 使用經(jīng)過(guò)驗(yàn)證的元操作。學(xué)習(xí)并使用安全的元操作。形成自己的生產(chǎn)者-消費(fèi)者隊(duì)列看似很容易捆探,但事實(shí)并非如此然爆。相比新形成的隊(duì)列系統(tǒng),任何并發(fā)實(shí)用程序庫(kù)都執(zhí)行了多重測(cè)試黍图。
● 使用超時(shí)模式進(jìn)行保護(hù)曾雕。雖然無(wú)法證明代碼不會(huì)發(fā)生死鎖,但可以確保死鎖不會(huì)一直持續(xù)下去助被。避免函數(shù)調(diào)用中的無(wú)限等待剖张,使用需要超時(shí)參數(shù)的函數(shù)版本。即使意味著需要更多的錯(cuò)誤處理代碼揩环,調(diào)用過(guò)程中也要始終使用超時(shí)模式搔弄。
● 小心那些看不到的代碼。所有的問(wèn)題都可能潛伏在第三方代碼的陰影中丰滑。要非常謹(jǐn)慎顾犹,自行測(cè)試一下。只要有可能吨枉,獲取并研究一下第三方代碼庫(kù)的源代碼蹦渣,了解那些出人意料的代碼和系統(tǒng)失效方式。出于這個(gè)原因貌亭,相比閉源程序庫(kù),大家可能更喜歡開源程序庫(kù)认臊。
六圃庭、自黑式攻擊
自黑式攻擊的典型例子,是從公司市場(chǎng)部發(fā)出的致“精選用戶組”的一份郵件失晴。該郵件包含一些特權(quán)或優(yōu)惠信息剧腻,其復(fù)制速度比“庫(kù)娃”木馬病毒(或者是歲數(shù)稍大的人所熟悉的“莫里斯”蠕蟲)快得多。任何限一萬(wàn)名用戶享受的特別優(yōu)惠涂屁,都可以吸引數(shù)百萬(wàn)的用戶书在。網(wǎng)絡(luò)比價(jià)社區(qū),會(huì)以毫秒為單位的速度檢測(cè)并分享可重復(fù)使用的優(yōu)惠碼拆又。
● 保持溝通渠道暢通儒旬。自黑式攻擊發(fā)源于組織內(nèi)部,人們通過(guò)制造自己的“快閃族”行為和流量高峰帖族,加重對(duì)系統(tǒng)本身的傷害栈源。這時(shí),可以同時(shí)幫助和支持這些營(yíng)銷工作并保護(hù)系統(tǒng)竖般,但前提是要知道將會(huì)發(fā)生的事情。確保沒(méi)有人發(fā)送大量帶有深層鏈接的電子郵件,可以將大量的電子郵件分批陸續(xù)發(fā)送熏矿,分散高峰負(fù)載。針對(duì)首次點(diǎn)擊優(yōu)惠界面的操作闭翩,創(chuàng)建一些靜態(tài)頁(yè)面作為“登陸區(qū)域”。注意防范URL中嵌入的會(huì)話ID迄埃。
● 保護(hù)共享資源疗韵。當(dāng)流量激增時(shí),編程錯(cuò)誤调俘、意外的放大效應(yīng)和共享資源都會(huì)產(chǎn)生風(fēng)險(xiǎn)伶棒。注意“搏擊俱樂(lè)部”軟件缺陷,此時(shí)前端負(fù)載的增加彩库,會(huì)導(dǎo)致后端處理量呈指數(shù)級(jí)增長(zhǎng)肤无。
● 快速地重新分配實(shí)惠的優(yōu)惠。任何一個(gè)認(rèn)為能限量發(fā)布特惠商品的人骇钦,都在自找麻煩宛渐。根本就沒(méi)有限量分配這回事。即使限制了一個(gè)超劃算的特惠商品可以購(gòu)買的次數(shù)眯搭,系統(tǒng)仍然會(huì)崩潰窥翩。
七、放大效應(yīng)
生物學(xué)中的平方-立方定律解釋了為什么永遠(yuǎn)不會(huì)看到像大象一樣大的蜘蛛鳞仙。蟲子的重量隨著體積增加而增加寇蚊,符合時(shí)間復(fù)雜度O(n3)。蟲子的腿部力量會(huì)隨腿的橫截面積的增加而增加棍好,符合時(shí)間復(fù)雜度O(n2)仗岸。如果讓蟲子增大為原來(lái)的10倍,那么變大后的蟲子的“力量與體重”之比就會(huì)變成原來(lái)的1/10借笙。此時(shí)扒怖,蟲子的腿根本支撐不了10倍大的個(gè)頭。
我們總會(huì)遇到這樣的放大效應(yīng)业稼。當(dāng)存在“多對(duì)一”或“多對(duì)少”的關(guān)系時(shí)盗痒,如果這個(gè)關(guān)系中一方的規(guī)模增大,另一方就會(huì)受到放大效應(yīng)的影響低散。例如當(dāng)1臺(tái)數(shù)據(jù)庫(kù)服務(wù)器被10臺(tái)機(jī)器調(diào)用時(shí)俯邓,可以很好地運(yùn)行。但是當(dāng)把調(diào)用它的機(jī)器數(shù)量再額外添加50臺(tái)時(shí)谦纱,數(shù)據(jù)庫(kù)服務(wù)器就可能會(huì)崩潰看成。
在開發(fā)環(huán)境中,每個(gè)應(yīng)用程序都只在一臺(tái)機(jī)器上運(yùn)行跨嘉。在測(cè)試環(huán)境中川慌,幾乎每個(gè)應(yīng)用程序都只安裝在一兩臺(tái)機(jī)器上吃嘿。然而,當(dāng)?shù)搅松a(chǎn)環(huán)境時(shí)梦重,一些應(yīng)用程序看起來(lái)非常小兑燥,另一些應(yīng)用程序則是中型、大型或超大型的琴拧。由于開發(fā)環(huán)境和測(cè)試環(huán)境的規(guī)模很少會(huì)與生產(chǎn)環(huán)境一致降瞳,因此很難在前兩個(gè)環(huán)境中,看到放大效應(yīng)跳出來(lái)“咬人”蚓胸。
-
點(diǎn)對(duì)點(diǎn)通信
不像開發(fā)環(huán)境或者測(cè)試環(huán)境挣饥,生產(chǎn)環(huán)境的連接的總數(shù),會(huì)以實(shí)例數(shù)量平方的數(shù)量級(jí)上升沛膳。當(dāng)實(shí)例增加到100個(gè)時(shí)扔枫,連接數(shù)會(huì)擴(kuò)展到時(shí)間復(fù)雜度O(n2),這會(huì)讓開發(fā)工程師感到非常痛苦锹安。這是由應(yīng)用程序?qū)嵗龜?shù)量所驅(qū)動(dòng)的乘數(shù)效應(yīng)短荐,雖然根據(jù)系統(tǒng)的最終規(guī)模,O(n2)的擴(kuò)展或許沒(méi)問(wèn)題叹哭,但無(wú)論上述哪種情況忍宋,在系統(tǒng)進(jìn)入生產(chǎn)環(huán)境之前,開發(fā)工程師都應(yīng)該知道這種放大效應(yīng)风罩。
點(diǎn)對(duì)點(diǎn)通信的替代方案:- UDP廣播:廣播能夠應(yīng)對(duì)服務(wù)器數(shù)量的不斷增長(zhǎng)糠排,但它不節(jié)省帶寬。由于服務(wù)器的網(wǎng)卡會(huì)獲取廣播超升,且必須要通知TCP/IP協(xié)議棧乳讥,因此廣播會(huì)讓那些與廣播消息不相關(guān)的服務(wù)器產(chǎn)生一些額外的負(fù)載。
- TCP或者UDP組播:組播只允許相關(guān)的服務(wù)器接收消息廓俭,因此傳送效率更高。
- 發(fā)布-訂閱消息傳遞:發(fā)布-訂閱消息傳遞的效率也較高唉工,即使服務(wù)器在消息發(fā)送的那一刻沒(méi)有監(jiān)聽研乒,也可以收到消息。當(dāng)然淋硝,發(fā)布-訂閱消息傳遞雹熬,通常會(huì)讓基礎(chǔ)設(shè)施成本大增。
- 消息隊(duì)列
-
留意共享資源谣膳。
共享資源會(huì)成為系統(tǒng)的瓶頸竿报、系統(tǒng)容量的約束和系統(tǒng)穩(wěn)定性的威脅。如果系統(tǒng)必須使用某種共享資源继谚,那就好好地對(duì)它進(jìn)行壓力測(cè)試烈菌。另外,當(dāng)共享資源處理速度變慢或發(fā)生死鎖時(shí),要確保其客戶端能繼續(xù)工作芽世。
八挚赊、失衡的系統(tǒng)容量
服務(wù)提供方和服務(wù)調(diào)用方的服務(wù)器的比例往往不好確定一個(gè)絕對(duì)合適的比例,所以對(duì)于服務(wù)的構(gòu)建济瓢,如果不能使之全部滿足前端潛在的壓倒性需求荠割,那么就必須構(gòu)建服務(wù)調(diào)用方和服務(wù)提供方的韌性,從而能夠應(yīng)對(duì)海嘯般襲來(lái)的請(qǐng)求旺矾。對(duì)服務(wù)調(diào)用方來(lái)說(shuō)蔑鹦,當(dāng)響應(yīng)獲取速度變慢或連接被拒絕時(shí),使用斷路器模式有助于緩解下游服務(wù)的壓力箕宙。對(duì)服務(wù)提供方來(lái)說(shuō)嚎朽,可以使用握手和背壓[插圖]通知調(diào)用方,限制調(diào)用方發(fā)送請(qǐng)求的速度扒吁。還可以考慮使用艙壁模式火鼻,為關(guān)鍵服務(wù)的高優(yōu)先級(jí)調(diào)用方預(yù)留系統(tǒng)容量。
● 檢查服務(wù)器和線程的數(shù)量雕崩。在開發(fā)環(huán)境和QA環(huán)境中魁索,系統(tǒng)可能看起來(lái)在一兩臺(tái)服務(wù)器上運(yùn)行,其調(diào)用的其他系統(tǒng)的所有QA環(huán)境也是如此盼铁。然而在生產(chǎn)環(huán)境中粗蔚,比例更有可能是10比1,而不是1比1饶火。要將QA環(huán)境和生產(chǎn)環(huán)境對(duì)照起來(lái)鹏控,檢查前端服務(wù)器與后端服務(wù)器的數(shù)量比,以及兩端所能處理的線程的數(shù)量比肤寝。
● 密切觀察放大效應(yīng)和用戶行為当辐。失衡的系統(tǒng)容量是放大效應(yīng)的特例:關(guān)系中一方的增幅變化大大超過(guò)另一方。季節(jié)性鲤看、市場(chǎng)驅(qū)動(dòng)或宣傳驅(qū)動(dòng)等流量模式的變化缘揪,會(huì)導(dǎo)致前端系統(tǒng)的大量請(qǐng)求涌向后端系統(tǒng)(通常是良性的),就像熱門的社交媒體帖子導(dǎo)致網(wǎng)站流量劇增义桂。
● 實(shí)現(xiàn)QA環(huán)境虛擬化并實(shí)現(xiàn)擴(kuò)展找筝。即使生產(chǎn)環(huán)境的規(guī)模是固定的,也不要僅只使用兩臺(tái)服務(wù)器運(yùn)行QA環(huán)境慷吊。擴(kuò)展測(cè)試環(huán)境袖裕,嘗試在調(diào)用方和服務(wù)提供方的規(guī)模擴(kuò)展到不同比例的環(huán)境中運(yùn)行測(cè)試用例,這些應(yīng)該能夠通過(guò)數(shù)據(jù)中心的自動(dòng)化工具自動(dòng)實(shí)現(xiàn)溉瓶。
● 重視接口的兩側(cè)急鳄。如果所開發(fā)的是后端系統(tǒng)谤民,假如系統(tǒng)突然收到10倍于歷史最高的請(qǐng)求數(shù)量,并且都涌向了系統(tǒng)開銷最大的事務(wù)部分攒岛,將會(huì)發(fā)生什么赖临?系統(tǒng)完全失效了嗎?它是先慢下來(lái)然后又恢復(fù)了正常嗎灾锯?如果所開發(fā)的是前端系統(tǒng)兢榨,那么就需要看一看當(dāng)后端系統(tǒng)在被調(diào)用時(shí)停止了響應(yīng),或變得非常慢的時(shí)候顺饮,前端系統(tǒng)會(huì)發(fā)生什么吵聪。
九、一窩蜂
系統(tǒng)達(dá)到穩(wěn)態(tài)時(shí)的負(fù)載兼雄,會(huì)與系統(tǒng)啟動(dòng)或周期性運(yùn)行的負(fù)載存在明顯不同吟逝。想象一個(gè)應(yīng)用程序服務(wù)器農(nóng)場(chǎng)的啟動(dòng)過(guò)程,每臺(tái)服務(wù)器都需要連接到數(shù)據(jù)庫(kù)赦肋,并加載一定數(shù)量的參考數(shù)據(jù)或種子數(shù)據(jù)块攒。每臺(tái)服務(wù)器的緩存都從空閑狀態(tài)開始,逐漸形成一個(gè)有用的工作集佃乘。到那時(shí)囱井,大多數(shù)HTTP請(qǐng)求會(huì)轉(zhuǎn)換為一個(gè)或多個(gè)數(shù)據(jù)庫(kù)查詢。這意味著當(dāng)應(yīng)用程序啟動(dòng)時(shí)趣避,數(shù)據(jù)庫(kù)上的瞬時(shí)負(fù)載要比運(yùn)行一段時(shí)間后的負(fù)載高得多庞呕。
一堆服務(wù)器一同對(duì)數(shù)據(jù)庫(kù)施加瞬時(shí)負(fù)載,這被稱為“一窩蜂”程帕。
引發(fā)一窩蜂現(xiàn)象的幾種情況如下住练。
? 在代碼升級(jí)和重新運(yùn)行之后,啟動(dòng)多臺(tái)服務(wù)器愁拭。
? 午夜(或任何一個(gè)整點(diǎn)時(shí)間)觸發(fā)cron作業(yè)讲逛。
? 配置管理系統(tǒng)推出變更。
一些配置管理工具允許配置一個(gè)隨機(jī)的“擺動(dòng)”(slew)值岭埠,使各臺(tái)服務(wù)器在稍微不同的時(shí)間點(diǎn)下載配置變更妆绞,從而把一窩蜂分散到幾秒鐘內(nèi)。
當(dāng)一些外部現(xiàn)象引起流量的同步“脈沖”時(shí)枫攀,也可能發(fā)生一窩蜂現(xiàn)象。想象城市街道每個(gè)角落安裝的紅綠燈株茶。當(dāng)綠燈亮?xí)r来涨,人們會(huì)簇?fù)沓扇旱剡^(guò)馬路。因?yàn)槊總€(gè)人的行走速度不同启盛,所以他們會(huì)在一定程度上分散開蹦掐,但下一個(gè)紅綠燈會(huì)再次將他們重新聚成一群技羔。注意系統(tǒng)中阻塞許多線程的所有地方,它們?cè)诘却硞€(gè)線程完成工作卧抗。而當(dāng)這個(gè)狀態(tài)打破時(shí)藤滥,新釋放的線程就會(huì)對(duì)任何接收數(shù)據(jù)包的下游系統(tǒng)施加一窩蜂。
如果虛擬用戶的腳本存在固定等待時(shí)間社裆,則在進(jìn)行負(fù)載測(cè)試時(shí)拙绊,就會(huì)產(chǎn)生流量脈沖。此時(shí)泳秀,腳本中的每個(gè)等待時(shí)間都應(yīng)該附帶一個(gè)小的隨機(jī)時(shí)間增量标沪。
● 一窩蜂所需系統(tǒng)成本過(guò)高,高峰需求無(wú)法處理嗜傅。一窩蜂是對(duì)系統(tǒng)的集中使用金句,相比將峰值流量分散開后所需的系統(tǒng)能力,一窩蜂需要一個(gè)更高的系統(tǒng)容量峰值吕嘀∥ツ● 使用隨機(jī)時(shí)鐘擺動(dòng)以分散需求。不要將所有cron作業(yè)都設(shè)置在午夜或其他任何整點(diǎn)時(shí)間執(zhí)行偶房。用混合的方式設(shè)置時(shí)間趁曼,分散負(fù)載。
● 使用增加的退避時(shí)間避免脈沖蝴悉。固定的重試時(shí)間間隔彰阴,會(huì)集中那段時(shí)間的調(diào)用方需求。相反拍冠,使用退避算法尿这,不同調(diào)用方在經(jīng)過(guò)自己的退避時(shí)間后,在不同的時(shí)間點(diǎn)發(fā)起調(diào)用庆杜。
十射众、做出誤判的機(jī)器
就像杠桿一樣,自動(dòng)化使得管理員能夠花費(fèi)較少的努力進(jìn)行大量的操作晃财,這就是一個(gè)力量倍增器叨橱。
同樣,當(dāng)自動(dòng)化程序出現(xiàn)“判斷失誤”時(shí)断盛,引發(fā)的故障同樣也會(huì)被放大罗洗。
這種情況一般會(huì)出現(xiàn)在控制層中「置停控制層中的軟件主要管理基礎(chǔ)設(shè)施和應(yīng)用程序伙菜,而不是直接交付用戶功能。日志記錄命迈、監(jiān)控贩绕、調(diào)度程序火的、擴(kuò)展控制器、負(fù)載均衡器和配置管理等都是控制層的一部分淑倾。
貫穿這些系統(tǒng)失效事件的常見線索是馏鹤,自動(dòng)化系統(tǒng)并未簡(jiǎn)單地按照人類管理員的意愿行事。相反娇哆,它更像是工業(yè)機(jī)器人:掌握控制層感知系統(tǒng)的當(dāng)前狀態(tài)湃累,將其與期望的狀態(tài)進(jìn)行對(duì)比,然后對(duì)系統(tǒng)施加影響迂尝,使當(dāng)前狀態(tài)進(jìn)入到期望狀態(tài)脱茉。
可以在控制層軟件中實(shí)現(xiàn)類似的防護(hù)措施。
? 如果軟件觀測(cè)器顯示系統(tǒng)中80%以上的部分不可用垄开,那么與系統(tǒng)出問(wèn)題相比琴许,軟件觀測(cè)器出問(wèn)題的可能性更大。
? 運(yùn)用滯后原則溉躲,快速啟動(dòng)機(jī)器榜田,但要慢慢關(guān)機(jī),啟動(dòng)新機(jī)器要比關(guān)閉舊機(jī)器更安全锻梳。
? 當(dāng)期望狀態(tài)與觀測(cè)狀態(tài)之間的差距很大時(shí)箭券,要發(fā)出確認(rèn)信號(hào),這相當(dāng)于工業(yè)機(jī)器人上的大型黃色旋轉(zhuǎn)警示燈在報(bào)警疑枯。
? 那些消耗資源的系統(tǒng)應(yīng)該設(shè)計(jì)成有狀態(tài)的辩块,從而檢測(cè)它們是否正在試圖啟動(dòng)無(wú)限多個(gè)實(shí)例。
? 構(gòu)建減速區(qū)域荆永,緩解勢(shì)能废亭。假想一下,控制層雖然每秒都能感知到系統(tǒng)已經(jīng)過(guò)載具钥,但它啟動(dòng)一臺(tái)虛擬機(jī)處理負(fù)載需要花費(fèi)5分鐘豆村。所以在大量負(fù)載依然存在的情況下,要確甭钌荆控制層不會(huì)在5分鐘內(nèi)啟動(dòng)300個(gè)虛擬機(jī)掌动。
● 在造成一片狼藉之前尋求幫助∧担基礎(chǔ)設(shè)施管理工具可以迅速對(duì)系統(tǒng)產(chǎn)生巨大的影響粗恢,要在其內(nèi)部構(gòu)建限制器和防護(hù)措施,防止其快速毀掉整個(gè)系統(tǒng)欧瘪。
● 注意滯后時(shí)間和勢(shì)能适滓。由自動(dòng)化所引發(fā)的操作,需要花時(shí)間才能完成。這段時(shí)間通常會(huì)比監(jiān)控的時(shí)間間隔要長(zhǎng)凭迹,因此請(qǐng)務(wù)必考慮到系統(tǒng)需要經(jīng)過(guò)一些延遲后,才能對(duì)操作做出響應(yīng)苦囱。
● 謹(jǐn)防幻想和迷信嗅绸。控制系統(tǒng)會(huì)感知環(huán)境撕彤,但它們有可能被愚弄鱼鸠。它們會(huì)計(jì)算出一個(gè)預(yù)期狀態(tài),并形成有關(guān)當(dāng)前狀態(tài)的“信念”羹铅,但這兩者都有可能是錯(cuò)誤的蚀狰。
十一、緩慢的響應(yīng)
生成響應(yīng)較慢比拒絕連接或返回錯(cuò)誤更糟职员,在中間層服務(wù)中尤為如此麻蹋。
快速返回系統(tǒng)失效信息,能使調(diào)用方的系統(tǒng)快速完成事務(wù)處理焊切,最終成功或是系統(tǒng)失效扮授,取決于應(yīng)用程序的邏輯。但是专肪,緩慢的響應(yīng)會(huì)將調(diào)用系統(tǒng)和被調(diào)用系統(tǒng)中的資源拖得動(dòng)彈不得刹勃。
緩慢的響應(yīng)通常由過(guò)度的需求引起。當(dāng)所有可用的請(qǐng)求處理程序都已開始工作時(shí)嚎尤,就不會(huì)有任何余力接受新的請(qǐng)求了荔仁。當(dāng)出現(xiàn)一些底層的問(wèn)題時(shí),也會(huì)表現(xiàn)出響應(yīng)緩慢芽死。內(nèi)存泄漏也經(jīng)常表現(xiàn)為響應(yīng)緩慢乏梁,此時(shí)虛擬機(jī)就越來(lái)越奮力地回收空間,從而處理事務(wù)收奔。CPU利用率雖然很高掌呜,但都用在垃圾回收上,而非事務(wù)處理坪哄。另外质蕉,我偶爾會(huì)看到由于網(wǎng)絡(luò)擁塞而導(dǎo)致的響應(yīng)緩慢。這種情況在局域網(wǎng)內(nèi)部相對(duì)少見翩肌,但在廣域網(wǎng)上是肯定存在的模暗,尤其是在協(xié)議過(guò)于“饒舌嘮叨”的情況下。然而念祭,更常見的情況是兑宇,應(yīng)用程序一方面忙著清空它們的套接字發(fā)送緩沖區(qū),另一方面又任由其接收緩沖區(qū)不斷積壓粱坤,從而導(dǎo)致TCP協(xié)議停頓隶糕。當(dāng)開發(fā)工程師自行編寫一個(gè)較低層級(jí)的套接字協(xié)議時(shí)瓷产,這種情況經(jīng)常發(fā)生,其中read()例程只有當(dāng)接收緩沖區(qū)被清空之后枚驻,才會(huì)被循環(huán)調(diào)用濒旦。
緩慢的響應(yīng)傾向于逐層向上傳播,并逐漸導(dǎo)致層疊失效再登。
讓系統(tǒng)具備監(jiān)控自身性能的能力尔邓,就能辨別其何時(shí)違背SLA。假設(shè)服務(wù)提供方需要在100毫秒內(nèi)做出響應(yīng)锉矢,當(dāng)剛剛過(guò)去的20次事務(wù)的響應(yīng)時(shí)長(zhǎng)移動(dòng)平均值超過(guò)100毫秒時(shí)梯嗽,系統(tǒng)就會(huì)開始拒絕請(qǐng)求。這可能發(fā)生在應(yīng)用層沽损,此時(shí)系統(tǒng)可以利用給定的協(xié)議返回一個(gè)錯(cuò)誤響應(yīng)灯节。這也可能發(fā)生在連接層,此時(shí)系統(tǒng)開始拒絕新的套接字連接缠俺。當(dāng)然显晶,任何這樣的拒絕服務(wù),都必須有詳細(xì)的文檔記錄壹士,并且調(diào)用方也能對(duì)此有所預(yù)期磷雇。
● 緩慢的響應(yīng)會(huì)觸發(fā)層疊失效。一旦陷入響應(yīng)緩慢躏救,上游系統(tǒng)本身的處理速度也會(huì)隨之變慢唯笙,并且當(dāng)響應(yīng)時(shí)間超過(guò)其自身的超時(shí)時(shí)間時(shí),會(huì)很容易引發(fā)穩(wěn)定性問(wèn)題盒使。
● 對(duì)網(wǎng)站來(lái)說(shuō)崩掘,響應(yīng)緩慢會(huì)招致更多的流量。那些等待頁(yè)面響應(yīng)的用戶少办,會(huì)頻繁地單擊重新加載按鈕苞慢,為已經(jīng)過(guò)載的系統(tǒng)施加更多的流量。
● 考慮快速失敗英妓。如果系統(tǒng)能跟蹤自己的響應(yīng)情況挽放,那么就可以知道自己何時(shí)變慢。當(dāng)系統(tǒng)平均響應(yīng)時(shí)間超出系統(tǒng)所允許的時(shí)間時(shí)蔓纠,可以考慮發(fā)送一個(gè)即時(shí)錯(cuò)誤響應(yīng)辑畦。至少,當(dāng)平均響應(yīng)時(shí)間超過(guò)調(diào)用方的超時(shí)時(shí)間時(shí)腿倚,應(yīng)該發(fā)送這樣的響應(yīng)纯出。
● 搜尋內(nèi)存泄漏或資源爭(zhēng)奪之處。爭(zhēng)著使用已經(jīng)供不應(yīng)求的數(shù)據(jù)庫(kù)連接,會(huì)使響應(yīng)變慢暂筝,進(jìn)而加劇這種爭(zhēng)用箩言,導(dǎo)致惡性循環(huán)。內(nèi)存泄漏會(huì)導(dǎo)致垃圾收集器過(guò)度運(yùn)行焕襟,從而引發(fā)響應(yīng)緩慢分扎。低層級(jí)的低效協(xié)議會(huì)導(dǎo)致網(wǎng)絡(luò)停頓,從而導(dǎo)致響應(yīng)緩慢胧洒。
十二、無(wú)限長(zhǎng)的結(jié)果集
● 使用切合實(shí)際的數(shù)據(jù)量墨状。典型的開發(fā)數(shù)據(jù)集和測(cè)試數(shù)據(jù)集都太小了卫漫,不能呈現(xiàn)“無(wú)限長(zhǎng)結(jié)果集”的問(wèn)題。當(dāng)查詢返回100萬(wàn)行記錄并轉(zhuǎn)成對(duì)象時(shí)肾砂,使用生產(chǎn)環(huán)境規(guī)模大小的數(shù)據(jù)集查看會(huì)發(fā)生什么情況列赎。這樣做還有一個(gè)額外的好處:當(dāng)使用生產(chǎn)環(huán)境規(guī)模的測(cè)試數(shù)據(jù)集時(shí),性能測(cè)試的結(jié)果更可靠镐确。
● 在前端發(fā)送分頁(yè)請(qǐng)求包吝。前端在調(diào)用服務(wù)時(shí),就要構(gòu)建好分頁(yè)信息源葫。該請(qǐng)求應(yīng)包含需要獲取的第一項(xiàng)和返回總個(gè)數(shù)這樣的參數(shù)诗越。服務(wù)器端的回復(fù)應(yīng)大致指明其中有多少條結(jié)果。
● 不要依賴數(shù)據(jù)生產(chǎn)者息堂。即使認(rèn)為某個(gè)查詢的結(jié)果固定為幾個(gè)嚷狞,也要注意:由于系統(tǒng)某個(gè)其他部分的作用,這個(gè)數(shù)量可能會(huì)在沒(méi)有警告的情況下發(fā)生變化荣堰。合理的數(shù)量只能是“零”“一”和“許多”床未。因此除非單單查詢某一行,否則就有可能返回太多結(jié)果振坚。要想對(duì)創(chuàng)建的數(shù)據(jù)量加以限制薇搁,不要依賴數(shù)據(jù)生產(chǎn)者。他們遲早會(huì)瘋狂起來(lái)渡八,無(wú)端地塞滿一張數(shù)據(jù)庫(kù)表啃洋,而那個(gè)時(shí)候該找誰(shuí)說(shuō)理去?
● 在其他應(yīng)用程序級(jí)別的協(xié)議中使用返回?cái)?shù)量限制機(jī)制呀狼。服務(wù)調(diào)用裂允、RMI、DCOM[插圖]哥艇、XML-RPC[插圖]以及任何其他類型的請(qǐng)求-回復(fù)調(diào)用绝编,都容易返回巨量的對(duì)象,從而消耗太多內(nèi)存。
第5章 穩(wěn)定性的模式
一十饥、超時(shí)
良好的超時(shí)機(jī)制可以提供失誤隔離功能——其他服務(wù)或設(shè)備中出現(xiàn)的問(wèn)題不一定會(huì)成為你的問(wèn)題窟勃。
超時(shí)機(jī)制與斷路器相得益彰。斷路器可以記錄一段時(shí)間內(nèi)的超時(shí)情況逗堵,如果超時(shí)過(guò)于頻繁秉氧,斷路器就會(huì)跳閘庆猫。
超時(shí)模式和快速失敗模式(請(qǐng)參閱5.5節(jié))都解決了延遲問(wèn)題竹习。當(dāng)其他系統(tǒng)失效時(shí)欺殿,要保證自身系統(tǒng)不受影響裸弦,超時(shí)模式非常有用纯续。若需要報(bào)告某些事務(wù)無(wú)法處理的原因尚卫,快速失敗模式則能派上用場(chǎng)喧伞。就像一枚硬幣的兩面窟她,快速失敗模式適用于傳入系統(tǒng)的請(qǐng)求纸泡,超時(shí)模式則主要適用于系統(tǒng)發(fā)出的出站請(qǐng)求漂问。
● 將超時(shí)模式應(yīng)用于集成點(diǎn)、阻塞線程和緩慢響應(yīng)女揭。超時(shí)模式可以防止對(duì)集成點(diǎn)的調(diào)用轉(zhuǎn)變?yōu)閷?duì)阻塞線程的調(diào)用蚤假,從而避免層疊失效。
● 采用超時(shí)模式吧兔,從意外系統(tǒng)失效中恢復(fù)磷仰。當(dāng)操作時(shí)間過(guò)長(zhǎng),有時(shí)無(wú)須明確其原因時(shí)掩驱,只需要放棄操作并繼續(xù)做其他事芒划。超時(shí)模式可以幫助我們實(shí)現(xiàn)這一點(diǎn)。
● 考慮延遲重試欧穴。大多數(shù)超時(shí)原因涉及網(wǎng)絡(luò)或遠(yuǎn)程系統(tǒng)中的問(wèn)題民逼。這些問(wèn)題不會(huì)立即被解決。立即重試很可能會(huì)遭遇同樣的問(wèn)題涮帘,并導(dǎo)致再次超時(shí)拼苍。這只會(huì)讓用戶等待更長(zhǎng)的時(shí)間才能看到錯(cuò)誤消息。大多數(shù)情況下调缨,應(yīng)該把操作任務(wù)放入隊(duì)列疮鲫,稍后再重試。
二弦叶、斷路器
斷路器能有效防止集成點(diǎn)俊犯、層疊失效、系統(tǒng)容量失衡和響應(yīng)緩慢等危及穩(wěn)定性的反模式出現(xiàn)伤哺,它能與超時(shí)模式緊密協(xié)作燕侠,跟蹤調(diào)用超時(shí)失斦咦妗(區(qū)別于調(diào)用執(zhí)行失敗)绢彤。
三七问、艙壁
船舶的艙壁是一些隔板,一旦將其密封起來(lái)茫舶,就能將船分隔成若干獨(dú)立的水密隔艙械巡。在艙壁口關(guān)閉的情況下,艙壁可以防止水從一個(gè)部分流到另一個(gè)部分饶氏。通過(guò)這種方式讥耗,船體即使被洞穿一次也不會(huì)沉沒(méi)。艙壁這種設(shè)計(jì)強(qiáng)調(diào)了控制損害范圍的原則疹启。
在軟件開發(fā)中也可以使用相同的技術(shù)葛账。通過(guò)使用隔板對(duì)系統(tǒng)進(jìn)行分區(qū),就可以將系統(tǒng)失效控制在其中某個(gè)分區(qū)內(nèi)皮仁,而不會(huì)令其摧毀整個(gè)系統(tǒng)。物理冗余是實(shí)現(xiàn)艙壁最常見的形式菲宴。如果系統(tǒng)有4臺(tái)獨(dú)立的服務(wù)器贷祈,那么其中一個(gè)硬件所出現(xiàn)的失效,就不會(huì)影響其他服務(wù)器喝峦。同樣势誊,在一臺(tái)服務(wù)器上運(yùn)行兩個(gè)應(yīng)用程序?qū)嵗绻渲幸粋€(gè)崩潰谣蠢,那么另一個(gè)仍將繼續(xù)運(yùn)行粟耻。(當(dāng)然,除非讓第1個(gè)應(yīng)用程序?qū)嵗罎⒌哪欠N外部影響力也能讓第2個(gè)應(yīng)用程序?qū)嵗罎⒚减狻#?/p>
四挤忙、穩(wěn)態(tài)
無(wú)論是文件系統(tǒng)中的日志文件,數(shù)據(jù)庫(kù)中的記錄行還是內(nèi)存中的緩存谈喳,任何累積資源的機(jī)制册烈,都令人聯(lián)想起美國(guó)高中微積分題目中的存儲(chǔ)桶。隨著數(shù)據(jù)的累積婿禽,存儲(chǔ)桶會(huì)以一定的速率填滿赏僧。此時(shí)這個(gè)存儲(chǔ)桶必須以相同或更快的速率清空數(shù)據(jù),否則最終會(huì)溢出扭倾。當(dāng)該存儲(chǔ)桶溢出時(shí)淀零,壞事會(huì)發(fā)生:服務(wù)器停機(jī),數(shù)據(jù)庫(kù)變慢或拋出錯(cuò)誤信息膛壹,響應(yīng)長(zhǎng)得仿佛是在星球間進(jìn)行通信驾中。穩(wěn)態(tài)模式表明唉堪,針對(duì)每個(gè)累積資源的機(jī)制,要相應(yīng)存在另一個(gè)機(jī)制回收該資源哀卫。下面介紹幾種長(zhǎng)期存在并有可能繼續(xù)擴(kuò)大的問(wèn)題巨坊,以及如何避免去擺弄它們。
- 數(shù)據(jù)清除
- 日志文件:不加控制的日志文件會(huì)打滿磁盤此改,耗盡文件系統(tǒng)的空間趾撵。最好一開始就避免一直往文件系統(tǒng)里添加內(nèi)容。配置日志文件回轉(zhuǎn)(rotation)只需要花幾分鐘時(shí)間共啃。
- 內(nèi)存中的緩存:如果緩存鍵數(shù)量沒(méi)有上限占调,則必須限制緩存大小,并且采用某種形式的緩存失效機(jī)制移剪。定時(shí)刷新緩存是最簡(jiǎn)單的緩存失效機(jī)制究珊,“最近最少使用”[插圖]或工作集算法也值得研究,但定時(shí)刷新緩存能夠處理90%的情況纵苛。
● 避免擺弄剿涮。人為干預(yù)生產(chǎn)環(huán)境會(huì)導(dǎo)致問(wèn)題。要消除對(duì)生產(chǎn)環(huán)境重復(fù)進(jìn)行人為干預(yù)的需求攻人。系統(tǒng)應(yīng)在無(wú)須手動(dòng)清理磁盤或每晚重新啟動(dòng)的情況下取试,至少運(yùn)行一個(gè)發(fā)布周期。
● 清除帶有應(yīng)用程序邏輯的數(shù)據(jù)怀吻。DBA可以通過(guò)創(chuàng)建腳本清除數(shù)據(jù)瞬浓,但他們不知道刪除數(shù)據(jù)之后,應(yīng)用程序會(huì)如何運(yùn)轉(zhuǎn)蓬坡。為了保持邏輯完整性(特別是在使用對(duì)象關(guān)系映射工具時(shí))猿棉,應(yīng)用程序需要清除自己的數(shù)據(jù)。
● 限制緩存屑咳。內(nèi)存中的緩存可以加快應(yīng)用程序的運(yùn)行速度萨赁。但若不對(duì)內(nèi)存中的緩存加以控制,執(zhí)行速度仍會(huì)降低兆龙。需要限制緩存可消耗的內(nèi)存量位迂。
● 滾動(dòng)日志。不要無(wú)限量保留日志文件详瑞〉嗔郑基于日志文件的大小來(lái)配置日志文件回轉(zhuǎn)。如果合規(guī)性要求保留坝橡,則在非生產(chǎn)服務(wù)器上執(zhí)行泻帮。
五、快速失敗
即使在快速失敗時(shí)计寇,也要確保用不同的方式報(bào)告系統(tǒng)性失效(資源不可用)和應(yīng)用程序失斅嘣印(參數(shù)違規(guī)或無(wú)效狀態(tài))脂倦。否則,哪怕僅是用戶輸入了錯(cuò)誤數(shù)據(jù)并點(diǎn)擊了三四次重新加載元莫,若報(bào)告成不具分辨度的一般性錯(cuò)誤赖阻,也可能導(dǎo)致上游系統(tǒng)引發(fā)斷路器跳閘□獯溃快速失敗模式通過(guò)避免響應(yīng)緩慢來(lái)提高整個(gè)系統(tǒng)的穩(wěn)定性火欧。與超時(shí)模式配合使用,快速失敗模式有助于避免層疊失效茎截。當(dāng)系統(tǒng)由于部分失效而面臨壓力時(shí)苇侵,快速失敗模式還有助于保持系統(tǒng)容量。
為了讓“任其崩潰并替換”卓有成效企锌,系統(tǒng)要具備幾個(gè)前提榆浓。
- 有限的粒度:必須為崩潰定義邊界。發(fā)生崩潰的組件應(yīng)該是獨(dú)立的撕攒,系統(tǒng)的其余部分必須能夠自我防護(hù)陡鹃,避免受到層疊失效的影響。在微服務(wù)架構(gòu)中抖坪,服務(wù)的整個(gè)實(shí)例可能是正確的崩潰粒度杉适。這在很大程度上取決于它被一個(gè)干凈的實(shí)例所取代的速度,繼而引入了下面這個(gè)關(guān)鍵的前提柳击。
- 快速替換:?jiǎn)?dòng)NodeJS進(jìn)程需要花幾毫秒,可實(shí)現(xiàn)快速替換片习;但是啟動(dòng)一臺(tái)新的虛擬機(jī)則需要幾分鐘捌肴,就不適合“任其崩潰并替換”的策略了。
- 監(jiān)管:監(jiān)管器需要密切注意它們重新啟動(dòng)子進(jìn)程的頻率藕咏。如果重新啟動(dòng)子進(jìn)程過(guò)于頻繁状知,那么監(jiān)管器可能需要自行崩潰。這種情況表明系統(tǒng)狀態(tài)沒(méi)有得到充分的清理孽查,或者整個(gè)系統(tǒng)面臨危險(xiǎn)饥悴,而監(jiān)管器只是掩蓋了根本問(wèn)題。
● 快速失敗盲再,而非緩慢響應(yīng)西设。如果系統(tǒng)無(wú)法滿足SLA要求,快速通知調(diào)用者答朋。不要讓調(diào)用者等待錯(cuò)誤信息贷揽,也不要讓他們一直等到超時(shí)。否則你的問(wèn)題也會(huì)變成他們的問(wèn)題梦碗。
● 預(yù)留資源禽绪,并盡早驗(yàn)證集成點(diǎn)有效蓖救。本著“不做無(wú)用功”的原則,確保在開始之前就能完成事務(wù)印屁。如果關(guān)鍵資源不可用循捺,比如所需調(diào)用的斷路器已跳閘,那么就不要再浪費(fèi)精力去調(diào)用雄人。在事務(wù)的開始階段和中間階段从橘,關(guān)鍵資源可用狀態(tài)發(fā)生變化的可能性極小。
● 使用輸入驗(yàn)證柠衍。即使在預(yù)留資源之前洋满,也要進(jìn)行基本的用戶輸入驗(yàn)證。不要糾結(jié)于檢查數(shù)據(jù)庫(kù)連接珍坊,獲取域?qū)ο笪矗畛鋽?shù)據(jù)以及調(diào)用validate()方法,找到未輸入的必需參數(shù)才是關(guān)鍵阵漏。
六驻民、任其崩潰并替換
有時(shí),為了實(shí)現(xiàn)系統(tǒng)級(jí)穩(wěn)定性履怯,放棄組件級(jí)穩(wěn)定性就是所能做的最好的事情了回还。在Erlang語(yǔ)言中,這被稱為“任其崩潰并替換”的哲學(xué)叹洲。
程序所能擁有的最干凈的狀態(tài)柠硕,就是在剛剛完成啟動(dòng)的那一刻。任其崩潰并替換的方法認(rèn)為錯(cuò)誤恢復(fù)難以完成且不可信賴运提,所以我們的目標(biāo)應(yīng)該是盡快回到剛完成啟動(dòng)時(shí)的干凈狀態(tài)蝗柔。
● 通過(guò)組件崩潰保護(hù)系統(tǒng)。通過(guò)組件級(jí)不穩(wěn)定性構(gòu)建系統(tǒng)級(jí)穩(wěn)定性民泵,這似乎違反直覺(jué)癣丧。即便如此,這可能是將系統(tǒng)恢復(fù)到已知良好狀態(tài)的最佳方式栈妆。
● 快速重新啟動(dòng)與重新歸隊(duì)胁编。優(yōu)雅崩潰的關(guān)鍵是快速恢復(fù)。否則鳞尔,當(dāng)太多組件同時(shí)啟動(dòng)時(shí)嬉橙,就可能會(huì)失去服務(wù)。一旦一個(gè)組件重新啟動(dòng)寥假,就應(yīng)該自動(dòng)令其重新歸隊(duì)憎夷。
● 隔離組件以實(shí)現(xiàn)獨(dú)立崩潰。使用斷路器將調(diào)用方與發(fā)生崩潰的組件隔離開來(lái)昧旨。使用監(jiān)管器確定重新啟動(dòng)的范圍拾给。設(shè)計(jì)監(jiān)管器層級(jí)樹祥得,既實(shí)現(xiàn)崩潰隔離,又不會(huì)影響無(wú)關(guān)的功能蒋得。
● 不要讓單體系統(tǒng)崩潰级及。運(yùn)行時(shí)負(fù)載較大或啟動(dòng)時(shí)間較長(zhǎng)的大型進(jìn)程,不適合運(yùn)用“任其崩潰并替換”策略额衙。同樣饮焦,將許多特性耦合到單個(gè)進(jìn)程中的應(yīng)用程序,也不推薦運(yùn)用該策略窍侧。
七县踢、握手
當(dāng)失衡的系統(tǒng)容量導(dǎo)致響應(yīng)緩慢時(shí),“握手”可能是最有價(jià)值的伟件。如果服務(wù)器檢測(cè)到自己不能滿足其SLA要求硼啤,那么它應(yīng)該通過(guò)一些方法請(qǐng)求調(diào)用方停止進(jìn)程。如果服務(wù)器位于負(fù)載均衡器之后斧账,那么它們可以使用“開關(guān)”控制谴返,來(lái)“開啟或停止”對(duì)負(fù)載均衡器的響應(yīng),然后負(fù)載均衡器可以將無(wú)響應(yīng)的服務(wù)器移出負(fù)載均衡池咧织。不過(guò)嗓袱,這是一種粗放的機(jī)制,最好在執(zhí)行的所有自定義協(xié)議中構(gòu)建握手機(jī)制习绢。
當(dāng)調(diào)用缺乏握手機(jī)制的服務(wù)時(shí)渠抹,斷路器是一種可以使用的權(quán)宜之計(jì)。在這種情況下闪萄,無(wú)須禮貌地詢問(wèn)服務(wù)器能否處理請(qǐng)求梧却,只須發(fā)起調(diào)用并跟蹤調(diào)用是否有效√壹澹總體而言,握手是一種未被充分利用的技術(shù)大刊,在應(yīng)用層協(xié)議中擁有巨大的優(yōu)勢(shì)为迈。在層疊失效情況下,握手是一種防止裂紋跨層蔓延的有效方法缺菌。
● 創(chuàng)建基于合作的需求控制機(jī)制葫辐。客戶端和服務(wù)器之間的握手伴郁,允許將需求的流量調(diào)節(jié)到可服務(wù)的級(jí)別耿战。在構(gòu)建客戶端和服務(wù)器時(shí),兩者都必須實(shí)現(xiàn)握手焊傅。而大多數(shù)最常見的應(yīng)用程序級(jí)協(xié)議剂陡,并沒(méi)有實(shí)現(xiàn)握手狈涮。
● 考慮健康狀況檢查。在集群或負(fù)載均衡服務(wù)中鸭栖,使用健康狀況檢查實(shí)現(xiàn)實(shí)例與負(fù)載均衡器握手歌馍。
|● 在自己的低層協(xié)議中構(gòu)建握手。如果創(chuàng)建了基于套接字的協(xié)議晕鹊,那么可以在其中構(gòu)建握手機(jī)制松却。這樣一來(lái),端點(diǎn)就可以在未準(zhǔn)備好接受工作時(shí)溅话,通知其他端點(diǎn)晓锻。
八、考驗(yàn)機(jī)
(本書中飞几,“考驗(yàn)機(jī)”是指能夠用網(wǎng)絡(luò)錯(cuò)誤砚哆、協(xié)議錯(cuò)誤或應(yīng)用級(jí)錯(cuò)誤等各種低層錯(cuò)誤來(lái)測(cè)試被測(cè)軟件。)
模擬依賴系統(tǒng)的失效行為來(lái)測(cè)試集成系統(tǒng)的一些無(wú)法驗(yàn)證的行為循狰。
假設(shè)要構(gòu)建一個(gè)替代每個(gè)遠(yuǎn)端Web服務(wù)調(diào)用的考驗(yàn)機(jī)窟社。由于遠(yuǎn)程調(diào)用使用網(wǎng)絡(luò),因此套接字連接容易出現(xiàn)以下類型的失效绪钥。
? 連接被拒絕灿里。
? 數(shù)據(jù)一直在監(jiān)聽隊(duì)列中等待,直到調(diào)用方超時(shí)程腹。
? 遠(yuǎn)端在回復(fù)了SYN/ACK之后就不再發(fā)送任何數(shù)據(jù)匣吊。
? 遠(yuǎn)端只發(fā)送了一些RESET數(shù)據(jù)包。
? 遠(yuǎn)端報(bào)告接收窗口已滿寸潦,但從不清空數(shù)據(jù)色鸳。
? 建立了連接,但遠(yuǎn)端一直不發(fā)送數(shù)據(jù)见转。
? 建立了連接命雀,但數(shù)據(jù)包丟失,重新傳輸導(dǎo)致延遲斩箫。
? 建立了連接吏砂,但遠(yuǎn)端對(duì)接收到的數(shù)據(jù)包從不進(jìn)行確認(rèn),導(dǎo)致無(wú)休止的重新傳輸乘客。
? 服務(wù)接受了請(qǐng)求狐血,并且發(fā)送了響應(yīng)頭(假設(shè)是HTTP),但從不發(fā)送響應(yīng)正文易核。
? 服務(wù)每30秒發(fā)送一字節(jié)的響應(yīng)匈织。
? 服務(wù)發(fā)送的響應(yīng)格式是HTML,而不是預(yù)期的XML。
? 服務(wù)本應(yīng)發(fā)送幾千字節(jié)的數(shù)據(jù)缀匕,但實(shí)際上發(fā)送了幾兆字節(jié)纳决。
? 服務(wù)拒絕了所有身份驗(yàn)證證書授權(quán)。
以上失效問(wèn)題可以分為幾類:網(wǎng)絡(luò)傳輸問(wèn)題弦追、網(wǎng)絡(luò)協(xié)議問(wèn)題岳链、應(yīng)用程序協(xié)議問(wèn)題和應(yīng)用程序邏輯問(wèn)題。
混沌工程
要點(diǎn)回顧
● 模擬偏離接口規(guī)范的系統(tǒng)失效方式劲件。調(diào)用真正的應(yīng)用程序掸哑,僅能測(cè)試真實(shí)應(yīng)用程序刻意生成的那些錯(cuò)誤。優(yōu)秀的考驗(yàn)機(jī)可以讓你模擬現(xiàn)實(shí)世界中的各種混亂的系統(tǒng)失效方式零远。
● 給調(diào)用方施加壓力苗分。考驗(yàn)機(jī)能產(chǎn)生緩慢響應(yīng)和垃圾響應(yīng)牵辣,甚至不響應(yīng)摔癣。這樣一來(lái),就可以看看應(yīng)用程序如何反應(yīng)纬向。
● 利用共享框架處理常見系統(tǒng)失效問(wèn)題择浊。對(duì)每個(gè)集成點(diǎn)來(lái)說(shuō),不一定需要有單獨(dú)的考驗(yàn)機(jī)逾条∽裂遥考驗(yàn)機(jī)這個(gè)“殺手”服務(wù)器可以監(jiān)聽多個(gè)端口,并根據(jù)你所連接的端口創(chuàng)建不同的系統(tǒng)失效方式师脂。
● 考驗(yàn)機(jī)僅是補(bǔ)充担孔,不能取代其他測(cè)試方法〕跃考驗(yàn)機(jī)模式是對(duì)其他測(cè)試方法的補(bǔ)充和完善糕篇。它并不能取代單元測(cè)試、驗(yàn)收測(cè)試酌心、滲透測(cè)試等(這些測(cè)試都有助于驗(yàn)證系統(tǒng)的功能性行為)拌消。考驗(yàn)機(jī)有助于驗(yàn)證非功能性行為安券,同時(shí)又與遠(yuǎn)程系統(tǒng)保持隔離墩崩。
九、中間件解耦
松耦合完疫、緊耦合
同步泰鸡、異步
● 在最后責(zé)任時(shí)刻再做決定债蓝。在不對(duì)設(shè)計(jì)或架構(gòu)進(jìn)行大規(guī)模更改的情況下壳鹤,大部分穩(wěn)定性模式可以實(shí)施。但中間件解耦是架構(gòu)決策饰迹,相關(guān)的實(shí)施會(huì)波及系統(tǒng)的每個(gè)部分芳誓。應(yīng)該在最后責(zé)任時(shí)刻到來(lái)時(shí)余舶,盡早做出這種幾乎不可逆轉(zhuǎn)的決策。
● 通過(guò)完全的解耦避免眾多系統(tǒng)失效方式锹淌。各臺(tái)服務(wù)器匿值、層級(jí)和應(yīng)用程序解耦得越徹底,集成點(diǎn)赂摆、層疊失效挟憔、響應(yīng)緩慢和線程阻塞等問(wèn)題就越少。應(yīng)用程序解耦后烟号,系統(tǒng)可以單獨(dú)更改其他應(yīng)用程序的所有配件绊谭,因此也更具適應(yīng)性。
● 了解更多架構(gòu)汪拥,從中進(jìn)行選擇达传。并非每個(gè)系統(tǒng)都必須看起來(lái)像是帶有關(guān)系數(shù)據(jù)庫(kù)的三層應(yīng)用程序。多了解一些架構(gòu)迫筑,并針對(duì)所面臨的問(wèn)題選擇最佳的架構(gòu)宪赶。
十、卸下負(fù)載
服務(wù)應(yīng)該模仿TCP的做法:當(dāng)負(fù)載過(guò)高時(shí)脯燃,就開始拒絕新的工作請(qǐng)求搂妻。這與快速失敗模式相關(guān)。
在系統(tǒng)或企業(yè)的內(nèi)部曲伊,運(yùn)用背壓機(jī)制會(huì)更有效(請(qǐng)參閱5.11節(jié))叽讳,這樣做有助于在同步耦合的服務(wù)中,維持均衡的請(qǐng)求吞吐量坟募。在這種情況下岛蚤,卸下負(fù)載模式可以作為輔助措施。
● 無(wú)法滿足全世界的請(qǐng)求懈糯。無(wú)論基礎(chǔ)設(shè)施的規(guī)模有多大涤妒,也無(wú)論容量的擴(kuò)展速度有多快,這個(gè)世界總是擁有超出系統(tǒng)容量極限的人數(shù)和設(shè)備數(shù)赚哗。當(dāng)系統(tǒng)面對(duì)數(shù)量不受控制的需求時(shí)她紫,一旦來(lái)自世界各地的請(qǐng)求瘋狂地涌來(lái),系統(tǒng)就需要卸下負(fù)載屿储。
● 通過(guò)卸下負(fù)載避免響應(yīng)緩慢贿讹。響應(yīng)緩慢可不是好事。讓系統(tǒng)的響應(yīng)時(shí)間得到控制够掠,而不是任其讓調(diào)用方超時(shí)民褂。
● 將負(fù)載均衡器用作減震器。個(gè)別的服務(wù)實(shí)例可以通過(guò)向負(fù)載均衡器報(bào)告HTTP 503錯(cuò)誤,獲得片刻喘息赊堪,而負(fù)載均衡器擅于快速地回收這些連接面殖。
十一、背壓模式
每個(gè)性能問(wèn)題都源于其背后的一個(gè)等待隊(duì)列哭廉,如套接字的監(jiān)聽隊(duì)列脊僚、操作系統(tǒng)的運(yùn)行隊(duì)列或數(shù)據(jù)庫(kù)的I/O隊(duì)列。
如果隊(duì)列無(wú)限長(zhǎng)遵绰,那么它就會(huì)耗盡所有可用的內(nèi)存辽幌。隨著隊(duì)列長(zhǎng)度的增加,完成隊(duì)列中某項(xiàng)工作的時(shí)間也會(huì)增加(類似“利特爾法則”)椿访。因此舶衬,當(dāng)隊(duì)列長(zhǎng)度達(dá)到無(wú)窮大時(shí),響應(yīng)時(shí)間也會(huì)趨向無(wú)窮大赎离。我們絕對(duì)不希望系統(tǒng)中出現(xiàn)無(wú)限長(zhǎng)的隊(duì)列逛犹。
如果隊(duì)列的長(zhǎng)度是有限的,那么當(dāng)隊(duì)列已滿且生產(chǎn)者仍試圖再塞入一個(gè)新請(qǐng)求時(shí)梁剔,必須立刻采取應(yīng)對(duì)措施虽画。即使要塞入的新請(qǐng)求很小,也沒(méi)有任何多余的空間荣病。
我們可以在以下情況中做出選擇码撰。
? 假裝接受新請(qǐng)求,但實(shí)際上將其拋棄个盆。
? 確實(shí)接受新請(qǐng)求脖岛,但拋棄隊(duì)列中的某一個(gè)請(qǐng)求。
? 拒絕新請(qǐng)求颊亮。
? 阻塞生產(chǎn)者柴梆,直至隊(duì)列出現(xiàn)空的位置。
對(duì)于某些使用場(chǎng)景终惑,拋棄新請(qǐng)求可能是最佳選擇绍在。對(duì)于那些隨著時(shí)間的推移價(jià)值迅速降低的數(shù)據(jù),拋棄隊(duì)列中最先發(fā)出的請(qǐng)求可能是最佳選擇雹有。
阻塞生產(chǎn)者是一種流量控制手段偿渡,允許隊(duì)列向發(fā)送數(shù)據(jù)包的上游系統(tǒng)實(shí)施“背壓”措施。有可能這個(gè)背壓措施會(huì)一直傳播到最終的客戶端霸奕,而這個(gè)客戶端會(huì)降低請(qǐng)求發(fā)送的速度溜宽,直到隊(duì)列出現(xiàn)空位置。
TCP在每個(gè)數(shù)據(jù)包中都采用額外的字段構(gòu)建背壓機(jī)制质帅。一旦接收方的窗口已滿适揉,發(fā)送方就不得發(fā)送任何內(nèi)容合武,直至窗口被釋放。來(lái)自TCP接收方窗口的背壓涡扼,會(huì)讓發(fā)送方填滿其發(fā)送緩沖區(qū),這時(shí)后續(xù)寫入套接字的調(diào)用將被阻塞盟庞。發(fā)送方與接收方的機(jī)制有所不同吃沪,但做法仍然是讓發(fā)送方放慢速度,直至接收方處理完“手上堆積的工作”什猖。
顯然票彪,背壓機(jī)制會(huì)導(dǎo)致線程阻塞。將兩種由臨時(shí)狀態(tài)導(dǎo)致的背壓不狮,和消費(fèi)者運(yùn)行中斷導(dǎo)致的背壓區(qū)別開來(lái)極為重要降铸。背壓機(jī)制最適合異步調(diào)用和編程,如果編程語(yǔ)言支持摇零,可以利用許多Rx框架、actor或channel工具實(shí)現(xiàn)這個(gè)機(jī)制。
當(dāng)消費(fèi)者的緩沖池容量有限時(shí)编丘,背壓機(jī)制就只能對(duì)負(fù)載進(jìn)行管理摆舟。原因在于,發(fā)送數(shù)據(jù)包的各個(gè)上游系統(tǒng)千差萬(wàn)別噪服,無(wú)法對(duì)其施加系統(tǒng)性的影響毡泻。可以用一個(gè)例子來(lái)說(shuō)明這一點(diǎn)粘优,假設(shè)系統(tǒng)提供了一個(gè)API仇味,能讓用戶在特定位置創(chuàng)建“標(biāo)簽”。而使用該API的上游雹顺,就包括大批手機(jī)應(yīng)用程序和Web應(yīng)用程序丹墨。
在系統(tǒng)內(nèi)部,創(chuàng)建新標(biāo)簽并為其創(chuàng)建索引的速度是確定的嬉愧,并且會(huì)受到存儲(chǔ)和索引技術(shù)的限制带到。當(dāng)上游調(diào)用“創(chuàng)建標(biāo)簽”的速率超過(guò)存儲(chǔ)引擎的處理上限時(shí),會(huì)發(fā)生什么情況英染?調(diào)用會(huì)變得越來(lái)越慢揽惹。如果沒(méi)有背壓機(jī)制,這會(huì)導(dǎo)致處理速度逐漸減慢四康,直至該API與離線無(wú)異搪搏。
然而,可以利用一個(gè)阻塞隊(duì)列構(gòu)建背壓機(jī)制闪金,實(shí)現(xiàn)“創(chuàng)建標(biāo)簽”的調(diào)用疯溺。假設(shè)每臺(tái)API服務(wù)器允許100個(gè)調(diào)用同時(shí)訪問(wèn)存儲(chǔ)引擎论颅。當(dāng)?shù)?01個(gè)調(diào)用抵達(dá)API服務(wù)器時(shí),調(diào)用線程將被阻塞囱嫩,直至隊(duì)列中空出一個(gè)位置恃疯,這種阻塞就是背壓機(jī)制。API服務(wù)器不能超出額定速度對(duì)存儲(chǔ)引擎發(fā)起調(diào)用墨闲。
在這種情況下今妄,限制每臺(tái)服務(wù)器僅接受100個(gè)調(diào)用,這樣處理過(guò)于粗糙鸳碧。這意味著有可能一臺(tái)API服務(wù)器有被阻塞的線程盾鳞,而另一臺(tái)服務(wù)器隊(duì)列中卻有空閑位置。為了令其更加智能化瞻离,可以讓API服務(wù)器發(fā)起任意多次調(diào)用腾仅,但將阻塞置于接收端。這種情況下套利,就必須在現(xiàn)有的存儲(chǔ)引擎外面包裹一個(gè)服務(wù)推励,進(jìn)而接收調(diào)用、度量響應(yīng)時(shí)間并調(diào)整其內(nèi)部隊(duì)列長(zhǎng)度肉迫,實(shí)現(xiàn)吞吐量的最大化吹艇,保護(hù)存儲(chǔ)引擎。
然而在某些時(shí)候昂拂,API服務(wù)器仍然會(huì)有一個(gè)等待調(diào)用的線程受神。正如4.5節(jié)所述,被阻塞的線程會(huì)快速引發(fā)系統(tǒng)失效格侯。當(dāng)跨越系統(tǒng)邊界時(shí)鼻听,被阻塞的線程會(huì)阻礙用戶使用,或引發(fā)反復(fù)重試操作联四。因此撑碴,在系統(tǒng)邊界內(nèi)運(yùn)用背壓機(jī)制效果最好。而在系統(tǒng)邊界之間朝墩,還是需要使用卸下負(fù)載模式和異步調(diào)用醉拓。
在上面的示例中,API服務(wù)器應(yīng)該用一個(gè)線程池接受調(diào)用請(qǐng)求收苏,然后用另一組線程向存儲(chǔ)引擎發(fā)出后續(xù)的出站調(diào)用亿卤。這樣一來(lái),當(dāng)后續(xù)出站調(diào)用阻塞時(shí)鹿霸,前面請(qǐng)求處理的線程就可以超時(shí)排吴、解除阻塞并回應(yīng)HTTP 503錯(cuò)誤狀態(tài)碼∨呈螅或者钻哩,API服務(wù)器可以丟棄隊(duì)列中的一個(gè)“創(chuàng)建標(biāo)簽”命令屹堰,以便進(jìn)行索引,此時(shí)返回HTTP 202狀態(tài)碼(表示“請(qǐng)求已接受街氢,但尚未處理”)更為合適扯键。
系統(tǒng)邊界內(nèi)的消費(fèi)者,會(huì)以性能問(wèn)題或超時(shí)的方式再現(xiàn)背壓過(guò)程珊肃。事實(shí)上荣刑,這確實(shí)表明了一個(gè)真實(shí)的性能問(wèn)題,消費(fèi)者集體產(chǎn)生了超出提供者所能處理的負(fù)載近范!盡管如此,有時(shí)提供者也情有可原延蟹。盡管它有足夠的容量應(yīng)對(duì)“正称谰兀”的流量,但碰上一個(gè)消費(fèi)者瘋狂地發(fā)送請(qǐng)求阱飘,這可能源于自黑式攻擊或者僅是網(wǎng)絡(luò)流量模式自身發(fā)生了變化斥杜。
當(dāng)背壓機(jī)制生效時(shí),需要通知監(jiān)控系統(tǒng)沥匈,從而判斷背壓是隨機(jī)波動(dòng)還是大體趨勢(shì)蔗喂。
要點(diǎn)回顧
● 背壓機(jī)制通過(guò)讓消費(fèi)者放慢工作來(lái)實(shí)現(xiàn)安全性。消費(fèi)者的處理速度終究會(huì)減慢高帖,此時(shí)唯一能做的就是讓消費(fèi)者“提醒”提供者缰儿,不要過(guò)快地發(fā)送請(qǐng)求。
● 在系統(tǒng)邊界內(nèi)運(yùn)用背壓機(jī)制散址。如果是跨越系統(tǒng)邊界的情況乖阵,就要換用卸下負(fù)載模式,當(dāng)用戶群是整個(gè)互聯(lián)網(wǎng)時(shí)更應(yīng)如此预麸。
● 要想獲得有限的響應(yīng)時(shí)間瞪浸,就需要構(gòu)建有限長(zhǎng)度的等待隊(duì)列。當(dāng)?shù)却?duì)列已滿時(shí)只有以下選擇(雖然都不令人愉悅):丟棄數(shù)據(jù)吏祸,拒絕工作或?qū)⑵渥枞云选OM(fèi)者必須當(dāng)心,不要永久阻塞贡翘。
十二蹈矮、調(diào)速器
● 放慢自動(dòng)化工具的工作速度,以便人工干預(yù)鸣驱。當(dāng)事情的發(fā)展即將脫離控制時(shí)含滴,我們經(jīng)常會(huì)發(fā)現(xiàn)自動(dòng)化工具會(huì)像“將油門踩到底”似的將事情搞砸。因?yàn)槿祟惛瞄L(zhǎng)情景思維丐巫,所以我們需要?jiǎng)?chuàng)造機(jī)會(huì)親身參與其中谈况。
● 在不安全的方向上施加阻力勺美。有些行為本身是不安全的。關(guān)機(jī)碑韵、刪除赡茸、阻塞……這些都可能會(huì)中斷服務(wù)。自動(dòng)化工具將迅速執(zhí)行這些操作祝闻,所以應(yīng)該使用一個(gè)調(diào)速器占卧,讓人們獲得時(shí)間來(lái)干預(yù)。
● 考慮使用響應(yīng)曲線联喘。在規(guī)定范圍內(nèi)操作就是安全的华蜒。但如果在該范圍之外,行動(dòng)就應(yīng)該遇到相應(yīng)的阻力豁遭,以減緩速度叭喜。
十三、總結(jié)
系統(tǒng)失效是不可避免的蓖谢。我們的系統(tǒng)及其所依賴的系統(tǒng)捂蕴,將會(huì)以大大小小的方式失效。穩(wěn)定性的反模式放大了瞬態(tài)事件闪幽,它們會(huì)加速裂紋的蔓延啥辨。避免反模式雖然不能防止壞事發(fā)生,但當(dāng)災(zāi)禍來(lái)臨時(shí)盯腌,這樣做有助于將損害降到最低溉知。
無(wú)論面臨什么困難,只要明智地運(yùn)用本章所描述的穩(wěn)定性模式腕够,就會(huì)令軟件始終保持運(yùn)行着倾。正確的判斷是成功地運(yùn)用這些模式的關(guān)鍵。因此燕少,要本著不信有好事的原則審查軟件的需求卡者;以懷疑和不信任的眼光審視其他企業(yè)系統(tǒng),防備這些系統(tǒng)在你背后“捅刀子”客们;識(shí)別這些威脅崇决,并運(yùn)用與每種威脅相關(guān)的穩(wěn)定性模式;“迫害妄想”般的自我保護(hù)也是不錯(cuò)的工程實(shí)踐底挫。
第16章
二恒傻、過(guò)程和組織
開發(fā)和運(yùn)維之間的邊界不僅已經(jīng)模糊,而且已經(jīng)被全部打亂并重新組合了建邓。這種狀況甚至在DevOps這個(gè)詞廣為人知之前就已經(jīng)出現(xiàn)了(參閱本節(jié)框注)盈厘。虛擬化和云計(jì)算的興起,實(shí)現(xiàn)了基礎(chǔ)設(shè)施的可編程性官边。開源運(yùn)維工具同樣實(shí)現(xiàn)了運(yùn)維工作的可編程性沸手。虛擬機(jī)鏡像以及后來(lái)的容器和unikernel的出現(xiàn)外遇,意味著程序都變成了“操作系統(tǒng)”。
回顧第7章介紹的各個(gè)層級(jí)契吉,可以發(fā)現(xiàn)整個(gè)層級(jí)棧都需要軟件開發(fā)跳仿。
同樣,這個(gè)層級(jí)棧也需要整體運(yùn)維【杈В現(xiàn)在菲语,基礎(chǔ)設(shè)施(過(guò)去由運(yùn)維團(tuán)隊(duì)負(fù)責(zé))中出現(xiàn)了大量的可編程組件,這些基礎(chǔ)設(shè)施發(fā)展成了平臺(tái)惑灵,其他軟件都能在這個(gè)平臺(tái)上運(yùn)行山上。無(wú)論是在云上還是在自己的數(shù)據(jù)中心里,都需要有一個(gè)平臺(tái)團(tuán)隊(duì)英支,將應(yīng)用程序開發(fā)團(tuán)隊(duì)視作其客戶佩憾,為應(yīng)用程序所需的常用功能以及第10章介紹的控制層工具提供API和命令行配置。
這個(gè)列表很長(zhǎng)潭辈,而且隨著時(shí)間的推移會(huì)變得更長(zhǎng)鸯屿。雖然其中的每一項(xiàng)都可以由單個(gè)團(tuán)隊(duì)自行構(gòu)建澈吨,但若將它們相互孤立起來(lái)把敢,那就毫無(wú)價(jià)值了。平臺(tái)團(tuán)隊(duì)要記住谅辣,當(dāng)前實(shí)施的機(jī)制修赞,應(yīng)該允許其他團(tuán)隊(duì)自行配置,這一點(diǎn)很重要桑阶。換句話說(shuō)柏副,平臺(tái)團(tuán)隊(duì)不應(yīng)該實(shí)施各個(gè)應(yīng)用程序開發(fā)團(tuán)隊(duì)特定的監(jiān)控規(guī)則。相反蚣录,平臺(tái)團(tuán)隊(duì)?wèi)?yīng)該為應(yīng)用程序開發(fā)團(tuán)隊(duì)提供API割择,使其能在平臺(tái)提供的監(jiān)控服務(wù)上,實(shí)施自己的監(jiān)控規(guī)則萎河。同樣荔泳,平臺(tái)團(tuán)隊(duì)也不會(huì)為各個(gè)應(yīng)用程序開發(fā)團(tuán)隊(duì)構(gòu)建API網(wǎng)關(guān),而是構(gòu)建一種服務(wù)虐杯,各個(gè)應(yīng)用程序開發(fā)團(tuán)隊(duì)能夠利用該服務(wù)構(gòu)建各自的API網(wǎng)關(guān)玛歌。
(平臺(tái)團(tuán)隊(duì)與應(yīng)用程序開發(fā)團(tuán)隊(duì)的職責(zé)分工)
平臺(tái)團(tuán)隊(duì)要記住,當(dāng)前實(shí)施的機(jī)制擎椰,應(yīng)該允許其他團(tuán)隊(duì)自行配置支子,這一點(diǎn)很重要。換句話說(shuō)达舒,平臺(tái)團(tuán)隊(duì)不應(yīng)該實(shí)施各個(gè)應(yīng)用程序開發(fā)團(tuán)隊(duì)特定的監(jiān)控規(guī)則值朋。相反叹侄,平臺(tái)團(tuán)隊(duì)?wèi)?yīng)該為應(yīng)用程序開發(fā)團(tuán)隊(duì)提供API,使其能在平臺(tái)提供的監(jiān)控服務(wù)上吞歼,實(shí)施自己的監(jiān)控規(guī)則圈膏。同樣,平臺(tái)團(tuán)隊(duì)也不會(huì)為各個(gè)應(yīng)用程序開發(fā)團(tuán)隊(duì)構(gòu)建API網(wǎng)關(guān)篙骡,而是構(gòu)建一種服務(wù)稽坤,各個(gè)應(yīng)用程序開發(fā)團(tuán)隊(duì)能夠利用該服務(wù)構(gòu)建各自的API網(wǎng)關(guān)。
必須讓應(yīng)用程序開發(fā)團(tuán)隊(duì)糯俗,而不是平臺(tái)團(tuán)隊(duì)尿褪,負(fù)責(zé)應(yīng)用程序的可用性。相反得湘,平臺(tái)的可用性是衡量平臺(tái)團(tuán)隊(duì)的標(biāo)準(zhǔn)杖玲。
平臺(tái)團(tuán)隊(duì)需要以聚焦客戶為導(dǎo)向,其客戶就是應(yīng)用程序開發(fā)人員淘正。這與舊的開發(fā)團(tuán)隊(duì)與運(yùn)維團(tuán)隊(duì)的劃分理念截然不同摆马。
四、在團(tuán)隊(duì)級(jí)別實(shí)現(xiàn)自治
亞馬遜公司創(chuàng)始人兼首席執(zhí)行官Jeff Bezos的“兩個(gè)比薩團(tuán)隊(duì)”規(guī)則:每個(gè)團(tuán)隊(duì)的規(guī)模不應(yīng)該超過(guò)能被兩張大號(hào)比薩喂飽的人數(shù)鸿吆。
但“兩個(gè)比薩團(tuán)隊(duì)”不僅僅是減少團(tuán)隊(duì)人數(shù)的問(wèn)題囤采,而是需要減少團(tuán)隊(duì)的外部依賴,減少各種上下游的評(píng)審和審批惩淳,包括架構(gòu)設(shè)計(jì)蕉毯、發(fā)布管理、變更管理思犁、
這個(gè)概念不僅僅是指為一個(gè)項(xiàng)目分配幾個(gè)編程人員代虾,這實(shí)際上是如何讓一個(gè)小組能夠?qū)崿F(xiàn)自給自足,并能將成果一直推入生產(chǎn)環(huán)境的問(wèn)題激蹲∶弈ィ縮小每個(gè)團(tuán)隊(duì)的規(guī)模,需要大量的工具和基礎(chǔ)設(shè)施的支持学辱。諸如防火墻乘瓤、負(fù)載均衡器和存儲(chǔ)區(qū)域網(wǎng)絡(luò)等專業(yè)硬件,都必須有API包裹在其周圍项郊,這樣每個(gè)團(tuán)隊(duì)才能管理自己的配置馅扣,而不會(huì)對(duì)其他人造成嚴(yán)重破壞。16.2.1節(jié)所討論的平臺(tái)團(tuán)隊(duì)着降,此時(shí)就能扮演重要角色差油。平臺(tái)團(tuán)隊(duì)的目標(biāo),必須是實(shí)現(xiàn)和促進(jìn)上述團(tuán)隊(duì)規(guī)模的自治。
本博客(liqipeng)除非已明確說(shuō)明轉(zhuǎn)載蓄喇,否則皆為liqipeng原創(chuàng)或者整理发侵,轉(zhuǎn)載請(qǐng)保留此鏈接:https://www.cnblogs.com/liqipeng/p/15377850.html。
如果你覺(jué)得這篇文章對(duì)你有幫助或者使你有所啟發(fā)妆偏,請(qǐng)點(diǎn)擊右下角的推薦按鈕刃鳄,謝謝,:)