這么牛的畢業(yè)生,來(lái)當(dāng)CTO吧!
時(shí)光如風(fēng)飄渺,眨眼間已經(jīng)在行業(yè)浸潤(rùn)多年了,見過無(wú)數(shù)厲害的人物,也見過更多更多的挫B。
前幾天剛上班,就接到面試一個(gè)畢業(yè)生的任務(wù),讓我感嘆人與人之間的差距。
他的水平,絕對(duì)的完爆工作多年的架構(gòu)師。在下佩服之~
我們的話題,是關(guān)于怎么構(gòu)建一個(gè)可伸縮的高可用、高可靠大型網(wǎng)站。嗯,就讓我們開始吧。
1. 要發(fā)問了
大家都知道,如今的互聯(lián)網(wǎng),數(shù)據(jù)量爆炸,服務(wù)請(qǐng)求飆升,即使是非常小的公司,也可能因?yàn)槟硞€(gè)產(chǎn)品產(chǎn)生不同于往日的數(shù)十倍流量,當(dāng)然這有時(shí)候是個(gè)夢(mèng)想而已。
流量增加就意味著對(duì)后端服務(wù)能力的增加,如何構(gòu)建每秒處理GB數(shù)據(jù)、QPS超過數(shù)十萬(wàn)的大型系統(tǒng),就變成了一個(gè)挑戰(zhàn)。尤其是某些天才的秒殺創(chuàng)意,讓這個(gè)流量變的越發(fā)變態(tài)不可預(yù)料。
在有效的資源下,如何讓系統(tǒng)保持良好的反饋?以支撐老板們的夢(mèng)想呢?你有什么處理方式?或者你有什么體系化的心得體會(huì)要和我分享一下的?
畢業(yè)生微微一笑:“我在這方面正好有點(diǎn)總結(jié),我可以多花點(diǎn)時(shí)間聊聊這個(gè)”。
好吧,洗耳恭聽。
2. 服務(wù)建設(shè)的重要指標(biāo)
我首先要說(shuō)明的是,服務(wù)的建設(shè)要關(guān)注幾個(gè)指標(biāo)。有了目標(biāo)就有了方向,大體上我總結(jié)了四個(gè)”:
- 可用性。 我們要保證服務(wù)的可用性,也就是SLA指標(biāo)。只有服務(wù)能夠正常響應(yīng),錯(cuò)誤率保持在較低的水平,我們的服務(wù)才算正常。
- 服務(wù)性能。 可用性和性能是相輔相成的,服務(wù)性能高了,有限的資源就能夠支撐更多的請(qǐng)求,可用性就會(huì)提高。所以服務(wù)性能優(yōu)化是一個(gè)持續(xù)性的工作,在巨量流量下每1ms的平均性能提升,都是值得追求的。
- 可靠性。 分布式服務(wù)的組件非常多,每個(gè)組件都可能會(huì)產(chǎn)生問題,影響面也不盡相同。如何保證每個(gè)組件的運(yùn)行時(shí)高可靠性,如何保證數(shù)據(jù)的一致性,都是有挑戰(zhàn)的。
- 可觀測(cè)性。 想要獲取服務(wù)優(yōu)化的指標(biāo)數(shù)據(jù),就要求我們的服務(wù),在設(shè)計(jì)開始能夠保證服務(wù)的可觀測(cè)性。宏觀上能夠識(shí)別組件類故障,微觀上能為性能優(yōu)化提供依據(jù)。在HPA等自動(dòng)化伸縮場(chǎng)景中,遙測(cè)數(shù)據(jù)甚至是自動(dòng)化決策的唯一依據(jù)。
對(duì)于一個(gè)服務(wù)來(lái)說(shuō),擴(kuò)容的手段主要有兩種:
- scale-up:垂直擴(kuò)展;
- scale-out:水平擴(kuò)展。
垂直擴(kuò)展通過增加單臺(tái)機(jī)器的配置來(lái)增加單節(jié)點(diǎn)的處理能力。這在某些業(yè)務(wù)場(chǎng)景下是非常有必要的。但我們的服務(wù)更多的是追求水平擴(kuò)展,用更多的機(jī)器來(lái)支撐業(yè)務(wù)的發(fā)展。
只要服務(wù)滿足了橫向擴(kuò)展的能力,滿足無(wú)狀態(tài)的特點(diǎn),剩下的事情就是堆硬件了。聽起來(lái)很美好,但實(shí)際上,對(duì)整個(gè)體系架構(gòu)的挑戰(zhàn)非常的大。
畢業(yè)生的一番分析像極了野雞CTO的發(fā)言,廢話連篇。我暗自點(diǎn)頭,鼓勵(lì)他繼續(xù)深入、細(xì)化下去,拿出點(diǎn)不一樣的東西。
3. 冪等性
如果接口調(diào)用失敗怎么辦?在早期的互聯(lián)網(wǎng)中,因?yàn)榫W(wǎng)絡(luò)原因,這樣的情況可能更嚴(yán)重。HTTP狀態(tài)碼504,就是典型的代表網(wǎng)關(guān)超時(shí)的狀態(tài)。第一次請(qǐng)求可能會(huì)超時(shí)失敗,第二次可能就成功了?,F(xiàn)實(shí)中需要嚴(yán)格重試的接口還是蠻多的,尤其是異步化的加入,使得重試變得更加重要。
但我們也要考慮由于快速重試所造成的重試風(fēng)暴,因?yàn)槌瑫r(shí)本身可能就意味著服務(wù)器已經(jīng)不堪重負(fù),我們沒有任何理由火上澆油。所以,重試都會(huì)有退避算法(exponential backoff),直到真正的結(jié)束請(qǐng)求進(jìn)入異常處理流程。
可以看出,由于超時(shí)和重試機(jī)制的引入,服務(wù)的冪等變的格外重要。它不僅僅是在單臺(tái)機(jī)器上支持重復(fù)的調(diào)用,在整個(gè)分布式集群環(huán)境中同樣保證可以重入多次。
在數(shù)學(xué)上,它甚至有一個(gè)優(yōu)美的函數(shù)公式:
f(f(f(x))) = f(f(x)) = f(x)
一旦接口擁有了冪等性,就有了能夠忍受故障的能力。當(dāng)我們因?yàn)榕及l(fā)的網(wǎng)絡(luò)故障、機(jī)器故障造成少量的服務(wù)調(diào)用失敗時(shí),可以通過重試和冪等很容易的最終完成調(diào)用。
對(duì)于查詢操作來(lái)說(shuō),在數(shù)據(jù)集合不變的情況下,它天然是冪等的,不需要做什么額外的處理。比較有挑戰(zhàn)的是添加和更新操作。
有不少的技術(shù)手段來(lái)保證冪等,比如使用數(shù)據(jù)庫(kù)的唯一索引,使用提前生成好的交易ID,或者使用token機(jī)制來(lái)保證唯一調(diào)用。其中,token機(jī)制被越來(lái)越多的使用,其做法是在請(qǐng)求之前,先請(qǐng)求一個(gè)唯一的tokenId,此后的調(diào)用冪等就圍繞著tokenId進(jìn)行編程。
4. 健康檢查
自從k8s把健康檢查這個(gè)東西標(biāo)準(zhǔn)化之后,健康檢查就成為了一個(gè)服務(wù)的必備選項(xiàng)。在k8s中,分為活躍探針(liveness probe)和 就緒探針(readiness probe)。
活躍探測(cè)主要用來(lái)查明應(yīng)用程序是否處于活動(dòng)狀態(tài)。它只展示應(yīng)用本身的狀態(tài),而不應(yīng)依賴于外部其他系統(tǒng)的健康狀態(tài);就緒探測(cè)指示應(yīng)用程序是否已準(zhǔn)備好接受流量,如果應(yīng)用程序?qū)嵗木途w狀態(tài)為未就緒,則不會(huì)將流量路由到該實(shí)例。
如果你使用了SpringBoot的actuator組件,通過health接口,將很容易獲取這部分功能。當(dāng)容器或者注冊(cè)中心通過health接口判斷到服務(wù)出現(xiàn)了問題,會(huì)自動(dòng)的把問題節(jié)點(diǎn)從節(jié)點(diǎn)列表中摘除,然后再通過一系列探測(cè)機(jī)制在服務(wù)恢復(fù)正常的時(shí)候再把它掛上去。
通過健康檢查機(jī)制,能夠避免流量被調(diào)度到錯(cuò)誤的機(jī)器上去。
5. 服務(wù)自動(dòng)發(fā)現(xiàn)
早期的軟件開發(fā)人員,對(duì)服務(wù)上線的機(jī)制摸的門清,不是因?yàn)樗麄兿胍@樣,而是不得不這樣做。
比如,我要擴(kuò)容一臺(tái)機(jī)器,需要首先測(cè)試這臺(tái)機(jī)器的存活性,然后部署服務(wù),最后再在負(fù)載均衡軟件比如nginx中將這臺(tái)機(jī)器配置上。通常情況下,還要看一下日志,到底有沒有流量到這臺(tái)機(jī)器上來(lái)。
借助于微服務(wù)和持續(xù)集成,我們?cè)僖膊恍枰@么繁雜的上線流程,只需要在頁(yè)面上點(diǎn)一下構(gòu)架、發(fā)布,服務(wù)就能夠自動(dòng)上線,并被其他服務(wù)發(fā)現(xiàn)。
注冊(cè)中心在服務(wù)發(fā)現(xiàn)方面承擔(dān)了非常重要的角色。它相當(dāng)于一個(gè)信息集中地,所有的服務(wù)啟動(dòng)、關(guān)閉,都要上報(bào)到這里;同樣,我想要調(diào)用某些服務(wù),也需要到同一個(gè)注冊(cè)中心去查詢。
注冊(cè)中心相當(dāng)于一個(gè)中介,將這些頻繁的上下線需求和查詢需求,全部統(tǒng)一起來(lái)進(jìn)行管理,現(xiàn)在已經(jīng)成為微服務(wù)的必備設(shè)施。
這些查詢需求可能是非常頻繁的,所以在調(diào)用方本地,同樣也會(huì)存儲(chǔ)一份副本,這樣在注冊(cè)中心出現(xiàn)問題的時(shí)候,不至于因?yàn)榇竽X缺氧而造成大規(guī)模故障。有了副本就有了一致性問題,有注冊(cè)中心通過Pull的方式更新信息,存在數(shù)據(jù)一致性的實(shí)效性。實(shí)效性處理的比較好的是有Push(通知)機(jī)制的組件,能夠在較快的時(shí)間感知服務(wù)的變化。
許多組件可以充當(dāng)服務(wù)注冊(cè)中心,只要它有分布式存儲(chǔ)數(shù)據(jù)的能力和數(shù)據(jù)一致性的能力。比如Eureka、Nacos、Zookeeper、Consul、Redis,甚至數(shù)據(jù)庫(kù),都能勝任這個(gè)角色。
6. 限流
web開發(fā)中,tomcat默認(rèn)是200個(gè)線程池,當(dāng)更多的請(qǐng)求到來(lái),沒有新的線程能夠去處理這個(gè)請(qǐng)求,那這個(gè)請(qǐng)求將會(huì)一直等待在瀏覽器方。表現(xiàn)的形式是,瀏覽器一直在轉(zhuǎn)圈(還沒超過acceptCount),即使你請(qǐng)求的是一個(gè)簡(jiǎn)單的Hello world。
我們可以把這個(gè)過程,也看作是限流。它在本質(zhì)上,是設(shè)置一個(gè)資源數(shù)量上限,超出這個(gè)上限的請(qǐng)求,將被緩沖,或者直接失敗。
對(duì)于高并發(fā)場(chǎng)景下的限流來(lái)說(shuō),它有特殊的含義:它主要是用來(lái)保護(hù)底層資源的。如果你想要調(diào)用某些服務(wù),你需要首先獲取調(diào)用它的許可。限流一般由服務(wù)提供方來(lái)提供,對(duì)調(diào)用方能夠做事的能力進(jìn)行限制。
比如,某個(gè)服務(wù)為A、B、C都提供了服務(wù),但根據(jù)提前申請(qǐng)的流量預(yù)估,限制A服務(wù)的請(qǐng)求為1000/秒、B服務(wù)2000/秒,C服務(wù)1w/秒。在同一時(shí)刻,某些客戶端可能會(huì)出現(xiàn)被拒絕的請(qǐng)求,而某些客戶端能夠正常運(yùn)行,限流被看作是服務(wù)端的自我保護(hù)能力。
常見的限流算法有:計(jì)數(shù)器、漏桶、令牌桶等。但計(jì)數(shù)器算法無(wú)法實(shí)現(xiàn)平滑的限流,在實(shí)際應(yīng)用中使用較少。
7. 熔斷
自從施耐德發(fā)明了斷路器,這個(gè)熔斷的概念席卷了全球。從A股熔斷,到服務(wù)熔斷,大有異曲同工之妙。
熔斷的意思是:當(dāng)電路閉合時(shí),電流可以通過,當(dāng)斷路器打開時(shí),電流停止。
通常情況下,用戶的一個(gè)請(qǐng)求,需要后端多個(gè)服務(wù)配合才能完成工作。后端的這些服務(wù),并不是每一個(gè)都是必須的,如果因?yàn)槠渲械哪硞€(gè)服務(wù)有問題,就把用戶的整個(gè)請(qǐng)求給拒絕掉,那是非常不合理的。
熔斷期望某些服務(wù),在發(fā)生問題時(shí),返回一些默認(rèn)值。整個(gè)請(qǐng)求依然可以正常進(jìn)行下去。
比如風(fēng)控。如果在某個(gè)時(shí)間風(fēng)控服務(wù)不可用了,用戶其實(shí)是應(yīng)該能夠正常交易的。這時(shí)候我們應(yīng)該默認(rèn)風(fēng)控是通過的,然后把這些異常交易倒到另外一個(gè)地方,在風(fēng)控恢復(fù)后再盡快趕在發(fā)貨的之前處理。
從上面的描述可以看出,有的服務(wù),熔斷后簡(jiǎn)單的返回些默認(rèn)數(shù)據(jù)就行,比如推薦服務(wù);但有的服務(wù)就需要有對(duì)應(yīng)的異常流程支持,算是一個(gè)if else;更要命的是,有些業(yè)務(wù)不支持熔斷,那就只能Fail Fast。
一股腦的處理是沒有思考的技術(shù)手段,不是我們所推薦的。
Hystrix、resilience4j、Sentinel等組件,是Java系廣泛使用的工具。通過SpringBoot的集成,這些框架一般用起來(lái)都比較方便,可以達(dá)到配置化編程。
8. 降級(jí)
降級(jí)是一個(gè)比較模糊的說(shuō)法。限流、熔斷,在一定程度上,也可以看作是降級(jí)的一種。但通常所說(shuō)的降級(jí),切入的層次更加高級(jí)一些。
降級(jí)一般考慮的是分布式系統(tǒng)的整體性,從源頭上切斷流量的來(lái)源。比如在雙11的時(shí)候,為了保證交易系統(tǒng),將會(huì)暫停一些不重要的服務(wù),以免產(chǎn)生資源爭(zhēng)占。服務(wù)降級(jí)有人工參與,人為使得某些服務(wù)不可用,多屬于一種業(yè)務(wù)降級(jí)方式。
在什么地方最適合做降級(jí)呢?就是入口。比如Nginx,比如DNS等。
在某些互聯(lián)網(wǎng)應(yīng)用中,會(huì)存在MVP(Minimum Viable Product)這個(gè)概念,意為最小化可行產(chǎn)品,它的SLA要求非常高。圍繞著最小可行性產(chǎn)品,會(huì)有一系列的服務(wù)拆分操作,當(dāng)然某些情況甚至需要重寫。
比如,一個(gè)電商系統(tǒng),在極端情況下,只需要把商品顯示出來(lái),把商品賣出去就行。其他一些支撐性的系統(tǒng),比如評(píng)論、推薦等,都可以臨時(shí)關(guān)掉。在物理部署和調(diào)用關(guān)系上,就要考慮這些情況。
9. 預(yù)熱
請(qǐng)看下面一種情況。
一個(gè)高并發(fā)環(huán)境下的DB,進(jìn)程死亡后進(jìn)行重啟。由于業(yè)務(wù)處在高峰期間,上游的負(fù)載均衡策略發(fā)生了重分配。剛剛啟動(dòng)的DB瞬間接受了1/3的流量,然后load瘋狂飆升,直至再無(wú)響應(yīng)。
原因就是:新啟動(dòng)的DB,各種Cache并沒有準(zhǔn)備完畢,系統(tǒng)狀態(tài)與正常運(yùn)行時(shí)截然不同??赡芷匠?/10的量,就能夠把它帶入死亡。
同理,一個(gè)剛剛啟動(dòng)的JVM進(jìn)程,由于字節(jié)碼并未被JIT編譯器優(yōu)化,在剛啟動(dòng)的時(shí)候,所有接口的響應(yīng)時(shí)間都比較慢。如果調(diào)用它的負(fù)載均衡組件,并沒有考慮這種剛啟動(dòng)的情況,1/n的流量被正常路由到這個(gè)節(jié)點(diǎn),就很容易出現(xiàn)問題。
所以,我們希望負(fù)載均衡組件,能夠依據(jù)JVM進(jìn)程的啟動(dòng)時(shí)間,動(dòng)態(tài)的慢慢加量,進(jìn)行服務(wù)預(yù)熱,直到達(dá)到正常流量水平。
10. 背壓
考慮一下下面兩種場(chǎng)景:
- 沒有限流。請(qǐng)求量過高,有多少收多少,極容易造成后端服務(wù)崩潰或者內(nèi)存溢出
- 傳統(tǒng)限流。你強(qiáng)行規(guī)定了某個(gè)接口最大的承受能力,超出了直接拒絕,但此時(shí)后端服務(wù)是有能力處理這些請(qǐng)求的
如何動(dòng)態(tài)的修改限流的值?這就需要一套機(jī)制。調(diào)用方需要知道被調(diào)用方的處理能力,也就是被調(diào)用方需要擁有反饋的能力。背壓,英文Back Pressure,其實(shí)是一種智能化的限流,指的是一種策略。
背壓思想,被請(qǐng)求方不會(huì)直接將請(qǐng)求端的流量直接丟掉,而是不斷的反饋?zhàn)约旱奶幚砟芰ΑU?qǐng)求端根據(jù)這些反饋,實(shí)時(shí)的調(diào)整自己的發(fā)送頻率。比較典型的場(chǎng)景,就是TCP/IP中使用滑動(dòng)窗口來(lái)進(jìn)行流量控制。
反應(yīng)式編程(Reactive)是觀察者模式的集大成者。它們大多使用事件驅(qū)動(dòng),多是非阻塞的彈性應(yīng)用,基于數(shù)據(jù)流進(jìn)行彈性傳遞。在這種場(chǎng)景下,背壓實(shí)現(xiàn)就簡(jiǎn)單的多。
背壓,讓系統(tǒng)更穩(wěn)定,利用率也更高,它本身?yè)碛懈叩膹椥院椭悄?。比如我們常見的HTTP 429狀態(tài)碼頭,表示的意思就是請(qǐng)求過多,讓客戶端緩一緩,不要那么著急,算是一個(gè)智能的告知。
11. 隔離
即使在同一個(gè)instance中,同類型的資源,有時(shí)候也要做到隔離。一個(gè)比較淺顯的比喻,就是泰坦尼克號(hào),它有多個(gè)船艙。每個(gè)船艙都相互隔離,避免單個(gè)船艙進(jìn)水造成整個(gè)船沉了。
當(dāng)然,泰坦尼克號(hào)帶著騷氣的jack沉了,那是因?yàn)榇撈频奶嗟木壒省?/p>
在有些公司的軟件中,報(bào)表查詢服務(wù)、定時(shí)任務(wù)、普通的服務(wù),都放在同一個(gè)tomcat中。它們使用同一套數(shù)據(jù)庫(kù)連接池,當(dāng)某些報(bào)表接口的請(qǐng)求一上升,其他正常的服務(wù)也無(wú)法使用。這就是混用資源所造成的后果。
除了遵循CQRS來(lái)把服務(wù)拆分,一個(gè)快速的機(jī)制就是把某類服務(wù)的使用資源隔離。比如,給報(bào)表分配一個(gè)單獨(dú)的數(shù)據(jù)庫(kù)連接池,分配一個(gè)單獨(dú)的限流器,它將無(wú)法影響其他服務(wù)。
耦合除了出現(xiàn)在無(wú)狀態(tài)服務(wù)節(jié)點(diǎn),同時(shí)還會(huì)出現(xiàn)在存儲(chǔ)節(jié)點(diǎn)。與其把報(bào)表服務(wù)的存儲(chǔ)和正常業(yè)務(wù)的存儲(chǔ)放在一個(gè)數(shù)據(jù)庫(kù),不如把它們拆開,分別提供服務(wù)。
一個(gè)和尚挑水喝,兩個(gè)和尚也可以挑水喝。原因就是他們?cè)趦蓚€(gè)廟。
12. 異步
如果你比較過BIO和NIO的區(qū)別,就可以看到,我們的服務(wù)其實(shí)大部分時(shí)間都是在等待返回,CPU根本就沒有跑滿。當(dāng)然,NIO是底層的機(jī)制,避免了線程膨脹和頻繁的上下文切換。
服務(wù)的異步化和NIO有點(diǎn)類似,采用之后可以避免無(wú)謂的等待。尤其是當(dāng)調(diào)用路徑冗長(zhǎng)的時(shí)候,異步不會(huì)阻塞,響應(yīng)也會(huì)變的迅速。
單機(jī)時(shí)候,我們會(huì)采用NIO;而在分布式環(huán)境中,我們會(huì)采用MQ。雖然它們是不同的技術(shù),但道理都是相通的。
異步通常涉及到編程模型的改變。同步方式,請(qǐng)求會(huì)一直阻塞,直到有成功,或者失敗結(jié)果的返回。雖然它的編程模型簡(jiǎn)單,但應(yīng)對(duì)突發(fā)的、時(shí)間段傾斜的流量,問題就特別大,請(qǐng)求很容易失敗。異步操作可以平滑的橫向擴(kuò)容,也可以把瞬時(shí)壓力時(shí)間上后移。同步請(qǐng)求,就像拳頭打在鋼板上;異步請(qǐng)求,就像拳頭打在海綿上。你可以想象一下這個(gè)過程,后者肯定是富有彈性,體驗(yàn)更加友好。
13. 緩存
緩存可能是軟件中使用最多的優(yōu)化技術(shù)了。比如,在最核心的CPU里,就存在著多級(jí)緩存;為了消除內(nèi)存和存儲(chǔ)之間的差異,各種類似Redis的緩存框架更是層出不窮。
緩存的優(yōu)化效果是非常好的,可以讓原本載入非常緩慢的頁(yè)面,瞬間秒開;也能讓本是壓力山大的數(shù)據(jù)庫(kù),瞬間清閑下來(lái)。
緩存,本質(zhì)上是為了協(xié)調(diào)兩個(gè)速度差異非常大的組件,通過加入一個(gè)中間層,將常用的數(shù)據(jù)存放在相對(duì)高速的設(shè)備中。
在應(yīng)用開發(fā)中,緩存分為本地緩存和分布式緩存。
那什么叫分布式緩存呢?它其實(shí)是一種集中管理的思想。如果我們的服務(wù)有多個(gè)節(jié)點(diǎn),堆內(nèi)緩存在每個(gè)節(jié)點(diǎn)上都會(huì)有一份;而分布式緩存,所有的節(jié)點(diǎn),共用一份緩存,既節(jié)約了空間,又減少了管理成本。
在分布式緩存領(lǐng)域,使用最多的就是Redis。Redis支持非常豐富的數(shù)據(jù)類型,包括字符串(string)、列表(list)、集合(set)、有序集合(zset)、哈希表(hash)等常用的數(shù)據(jù)結(jié)構(gòu)。當(dāng)然,它也支持一些其他的比如位圖(bitmap)一類的數(shù)據(jù)結(jié)構(gòu)。
所以加下來(lái)的問題一定集中在緩存穿透、擊穿和雪崩,以及一致性上,這個(gè)我就不多聊了。
14. Plan-B
一個(gè)成熟的系統(tǒng)都有B方案,除了異地多活和容災(zāi)等處置方案,Plan-B還以為著我們要為正常的服務(wù)提供異常的通道。
比如,專門運(yùn)行一個(gè)最小可行性系統(tǒng),運(yùn)行公司的核心業(yè)務(wù)。在大面積故障的時(shí)候,將請(qǐng)求全面切換到這個(gè)最小系統(tǒng)上。
Plan-B通常都是全局性的,它保證了公司最基本的服務(wù)能力,我們期望它永遠(yuǎn)用不上。
15. 監(jiān)控報(bào)警
問題之所以成為問題,是因?yàn)樗粝铝俗C據(jù)。沒有證據(jù)的問題,你雖然看到了影響結(jié)果,但是你無(wú)法找到元兇。
而且問題通常都具有人性化,當(dāng)它發(fā)現(xiàn)無(wú)法發(fā)現(xiàn)它的時(shí)候,它總會(huì)再次出現(xiàn)。就如同罪犯發(fā)現(xiàn)了漏洞,還會(huì)再次嘗試?yán)盟?/p>
所以,要想處理線上問題,你需要留下問題發(fā)生的證據(jù),這是重中之重。如果沒有這些東西,你的公司,絕對(duì)會(huì)陷入無(wú)盡的扯皮之中。
日志是最常見的做法。通過在程序邏輯中進(jìn)行打點(diǎn),配合Logback等日志框架,可以快速定位到發(fā)生問題的代碼行。我們需要看一下bug的詳細(xì)發(fā)生過程,對(duì)可能發(fā)生問題的邏輯進(jìn)行詳細(xì)的日志記錄,進(jìn)行更加細(xì)致的日志輸出,在發(fā)生問題的時(shí)候,就可以切換到debug進(jìn)行調(diào)試。
如果是大范圍的bug,那么強(qiáng)烈建議直接在線上進(jìn)行調(diào)試。不太推薦使用Arthas等工具動(dòng)態(tài)的修改字節(jié)碼進(jìn)行測(cè)試,當(dāng)然也不推薦IDEA的遠(yuǎn)程調(diào)試。相反,推薦使用類似金絲雀發(fā)布的方式,導(dǎo)出非常小的一部分流量,構(gòu)造一個(gè)新的版本進(jìn)行測(cè)試。如果你沒有金絲雀發(fā)布平臺(tái),類似Nginx的負(fù)載均衡工具也可以通過權(quán)重做到類似的事情。
日志系統(tǒng)與監(jiān)控系統(tǒng),對(duì)硬件的需求是比較大的,尤其是你的請(qǐng)求體和返回體比較大的情況下,對(duì)存儲(chǔ)和計(jì)算資源的額要求更是高。它的硬件成本,在整個(gè)基礎(chǔ)設(shè)施中,占比也是比較高的。但這些證據(jù)信息,對(duì)分析問題來(lái)說(shuō),是非常有必要的。所以即使比較貴,很多公司依然會(huì)有很大的投入在這上面,包括硬件投入和人力投入。
MTTD和MTTR是兩個(gè)非常重要的指標(biāo),我們一定要加大關(guān)注。
16. 結(jié)尾
我看了一下表,這家伙很能說(shuō),預(yù)定的時(shí)間很快用完了。我揮揮手打?。骸蹦氵€會(huì)哪些東西?簡(jiǎn)單的說(shuō)一下吧!“
”也不是很多。像怎么構(gòu)建一個(gè)DevOps團(tuán)隊(duì)支撐我們開發(fā)、測(cè)試、線上環(huán)境,如何進(jìn)行更深入的性能優(yōu)化,如何進(jìn)行實(shí)際的故障排查。以及一些細(xì)節(jié)問題,比如怎么優(yōu)化操作系統(tǒng),網(wǎng)絡(luò)編程和多線程,我這些還都沒有聊?!?/p>
我說(shuō),”夠了,你已經(jīng)非常優(yōu)秀了“。
”你把自己叫作畢業(yè)生,已經(jīng)碾壓絕大多數(shù)人了。你到底是哪里的畢業(yè)生啊!“
”我是B站的,昨天剛畢業(yè)~“,他靦腆的笑了。
我盯著他的眼睛,也笑了??菽痉甏邯q再發(fā),人可兩度再少年!妙啊。