揭秘软件开发中的达摩克利斯之剑
為什么你的程序總是出現(xiàn) bug?
憑什么讓改 bug 占據(jù)了你大部分的時(shí)間?
看完本文,保證你能設(shè)計(jì)出更穩(wěn)定的程序,擺脫 bug 的纏繞,做項(xiàng)目更安心!
記得我在學(xué)校的時(shí)候,做的那些項(xiàng)目,不是為了應(yīng)付課程作業(yè),就是為了參加比賽時(shí)展示用,因此對(duì)項(xiàng)目的質(zhì)量要求非常低。
到底有多低呢?
大部分的項(xiàng)目,只要基本的功能可以使用,就算完成了,完全不考慮任何的異常情況。甚至只要能成功運(yùn)行一次,讓我截幾張圖放到 PPT 或者實(shí)驗(yàn)報(bào)告里,足夠向老師交差或者應(yīng)付比賽答辯就行。
那項(xiàng)目出現(xiàn) bug 怎么辦呢?
-
如果測(cè)試的時(shí)候發(fā)現(xiàn)有些功能不可用,那很簡(jiǎn)單,不管他,直接 PS 一張正常運(yùn)行的圖就行。
-
如果比賽的時(shí)候發(fā)現(xiàn)有些功能不可用,那也很簡(jiǎn)單,把鍋甩給 “現(xiàn)場(chǎng)網(wǎng)絡(luò)不好” 就行。
但是,這些 “小技巧” 在企業(yè)中是行不通的,企業(yè)級(jí)項(xiàng)目必須為企業(yè)帶來(lái)實(shí)際的價(jià)值,容不得半點(diǎn)馬虎和欺騙。
我第一次進(jìn)入企業(yè)實(shí)習(xí)時(shí),還保留著自己在學(xué)校開(kāi)發(fā)項(xiàng)目的狼性 🐺,只要能夠完成基本功能就行,保證以最快的速度完成開(kāi)發(fā)。
有一天,當(dāng)我洋洋得意準(zhǔn)備早點(diǎn)下班時(shí),測(cè)試同學(xué)走過(guò)來(lái)跟我說(shuō)。
“喂,你的程序有 bug,這里用戶(hù)下單怎么金額是負(fù)的?”
對(duì)于我一個(gè)初入職場(chǎng)的小白,這是人生中第一次有人說(shuō)我的代碼有 bug,我有問(wèn)題,我不對(duì)勁。
當(dāng)時(shí),我腦海的第一個(gè)念頭竟然是怎么把這個(gè) bug 糊弄過(guò)去,而不是怎么去更正!看來(lái)我已經(jīng)養(yǎng)成了非常不好的習(xí)慣。
那之后幾天,我又連續(xù)收到了測(cè)試提出的多個(gè) bug,然后將他們一個(gè)個(gè)改正。如果將這樣一個(gè)漏洞百出的程序發(fā)布上線,帶來(lái)的損失是不可估量的,現(xiàn)在想想仍心有余悸。
這件事之后,我意識(shí)到,在企業(yè)中開(kāi)發(fā)項(xiàng)目,不能只追求開(kāi)發(fā)時(shí)的效率,還要注重項(xiàng)目的穩(wěn)定性,否則帶來(lái)的額外返工時(shí)間遠(yuǎn)比開(kāi)發(fā)時(shí)節(jié)省的時(shí)間要長(zhǎng),而且會(huì)影響同事對(duì)你的看法。如果將開(kāi)發(fā)時(shí)產(chǎn)生的 bug 遺留到線上,后果更是不堪設(shè)想!
后來(lái),在字節(jié)跳動(dòng)和騰訊這兩家大公司工作后,我進(jìn)一步認(rèn)識(shí)到了項(xiàng)目穩(wěn)定性有多重要,并且積累了更多只有在大公司才能學(xué)到的提升項(xiàng)目穩(wěn)定性的經(jīng)驗(yàn)。
我總結(jié)了 10 個(gè)開(kāi)發(fā)中通常不會(huì)考慮到的風(fēng)險(xiǎn)點(diǎn),以及 16 個(gè)減少風(fēng)險(xiǎn)、提升項(xiàng)目穩(wěn)定性的方法,分享給大家~
在分享這些之前,先講個(gè)故事。
達(dá)摩克利斯之劍
古希臘傳說(shuō)中,達(dá)摩克利斯是公元前 4 世紀(jì)意大利敘拉古的僭主(古希臘統(tǒng)治者獨(dú)有的稱(chēng)號(hào))狄?jiàn)W尼修斯二世的朝臣,他非常喜歡奉承狄?jiàn)W尼修斯。
他奉承道:“作為一個(gè)擁有權(quán)力和威信的偉人,狄?jiàn)W尼修斯實(shí)在很幸運(yùn)。”
于是狄?jiàn)W尼修斯提議與他交換一天的身份,那他就可以嘗試到首領(lǐng)的命運(yùn)。
在晚上舉行的宴會(huì)里,達(dá)摩克利斯非常享受成為國(guó)王的感覺(jué)。當(dāng)晚餐快結(jié)束的時(shí)候,他抬頭才注意到王位上方僅用一根馬鬃懸掛著的利劍。他立即失去了對(duì)美食和美女的興趣,并請(qǐng)求僭主放過(guò)他,他再也不想得到這樣的幸運(yùn)。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-mdnK2fxd-1607088076658)(http://qju0b2x8b.hd-bkt.clouddn.com/image-20201130214411651(1)].png)
這個(gè)故事告訴我們什么呢?
在和平安寧之后,時(shí)刻存在著危險(xiǎn)與不安。
當(dāng)一個(gè)人獲取多少榮譽(yù)和地位,他都要付出同樣多的代價(jià)。
地位越高,看似越安全,實(shí)則越危險(xiǎn)。
居安思危,對(duì)隨時(shí)可能帶來(lái)的嚴(yán)重后果,要做到謹(jǐn)慎。
那么這和軟件開(kāi)發(fā)又有什么關(guān)系呢?下面就讓我來(lái)揭秘軟件開(kāi)發(fā)中的達(dá)摩克利斯之劍。
危機(jī)四伏
“在和平安寧之后,時(shí)刻存在著危險(xiǎn)與不安。”
軟件開(kāi)發(fā)正是如此,表面上機(jī)器是 “死” 的,只會(huì)按照人輸入的指令或編好的程序來(lái)執(zhí)行,一成不變,聽(tīng)話的很。好像我們寫(xiě)好代碼扔到機(jī)器上后,就可以高枕無(wú)憂(yōu)。
但真的是這樣么?我們真的可以信任機(jī)器和程序么?
其實(shí),在程序世界中危機(jī)四伏,人為因素、環(huán)境因素等可能都會(huì)對(duì)我們的程序產(chǎn)生影響。因此,我們必須時(shí)刻堅(jiān)守軟件開(kāi)發(fā)的不信任原則,保持 overly pessimistic(過(guò)于悲觀),把和程序有關(guān)的一切請(qǐng)求、服務(wù)、接口、返回值、機(jī)器、框架、中間件等等都當(dāng)做不可信的,步步為營(yíng)、處處設(shè)防。
那為什么寫(xiě)個(gè)代碼要這么小心翼翼,什么都不信任呢?
大項(xiàng)目的苦衷
“當(dāng)一個(gè)人獲取多少榮譽(yù)和地位,他都要付出同樣多的代價(jià)。”
軟件開(kāi)發(fā)中,項(xiàng)目?jī)r(jià)值越大,需要承受的壓力也越大,來(lái)聽(tīng)聽(tīng)大項(xiàng)目的苦衷吧。
我是一個(gè)身價(jià)過(guò)億的大項(xiàng)目,每天服務(wù)著上千萬(wàn)的用戶(hù),幫助他們獲得知識(shí)與快樂(lè)。
我的小伙伴們只看到我身上的光環(huán)和榮耀,但是他們看不到我背負(fù)的壓力和風(fēng)險(xiǎn),今天終于有機(jī)會(huì)和大家傾訴我的苦衷了。
記得很多年前,我還是個(gè)孩子,只有幾個(gè)小主人開(kāi)發(fā)我,那段時(shí)間,我成長(zhǎng)的很快。雖然只有幾十個(gè)人使用我,但我感到非常輕松和快樂(lè),偶爾偷會(huì)兒懶,也不會(huì)被人發(fā)現(xiàn)。
后來(lái),我的功能越來(lái)越多,越來(lái)越強(qiáng)大。每天有數(shù)之不盡的新面孔來(lái)和我打招呼,并享受我提供的服務(wù)。漸漸地,更多開(kāi)發(fā)者在我身上留下了印記,我感覺(jué)自己正在變得復(fù)雜,也開(kāi)始感受到了壓力。我再也找不到機(jī)會(huì)偷懶,因?yàn)槲乙坏┬菹?#xff0c;就會(huì)讓我的主人們損失一比不小的財(cái)富。
如今,我已經(jīng)是一個(gè)成熟的大項(xiàng)目了,每天有上千萬(wàn)的用戶(hù)依賴(lài)我,我終于擁有了更大的價(jià)值,卻也增加了很多煩惱,感受到了更大的危險(xiǎn)。
首先,同時(shí)服務(wù)千萬(wàn)用戶(hù),每秒鐘都可能會(huì)有幾十萬(wàn)、甚至幾百萬(wàn)個(gè)請(qǐng)求需要我來(lái)處理,因此我必須每時(shí)每刻無(wú)休止地高負(fù)載工作,且不說(shuō)休息,哪怕稍微慢了一點(diǎn),就會(huì)遭到用戶(hù)的投訴,主人們也會(huì)因此受到批評(píng)。
我的運(yùn)行,必須依靠很多兄弟們的支撐,因此我必須和兄弟們好好相處,哪怕一個(gè)兄弟倒了,我都會(huì)受到影響。
在我強(qiáng)大的實(shí)力背后,有一顆非常脆弱的心。經(jīng)歷了那么多次的強(qiáng)化和改造,我的功能逐漸變多的同時(shí),也因此被植入了各種框架和插件,體積像滾雪球一般越來(lái)越大,不知道什么時(shí)候就會(huì)爆炸。以至于主人們每次改動(dòng)我時(shí)都要萬(wàn)分謹(jǐn)慎,我的成長(zhǎng)也變得十分緩慢。
然而最讓我感到恐懼的,是那些壞家伙們!
他們和正常的用戶(hù)不同,有的不斷制造請(qǐng)求,試圖將我擊垮。有的繞到我的背后,試圖直接控制我。有的對(duì)我虎視眈眈,監(jiān)視并記錄我的一舉一動(dòng)。還有的嘗試各種非法操作,想從我身上牟取暴利。
作為一個(gè)大項(xiàng)目真是太累了,我不知道我還能堅(jiān)持多久。
真的可信么?
“地位越高,看似越安全,實(shí)則越危險(xiǎn)。”
如今是一個(gè)軟件開(kāi)源和共享的時(shí)代,我們?cè)陂_(kāi)發(fā)項(xiàng)目時(shí),或多或少會(huì)使用到網(wǎng)上現(xiàn)有的資源,比如依賴(lài)包、工具、組件、框架、接口、現(xiàn)成的云服務(wù)等等,這些資源能夠大大提升我們的開(kāi)發(fā)效率。
就拿云服務(wù)來(lái)說(shuō),幾乎已經(jīng)成了我們開(kāi)發(fā)必備的資源,以前我們想要做一個(gè)網(wǎng)站,可能需要自己買(mǎi)一臺(tái)物理服務(wù)器,然后連通網(wǎng)絡(luò),再把項(xiàng)目部署上去。而如今,直接登錄大公司的云官網(wǎng)(像騰訊云、阿里云),然后租一臺(tái)云服務(wù)器就行了,非常省事。
再說(shuō)說(shuō)現(xiàn)在主流的開(kāi)發(fā)框架,以前做一個(gè)簡(jiǎn)單的網(wǎng)站界面可能只會(huì)使用 HTML、CSS、JavaScript 這三種最基礎(chǔ)的技術(shù),而如今,網(wǎng)站的樣式和交互越來(lái)越復(fù)雜,我們不得不使用一些知名的框架來(lái)提升開(kāi)發(fā)效率,比如 Vue 和 React。
聽(tīng)起來(lái)好像沒(méi)有任何問(wèn)題,你也根本不會(huì)去懷疑什么,因?yàn)?strong>我們天生帶著對(duì)大公司,或者說(shuō)是對(duì)名氣的信任。
但是,你知道么,當(dāng)你決定使用其他人的資源時(shí),你就已經(jīng)把項(xiàng)目系統(tǒng)的部分掌控權(quán)、甚至可能是半條命,都交出去了。
那么不妨思考一下,你使用的這些資源,真的可信么?
下面 10 個(gè)問(wèn)題,可能改變你對(duì)開(kāi)發(fā)的認(rèn)知。
1. 開(kāi)發(fā)工具可信么?
我們通常是在大而全的開(kāi)發(fā)工具中編寫(xiě)代碼,比如 JetBrains IDEA 或者 Vscode。很多剛開(kāi)始寫(xiě)代碼的同學(xué)、甚至是一些經(jīng)驗(yàn)豐富的老手,都對(duì)開(kāi)發(fā)工具保持絕對(duì)的信任。
比如你在鍵盤(pán)上敲擊 a,那編輯器界面上顯示的一定是 a。
但是,由于內(nèi)存不足等種種原因,開(kāi)發(fā)工具其實(shí)也會(huì)抽風(fēng)。
比如你想要調(diào)用某個(gè)函數(shù),通常敲擊函數(shù)名前幾個(gè)字母后,開(kāi)發(fā)工具就會(huì)自動(dòng)給你提示完整的函數(shù)名,但如果開(kāi)發(fā)工具沒(méi)有給你提示,你首先懷疑的是這個(gè)函數(shù)不存在,而不是編輯器沒(méi)有按預(yù)期給出提示。遇到這種情況,可以稍等編輯器一下,或者進(jìn)一步確認(rèn)函數(shù)是否真的不存在,而不是立刻創(chuàng)建一個(gè)新的函數(shù)。
又或是項(xiàng)目無(wú)法運(yùn)行,怎么排查都覺(jué)得沒(méi)問(wèn)題,這時(shí)不妨重新啟動(dòng)下開(kāi)發(fā)工具,或者清理一下緩存,說(shuō)不定項(xiàng)目就能正常運(yùn)行了!
還有很多非常有意思的情況,比如編輯器一片大紅,各種提示錯(cuò)誤,但是項(xiàng)目依然能成功運(yùn)行。
因此,不要絕對(duì)相信開(kāi)發(fā)工具。
2. 開(kāi)源項(xiàng)目可信么?
現(xiàn)在是一個(gè)軟件開(kāi)源的時(shí)代,在 GitHub 等開(kāi)源項(xiàng)目平臺(tái)上能夠找到大量?jī)?yōu)秀的開(kāi)源項(xiàng)目,好的開(kāi)源項(xiàng)目甚至可以得到 10 萬(wàn)多個(gè)關(guān)注,那這些知名的開(kāi)源項(xiàng)目可信么?
不完全可信!從每個(gè)開(kāi)源項(xiàng)目的 Issues 就能看出這點(diǎn),而且通常越大的項(xiàng)目,被發(fā)現(xiàn)的問(wèn)題越多,比如 Vue 項(xiàng)目,累積提出并關(guān)閉了 8000 多個(gè)問(wèn)題。
我記得自己有一次使用知名的開(kāi)源服務(wù)器 Tomcat,就遇到了 bug,每次接受到特定的請(qǐng)求都會(huì)報(bào)錯(cuò)。剛開(kāi)始我根本沒(méi)有懷疑是 Tomcat 的問(wèn)題,而是絞盡腦汁地想自己的代碼哪里寫(xiě)錯(cuò)了。后來(lái)經(jīng)過(guò)反復(fù)的排查和搜索,終于確認(rèn)了就是 Tomcat 本身的 bug!
雖然開(kāi)源項(xiàng)目并不完全可信,但是相對(duì)于私有項(xiàng)目而言,所有對(duì)項(xiàng)目感興趣的同學(xué)可以共同發(fā)現(xiàn)項(xiàng)目中的問(wèn)題,并加以解決,在一定程度上還是能夠提高項(xiàng)目的可靠性的。
3. 依賴(lài)庫(kù)可信么?
我們?cè)陂_(kāi)發(fā)項(xiàng)目時(shí),通常會(huì)用到大量的依賴(lài)庫(kù)。直接在官方依賴(lài)源(比如 Maven 和 npm)搜索依賴(lài)庫(kù),然后使用包管理器,用一行命令或者編寫(xiě)配置文件就能夠讓其自動(dòng)安裝依賴(lài),非常方便。
但是,這些發(fā)布到官方源的依賴(lài)庫(kù),就可信么?
且不說(shuō)基本每個(gè)開(kāi)發(fā)者都有機(jī)會(huì)發(fā)布依賴(lài)庫(kù)到官方,就算是互聯(lián)網(wǎng)大公司的依賴(lài)庫(kù),也未必可信。
給我印象最深刻的就是阿里巴巴的 JSON 序列化類(lèi)庫(kù) fastjson,幾乎無(wú)人不知、無(wú)人不曉,因?yàn)槠錁O快的解析速度廣受好評(píng)。但是,這個(gè)庫(kù)被多次曝光存在高危漏洞,可以讓攻擊者遠(yuǎn)程執(zhí)行命令!一般的開(kāi)發(fā)者根本不會(huì)發(fā)現(xiàn)這點(diǎn),從而給項(xiàng)目帶來(lái)了極大的危害。
因此,在選用依賴(lài)庫(kù)的時(shí)候,要做好充分的調(diào)研,盡量確認(rèn)依賴(lài)庫(kù)的安全,并且保證不要和已有的依賴(lài)沖突。
4. 編程語(yǔ)言可信么?
Java 是一種強(qiáng)類(lèi)型語(yǔ)言,具有健壯性。這句話我相信所有學(xué)過(guò) Java 的同學(xué)都再熟悉不過(guò)了。但是,強(qiáng)類(lèi)型編程語(yǔ)言就一定可信么?
這里可能有同學(xué)就要表示懷疑了,如果我們一直使用的最基礎(chǔ)最底層的編程語(yǔ)言都存在 bug,那我們?cè)趺慈ハ嘈沤⒃谶@些編程語(yǔ)言上的框架呢?
然而真相是,所有的編程語(yǔ)言都有 bug!而且基本每次編程語(yǔ)言發(fā)布新版本時(shí)都會(huì)對(duì)一些歷史 bug 進(jìn)行修正。就 Java 而言,甚至還有一個(gè)專(zhuān)門(mén)記錄 bug 的數(shù)據(jù)庫(kù)!
但是,對(duì)于大多數(shù)開(kāi)發(fā)者來(lái)說(shuō),我相信即使在程序中偶然觸發(fā)了編程語(yǔ)言本身的 bug,也沒(méi)有足夠的自信去質(zhì)疑,而是直接修改代碼來(lái)繞過(guò)。
確實(shí),質(zhì)疑編程語(yǔ)言需要一定的基礎(chǔ)和知識(shí)儲(chǔ)備,但是一旦發(fā)現(xiàn)了程序中莫名其妙的問(wèn)題,建議大家不要直接忽略,可以花一些時(shí)間去探索研究,說(shuō)不定你就成功地發(fā)現(xiàn)了一個(gè)重大的 bug,也能夠加深對(duì)這門(mén)編程語(yǔ)言的理解。
5. 服務(wù)器可信么?
服務(wù)器是項(xiàng)目賴(lài)以生存的宿主,服務(wù)器的性能和穩(wěn)定性將直接影響到項(xiàng)目進(jìn)程。
無(wú)論是個(gè)人開(kāi)發(fā)者還是企業(yè),通常都會(huì)直接租用大公司提供的云服務(wù)器來(lái)部署項(xiàng)目,省去了自己搭建和維護(hù)的麻煩。
但是大公司的云服務(wù)器就可信么?
不完全可信!即使現(xiàn)在的云服務(wù)器提供商都承諾自己的服務(wù) SLA(服務(wù)級(jí)別協(xié)議)可以達(dá)到 5 個(gè) 9(99.999% 一年約宕機(jī) 5 分鐘),甚至 6 個(gè) 9(99.9999% 一年約宕機(jī) 30 秒),但是仍然存在一定的風(fēng)險(xiǎn)。
有一個(gè)非常有名的案例,在 2013 年,中國(guó)最大的社交通訊軟件出現(xiàn)大規(guī)模的故障,多達(dá)幾億用戶(hù)受到影響。原因竟然是,市政道路建設(shè)的一個(gè)不注意,把網(wǎng)絡(luò)光纜挖斷了,就導(dǎo)致該軟件所在服務(wù)器的無(wú)法訪問(wèn)。
除了可用性的不可信之外,可能還有一些安全隱私方面的問(wèn)題。當(dāng)然云服務(wù)商通常是不會(huì)獲取用戶(hù)的數(shù)據(jù)的,但也沒(méi)有辦法絕對(duì)相信他們。畢竟數(shù)據(jù)的隱私對(duì)企業(yè)至關(guān)重要,這也是為什么大的公司都會(huì)搭建屬于自己的服務(wù)器機(jī)房和網(wǎng)絡(luò)。
6. 數(shù)據(jù)庫(kù)可信么?
企業(yè)中的大多數(shù)業(yè)務(wù)數(shù)據(jù)都是存放在數(shù)據(jù)庫(kù)中的,通過(guò)項(xiàng)目后端程序來(lái)操作和查詢(xún)數(shù)據(jù)庫(kù)中的數(shù)據(jù)。
和服務(wù)器一樣,我們可以使用軟件自己搭建數(shù)據(jù)庫(kù),比如 MySQL,也可以直接租用大公司的云數(shù)據(jù)庫(kù),那么數(shù)據(jù)庫(kù)可信么?
其實(shí)在企業(yè)后端項(xiàng)目中,數(shù)據(jù)庫(kù)通常是性能瓶頸,相對(duì)比較脆弱,當(dāng)訪問(wèn)并發(fā)量大一點(diǎn)時(shí),數(shù)據(jù)庫(kù)的查詢(xún)性能就會(huì)下降,嚴(yán)重時(shí)可能整個(gè)宕機(jī)!即使是大公司提供的云數(shù)據(jù)庫(kù)服務(wù),遇到慢查詢(xún)(需要較長(zhǎng)時(shí)間的查詢(xún))時(shí),可能也無(wú)從應(yīng)對(duì)。
數(shù)據(jù)庫(kù)中的數(shù)據(jù)其實(shí)也未必可信,有時(shí)管理員的一個(gè)誤操作,不小心刪除數(shù)據(jù)或添加了一條錯(cuò)誤數(shù)據(jù),可能就會(huì)影響用戶(hù),造成損失。更有甚者,竟然刪庫(kù)跑路,不講碼德!
因此,不要過(guò)于信任數(shù)據(jù)庫(kù),應(yīng)當(dāng)使用緩存之類(lèi)的技術(shù)幫助數(shù)據(jù)庫(kù)分擔(dān)壓力,并定期備份。否則一旦數(shù)據(jù)庫(kù)宕機(jī)或數(shù)據(jù)丟失,帶來(lái)的損失是不可估量的!
7. 緩存服務(wù)可信么?
緩存是開(kāi)發(fā)高性能程序必備的技術(shù),通過(guò)將數(shù)據(jù)庫(kù)等查詢(xún)較慢的數(shù)據(jù)存放在內(nèi)存中,直接從內(nèi)存中讀取數(shù)據(jù),以提升查詢(xún)性能。有了緩存之后,項(xiàng)目不僅能夠支持更多人同時(shí)查詢(xún)數(shù)據(jù),還能夠保護(hù)數(shù)據(jù)庫(kù)。
目前比較主流的緩存技術(shù)有 Redis、Memcached 等,可以自己在服務(wù)器搭建,也可以直接租用大公司提供的云緩存服務(wù)。
那么緩存服務(wù)是否可信呢?
項(xiàng)目的并發(fā)量不是特別大的話,一般的緩存技術(shù)就足以支持了,但是如果項(xiàng)目的量級(jí)很大,可能緩存也無(wú)法承受住壓力,嚴(yán)重時(shí)就會(huì)宕機(jī)。而一旦緩存掛掉,大量的查詢(xún)命令會(huì)直接請(qǐng)求數(shù)據(jù)庫(kù),于是數(shù)據(jù)庫(kù)也會(huì)在瞬間掛掉,嚴(yán)重時(shí)還會(huì)導(dǎo)致整個(gè)項(xiàng)目癱瘓!
因此,在使用緩存時(shí),需要對(duì)并發(fā)量進(jìn)行評(píng)估,通過(guò)搭建集群和數(shù)據(jù)同步保證高可用性。此外,還要預(yù)防緩存雪崩、緩存穿透、緩存擊穿等問(wèn)題,簡(jiǎn)單解釋一下。
緩存雪崩:指大量緩存在同一時(shí)間過(guò)期,請(qǐng)求都訪問(wèn)不到緩存,全部打到數(shù)據(jù)庫(kù)上,導(dǎo)致數(shù)據(jù)庫(kù)掛掉。
緩存穿透:持續(xù)訪問(wèn)緩存中不存在的 key,導(dǎo)致請(qǐng)求繞過(guò)緩存,直接打到數(shù)據(jù)庫(kù)上,導(dǎo)致數(shù)據(jù)庫(kù)掛掉。
緩存擊穿:一個(gè)被大量請(qǐng)求高頻訪問(wèn)的熱點(diǎn) key 突然過(guò)期,導(dǎo)致請(qǐng)求瞬間全部打到數(shù)據(jù)庫(kù)上,導(dǎo)致數(shù)據(jù)庫(kù)掛掉。
如果不預(yù)防這三個(gè)問(wèn)題,即使是租用大公司的緩存服務(wù),也一樣吹彈可破。
8. 對(duì)象存儲(chǔ)可信么?
項(xiàng)目中,經(jīng)常會(huì)有用戶(hù)上傳圖片或文件的功能,這類(lèi)數(shù)據(jù)通常較大,用數(shù)據(jù)庫(kù)存儲(chǔ)不太方便。雖然我們可以將文件直接存到服務(wù)器上,但更好的做法是使用專(zhuān)門(mén)的對(duì)象存儲(chǔ)服務(wù)。
可以簡(jiǎn)單地把對(duì)象存儲(chǔ)當(dāng)做一個(gè)大的文件夾,我們可以通過(guò)它直接上傳和下載文件。大的云服務(wù)商也都提供了專(zhuān)業(yè)的對(duì)象存儲(chǔ)服務(wù),而無(wú)需自己搭建,那么對(duì)象存儲(chǔ)可信么?
一般情況下,上傳到對(duì)象存儲(chǔ)的文件是不會(huì)缺失或丟失的,而且還可以將已上傳的數(shù)據(jù)進(jìn)行跨園區(qū)同步,起到備份的作用。
但是,記得有一次,上傳到對(duì)象存儲(chǔ)上的文件和源文件竟然不一致,大小足足少了 1M。起初我以為是文件上傳到對(duì)象存儲(chǔ)時(shí),會(huì)自動(dòng)被壓縮,但是將對(duì)象存儲(chǔ)中的文件下載到本地后,發(fā)現(xiàn)的確和源文件不一致!雖然出現(xiàn)這種情況的概率極其小,但從那一刻起,我再也不相信對(duì)象存儲(chǔ)了。
再用自己的真實(shí)經(jīng)歷來(lái)聊聊對(duì)象存儲(chǔ)的跨園區(qū)同步。因?yàn)閭€(gè)人負(fù)責(zé)的業(yè)務(wù)比較重要,萬(wàn)一單個(gè)機(jī)房整體掛掉,可能分分鐘是幾十萬(wàn)元的損失!因此我為對(duì)象存儲(chǔ)配置了自動(dòng)跨園區(qū)同步,將文件先上傳至廣州機(jī)房,然后數(shù)據(jù)會(huì)自動(dòng)同步到上海機(jī)房,且運(yùn)維同學(xué)承諾自動(dòng)同步的延遲不超過(guò) 15 分鐘。
我相信大部分開(kāi)發(fā)者配置數(shù)據(jù)同步后也就不管了,相信它一定會(huì)自動(dòng)同步的。結(jié)果后面我編寫(xiě)程序去做同步監(jiān)控、對(duì)比數(shù)據(jù)時(shí),發(fā)現(xiàn)經(jīng)常出現(xiàn)數(shù)據(jù)未同步的情況,比例高達(dá) 10%!
因此,不能完全相信對(duì)象存儲(chǔ),雖然大部分情況下大公司的對(duì)象存儲(chǔ)服務(wù)很可靠,但不能確保萬(wàn)無(wú)一失。尤其是同步備份的場(chǎng)景下,是否真的同步成功了,又有多少同學(xué)關(guān)心過(guò)呢?不妨寫(xiě)個(gè)程序去驗(yàn)證和保障。
9. API 接口可信么?
在開(kāi)發(fā)中,我們經(jīng)常會(huì)調(diào)用其他系統(tǒng)提供的 API 接口來(lái)輕松實(shí)現(xiàn)某種功能。比如查詢(xún)某地的天氣,可以直接調(diào)用其他人提供的天氣查詢(xún)接口,而無(wú)需自己編寫(xiě)。我們也可以提供 API 接口給其他人使用,尤其是在微服務(wù)架構(gòu)中,各服務(wù)之間都是以接口調(diào)用的形式實(shí)現(xiàn)交互協(xié)作的。
幾乎所有的 API 接口提供者都會(huì)說(shuō)自己的接口有多安全、請(qǐng)放心使用,那么 API 接口真的可信么?
其實(shí),API 接口是最不可信的資源!
首先,API 接口的提供方可以是任何開(kāi)發(fā)者,很難通過(guò)他們的一面之詞來(lái)確定接口的穩(wěn)定性和安全性。
即使這個(gè)接口性能很高、也很安全,但是你并不了解有多少人和你在同時(shí)使用這個(gè)接口,也許只有你,又也許是 100 萬(wàn)個(gè)其他的開(kāi)發(fā)者呢?在這個(gè)競(jìng)爭(zhēng)條件下,接口的 qps(query per second 每秒查詢(xún)數(shù))還能達(dá)到預(yù)期么?接口返回時(shí)長(zhǎng)真的不會(huì)超時(shí)么?
更有甚者,偷偷地把 API 接口改動(dòng)了,卻沒(méi)有給調(diào)用者發(fā)送通知,這樣接口的調(diào)用方全部都會(huì)調(diào)用失敗,嚴(yán)重影響項(xiàng)目的運(yùn)行!
因此,我們?cè)谡{(diào)用第三方 API 接口時(shí),一定要慎重、慎重、再慎重!
此外,如果我們是 API 接口的提供者,也要注意保護(hù)好自己的 API 接口,避免同時(shí)被太多的開(kāi)發(fā)者調(diào)用,導(dǎo)致接口掛掉。
10. Serverless 可信么?
如果說(shuō)服務(wù)器不可信,那我們干脆就不租服務(wù)器了,直接租用大公司提供的 Serverless 服務(wù)來(lái)作為項(xiàng)目的后臺(tái)不就行了?
Serverless 指無(wú)服務(wù)器架構(gòu),并不是真的不需要服務(wù)器,而是將項(xiàng)目接口的部署、運(yùn)維等需要對(duì)服務(wù)器的操作交給服務(wù)商去做,讓開(kāi)發(fā)者無(wú)需關(guān)心服務(wù)器,專(zhuān)心寫(xiě)代碼就好。
聽(tīng)起來(lái)非常爽,那 Serverless可信么?
使用 Serverless,雖然能夠大大提升開(kāi)發(fā)和運(yùn)維效率,但是其相對(duì)服務(wù)器等資源而言,更不可信!
首先,Serverless 本身就是部署在服務(wù)器上的,難免會(huì)受到服務(wù)器的影響。
其次,Serverless 服務(wù)不會(huì)長(zhǎng)期保持應(yīng)用的狀態(tài),而是隨著請(qǐng)求的到來(lái)而啟動(dòng),存在冷啟動(dòng)時(shí)期,雖然也有很多相關(guān)的優(yōu)化和解決方案,但仍無(wú)法精確地保證接口的性能,尤其是在高并發(fā)場(chǎng)景下,性能往往達(dá)不到預(yù)期。
最重要的是,當(dāng)你選擇使用 Serverless 服務(wù)時(shí),你就和某云服務(wù)提供商綁定了,后續(xù)想要遷移是非常困難的!試想一下,你項(xiàng)目的所有功能都交給別人來(lái)維護(hù),真的是好事么?一旦云服務(wù)提供商改造了架構(gòu)或接口,你的代碼也要隨之改動(dòng),而這種改動(dòng)卻不是由自己控制的!
當(dāng)然,Serverless 具有非常多的優(yōu)點(diǎn),也是云計(jì)算技術(shù)發(fā)展的必然趨勢(shì),只是希望大家在使用前,考慮到那些可能的風(fēng)險(xiǎn),并做好應(yīng)對(duì)措施。
總結(jié):正是因?yàn)槲覀兲^(guò)信任那些名氣大、看似安全的資源,所以其背后的危險(xiǎn)才更難以被察覺(jué),帶來(lái)的后果往往也更致命!
防御性編程
“居安思危,對(duì)隨時(shí)可能帶來(lái)的嚴(yán)重后果,要做到謹(jǐn)慎。”
在軟件開(kāi)發(fā)中,雖然項(xiàng)目表面上能夠正常運(yùn)行,但風(fēng)險(xiǎn)無(wú)處不在,因此我們要學(xué)習(xí)防御性編程思想。把自己當(dāng)成一個(gè)杠精,不要相信任何人,盡力去發(fā)現(xiàn)程序中的風(fēng)險(xiǎn),積極防御。
下面給大家分享 16 個(gè)防御性編程的方法,學(xué)習(xí)之后,能夠大大減少程序中的風(fēng)險(xiǎn)。
1. 編程習(xí)慣
要減少程序中的風(fēng)險(xiǎn),首先要養(yǎng)成良好的編程習(xí)慣。
首先,在寫(xiě)代碼時(shí),一定要保持良好的心態(tài),不要倉(cāng)促或者以完成任務(wù)的心態(tài)去寫(xiě)代碼。如果僅僅是為了完成需求,那么很有可能不會(huì)注意到代碼中的風(fēng)險(xiǎn),甚至是發(fā)現(xiàn)了風(fēng)險(xiǎn)也懶得去修補(bǔ),這樣確實(shí)能夠節(jié)約開(kāi)發(fā)的時(shí)間,但是后面出現(xiàn)問(wèn)題后,你還是要花費(fèi)更多的時(shí)間去排查、溝通和修復(fù) bug。拔苗助長(zhǎng),適得其反。
在寫(xiě)代碼時(shí),如果在一個(gè)地方多次使用相同且復(fù)雜的變量名或字符串,建議不要手動(dòng)去敲,而是用大家最喜歡的 “復(fù)制粘貼”,防止因?yàn)槭终`而導(dǎo)致的 bug。
此外,我們?cè)诖a中應(yīng)該加強(qiáng)對(duì)返回值的檢查,并且選擇安全的語(yǔ)法和數(shù)據(jù)結(jié)構(gòu),避免使用被廢棄的語(yǔ)法。不同的編程語(yǔ)言也有不同的最佳編程習(xí)慣,比如在 Java 語(yǔ)言中,應(yīng)該對(duì)所有可能為 NULL 的變量進(jìn)行檢查,防止 NPE(NULL Pointer Error 空指針異常),在開(kāi)發(fā)多線程程序時(shí),選用線程安全的 ConcurrentHashMap 而不是 HashMap 等等。還可以利用 Assert(斷言)來(lái)保證程序運(yùn)行中的變量值符合預(yù)期。
推薦使用一個(gè)自帶檢查功能的編輯器來(lái)書(shū)寫(xiě)代碼,在我們編寫(xiě)代碼時(shí)會(huì)自動(dòng)檢查出錯(cuò)誤,還能給出好的編碼風(fēng)格的建議,能夠大大減少開(kāi)發(fā)時(shí)的風(fēng)險(xiǎn)。此外,在代碼提交前,一定要多次檢查代碼,尤其是那些復(fù)制粘貼過(guò)來(lái)的文件,經(jīng)常會(huì)出現(xiàn)遺漏的修改。提交代碼后,也可以找有經(jīng)驗(yàn)的同事幫忙閱讀和檢查下代碼(代碼審查),進(jìn)一步保證沒(méi)有語(yǔ)法和邏輯錯(cuò)誤。
2. 異常處理
程序的運(yùn)行風(fēng)云變幻,同一段代碼在不同情況下也可能會(huì)產(chǎn)生不同的結(jié)果,甚至是異常。因此很多主流的編程語(yǔ)言中都有異常處理機(jī)制,比如在 Java 中,先用 try 捕獲異常、再用 catch 處理異常、最后用 finally 釋放資源和善后。
在編程時(shí),要合理利用異常處理機(jī)制,來(lái)防御代碼中可能出現(xiàn)的種種問(wèn)題。通常在異常處理中,我們會(huì)記錄錯(cuò)誤日志、執(zhí)行錯(cuò)誤上報(bào)和告警、重試等。
比如不信任數(shù)據(jù)庫(kù),那就在查詢(xún)和操作數(shù)據(jù)時(shí)添加異常處理,一旦數(shù)據(jù)庫(kù)抽風(fēng)導(dǎo)致操作失敗,就在日志中記錄失敗信息,并通過(guò)郵件、短信等告警方式通知到開(kāi)發(fā)者,就能第一時(shí)間發(fā)現(xiàn)問(wèn)題并排查。必要時(shí)還可以實(shí)現(xiàn)自動(dòng)重試,省去一部分人工操作。
3. 請(qǐng)求校驗(yàn)
所有的請(qǐng)求都是不可信的,哪怕是在公司內(nèi)網(wǎng),也有可能因?yàn)橐恍┦д`,導(dǎo)致發(fā)出了錯(cuò)誤的請(qǐng)求。
因此我們編寫(xiě)的每個(gè)接口,在實(shí)現(xiàn)具體的業(yè)務(wù)邏輯前,一定要先對(duì)請(qǐng)求參數(shù)加上校驗(yàn),下面列舉幾種常見(jiàn)的校驗(yàn)方式:
參數(shù)類(lèi)型校驗(yàn):比如請(qǐng)求參數(shù)應(yīng)該是 Integer 整型而不是 Long 長(zhǎng)整數(shù)類(lèi)型。
值合法性校驗(yàn):比如整數(shù)的范圍大于等于 0、字符串長(zhǎng)度大于 5,或者滿(mǎn)足某種特定格式,比如手機(jī)號(hào)、身份證等。
用戶(hù)權(quán)限校驗(yàn):很多接口需要登錄用戶(hù)或者管理員才能調(diào)用,因此必須通過(guò)請(qǐng)求參數(shù)(請(qǐng)求頭)來(lái)判斷當(dāng)前用戶(hù)的身份,被一個(gè)普通用戶(hù)下載了 VIP 付費(fèi)電影肯定是不合理的!
4. 流量控制
上面提到,所有的請(qǐng)求都是不可信的,不僅僅是請(qǐng)求的值,還有請(qǐng)求的量和頻率。對(duì)于所有接口,都要限制它的調(diào)用頻率,防止接口被大量瞬時(shí)的請(qǐng)求刷爆。對(duì)于付費(fèi)接口,還要防止用戶(hù)對(duì)接口的請(qǐng)求數(shù)超過(guò)原購(gòu)買(mǎi)數(shù)。
此外,還有一種容易被忽視的情況,假如你的接口 A 中又調(diào)用了其他人的接口 B,也許你的接口 A 自身的邏輯能夠承受每秒 1000 個(gè)請(qǐng)求,但是你確定接口 B 可以承受么?
因此,需要進(jìn)行流量控制,不僅僅是預(yù)防接口被刷爆,還可以保護(hù)內(nèi)部的服務(wù)和調(diào)用。
什么,你說(shuō)你的接口很牛逼,每秒能抗 100 萬(wàn)個(gè)請(qǐng)求,也沒(méi)有調(diào)用其他的服務(wù),那我就找 100 萬(wàn) + 1 個(gè)人同時(shí)請(qǐng)求你的接口,看你怕不怕!
常用的流量控制按照不同的粒度可分為:
用戶(hù)流控:限制每個(gè)用戶(hù)在一定時(shí)間內(nèi)對(duì)某個(gè)接口的調(diào)用數(shù)。
接口流控:限制一定時(shí)間內(nèi)某個(gè)接口的總調(diào)用數(shù)。
單機(jī)流控:限制一定時(shí)間內(nèi)單臺(tái)服務(wù)器上的項(xiàng)目所有接口的總調(diào)用數(shù)。
分布式流控:限制一定時(shí)間內(nèi)項(xiàng)目所有服務(wù)器的總請(qǐng)求數(shù)。
當(dāng)然,除了上面提到的幾種方式外,流控可以非常靈活,也有很多優(yōu)秀的限流工具。比如 Java 語(yǔ)言 Guava 庫(kù)的 RateLimiter 令牌桶單機(jī)限流、阿里的 Sentinel 分布式限流框架等。
5. 回滾
有時(shí),我們對(duì)項(xiàng)目的操作可能是錯(cuò)誤的,可能是人工操作,也可能是機(jī)器操作,從而導(dǎo)致了一些線上故障。這時(shí),可以選擇回滾。
回滾是指撤銷(xiāo)某個(gè)操作,將項(xiàng)目還原到之前的狀態(tài),這里介紹幾種常見(jiàn)的回滾操作。
數(shù)據(jù)回滾
有時(shí),我們想要批量插入數(shù)據(jù),但是數(shù)據(jù)插入到一半時(shí),程序突然出現(xiàn)異常,這個(gè)時(shí)候我們就需要把之前插入成功的數(shù)據(jù)進(jìn)行回滾,就好像什么都沒(méi)發(fā)生過(guò)一樣。否則可能存在數(shù)據(jù)不一致的風(fēng)險(xiǎn)。
最常見(jiàn)的方式就是使用事務(wù)來(lái)處理數(shù)據(jù)庫(kù)的批量操作,當(dāng)出現(xiàn)異常時(shí),執(zhí)行數(shù)據(jù)庫(kù)客戶(hù)端的回滾方法即可。
配置回滾
如果將項(xiàng)目的配置信息,比如數(shù)據(jù)庫(kù)鏈接地址,寫(xiě)死到代碼中,一旦配置錯(cuò)了或者地址發(fā)生變更,就要重新修改代碼,非常麻煩。
比較好的方式是將配置發(fā)布到配置中心進(jìn)行管理,讓項(xiàng)目去動(dòng)態(tài)讀取配置中心的配置。如果不小心發(fā)布了錯(cuò)誤的配置,可以直接在配置中心進(jìn)行回滾,將配置還原。
發(fā)布回滾
沒(méi)有人能保證自己的代碼正確無(wú)誤,很多時(shí)候,項(xiàng)目在測(cè)試環(huán)境驗(yàn)證時(shí)沒(méi)有發(fā)現(xiàn)任何問(wèn)題,但是一上線,就漏洞百出。這就說(shuō)明我們最新發(fā)布的代碼是存在問(wèn)題的。
這時(shí),最簡(jiǎn)單的做法就是進(jìn)行版本回滾,將之前能夠正常運(yùn)行的代碼重新打包發(fā)布。大公司一般都有自己的項(xiàng)目發(fā)布平臺(tái),能夠使用界面一鍵回滾,自動(dòng)發(fā)布以前版本的項(xiàng)目包。
6. 多級(jí)緩存
上面提到,緩存對(duì)項(xiàng)目是非常重要的,不僅是提升性能的利器,也是數(shù)據(jù)庫(kù)的保護(hù)傘。
但如果緩存掛掉怎么辦呢?
有兩種方案,第一種是為緩存搭建集群,從而保證緩存的高可用。
但是一切都不可信,集群也有可能掛掉!
那么可以用第二種方案,一級(jí)緩存掛掉,我們就再搞一個(gè)二級(jí)緩存頂上!
通常,在高并發(fā)項(xiàng)目中,我們會(huì)設(shè)計(jì)多級(jí)緩存,即分布式緩存 + 本地緩存。當(dāng)請(qǐng)求需要獲取數(shù)據(jù)時(shí),先從分布式緩存(比如 Redis) 中查詢(xún),如果分布式緩存集體宕機(jī),那就從本地緩存中獲取數(shù)據(jù)。這樣,即使緩存掛掉,也能夠幫助系統(tǒng)支撐一段時(shí)間。
這里可能和一些多級(jí)緩存的設(shè)計(jì)不同,有時(shí),我們會(huì)把本地緩存作為一級(jí)緩存,緩存一些熱點(diǎn)數(shù)據(jù),本地緩存找不到值時(shí),才去訪問(wèn)分布式緩存。這種設(shè)計(jì)主要解決的問(wèn)題是,減少對(duì)分布式緩存的請(qǐng)求量,并進(jìn)一步提升性能,和上面的設(shè)計(jì)目的不同。
7. 服務(wù)熔斷和降級(jí)
每年的雙十一,我們會(huì)準(zhǔn)時(shí)守著屏幕上的搶購(gòu)頁(yè)面,只為等待那一個(gè) “請(qǐng)稍后再試!”
我們的項(xiàng)目其實(shí)遠(yuǎn)比想象的要脆弱,很多服務(wù)經(jīng)常因?yàn)楦鞣N原因出現(xiàn)問(wèn)題。比如搞活動(dòng)時(shí),大量用戶(hù)同時(shí)訪問(wèn)會(huì)導(dǎo)致對(duì)項(xiàng)目服務(wù)的請(qǐng)求增多,如果項(xiàng)目頂不住壓力,就會(huì)掛掉。
為了防止這種風(fēng)險(xiǎn),我們可以采用服務(wù)降級(jí)策略,如果系統(tǒng)實(shí)在無(wú)法為所有用戶(hù)提供服務(wù),那就退而求其次,給用戶(hù)直接返回一個(gè) “友好的” 提示或界面,而不是強(qiáng)行讓項(xiàng)目頂著壓力過(guò)勞死。
配合服務(wù)熔斷技術(shù),可以根據(jù)系統(tǒng)的負(fù)載等指標(biāo)來(lái)動(dòng)態(tài)開(kāi)啟或關(guān)閉降級(jí)。比如機(jī)器的 CPU 被占用爆滿(mǎn)時(shí),就開(kāi)啟降級(jí),直接返回錯(cuò)誤;當(dāng)機(jī)器 CPU 恢復(fù)正常時(shí),再正常返回?cái)?shù)據(jù)、執(zhí)行操作。
Hystrix 就是比較有名的微服務(wù)熔斷降級(jí)框架。
8. 主動(dòng)檢測(cè)
上面提到,即使是大公司的同步服務(wù),也可能會(huì)出現(xiàn)同步不及時(shí)甚至是數(shù)據(jù)丟失的情況。因此,為了進(jìn)一步保證同步成功、數(shù)據(jù)的準(zhǔn)確,我們可以主動(dòng)檢測(cè)。
比如編寫(xiě)一個(gè)定時(shí)腳本或者任務(wù),每隔一段時(shí)間去檢查原地址和目標(biāo)地址的數(shù)據(jù)是否一致,或者通過(guò)一些邏輯來(lái)檢查數(shù)據(jù)是否正確。當(dāng)然也可以在每次數(shù)據(jù)同步結(jié)束后都立即去檢測(cè),更加保險(xiǎn)。
9. 數(shù)據(jù)補(bǔ)償
當(dāng)檢測(cè)出數(shù)據(jù)不一致后,我們就要進(jìn)行數(shù)據(jù)補(bǔ)償,比如將沒(méi)有同步的數(shù)據(jù)再次進(jìn)行同步、將不一致的數(shù)據(jù)進(jìn)行更新等。
除了用來(lái)解決主動(dòng)檢測(cè)出的數(shù)據(jù)不一致,數(shù)據(jù)補(bǔ)償也被廣泛用于業(yè)務(wù)設(shè)計(jì)和架構(gòu)設(shè)計(jì)中。
比如調(diào)用某個(gè)接口查詢(xún)數(shù)據(jù)失敗后,停頓一段時(shí)間,然后自動(dòng)重試,或者從其他地方獲取數(shù)據(jù)。又如消息隊(duì)列的生產(chǎn)者發(fā)送消息失敗時(shí),應(yīng)該自動(dòng)進(jìn)行補(bǔ)發(fā)和記錄,而不是直接把這條消息作廢。
數(shù)據(jù)補(bǔ)償?shù)乃枷氡举|(zhì)上是保證數(shù)據(jù)的最終一致性,數(shù)據(jù)出錯(cuò)不可怕,知錯(cuò)能改就是好孩子。這種思想也被廣泛應(yīng)用于分布式事務(wù)等場(chǎng)景中。
10. 數(shù)據(jù)備份
數(shù)據(jù)是企業(yè)的生命,因此我們必須盡可能地保證數(shù)據(jù)的安全和完整。
很多同學(xué)會(huì)把自己重要的文件存放在多個(gè)地方,比如自己的電腦、網(wǎng)盤(pán)上等等。同樣,在軟件開(kāi)發(fā)中,我們也應(yīng)該把重要的數(shù)據(jù)復(fù)制多份,作為副本存放在不同的地方。這樣,即使一臺(tái)服務(wù)器掛了,也可以從其他的服務(wù)器上獲取到數(shù)據(jù),減少了風(fēng)險(xiǎn)。
11. 心跳機(jī)制
接口可是個(gè)復(fù)雜多變的家伙,如果我們的項(xiàng)目依賴(lài)其他的接口來(lái)完成功能,那么最好保證該接口一直活著,否則可能會(huì)影響項(xiàng)目的運(yùn)行。
舉個(gè)例子,我們?cè)谑褂勉y行卡支付時(shí),肯定需要調(diào)用銀行提供的接口來(lái)獲取銀行卡的余額信息,如果這個(gè)接口掛了,獲取不到余額,用戶(hù)也就無(wú)法支付,也就損失了一筆收入!
因此,我們需要時(shí)刻和重要的接口保持聯(lián)系,防止他們不小心死了。可以采用心跳機(jī)制,定時(shí)調(diào)用該接口或者發(fā)送一個(gè)心跳包,來(lái)判斷該接口是否仍然存活。一旦調(diào)用超時(shí)或者失敗,可以立刻進(jìn)行排查和處理,從而大大減少了事故的影響時(shí)長(zhǎng)。
12. 冗余設(shè)計(jì)
在系統(tǒng)資源和容量評(píng)估時(shí),我們要做一些冗余設(shè)計(jì),比如數(shù)據(jù)庫(kù)目前的總數(shù)據(jù)量有 1G,那么如果要將數(shù)據(jù)庫(kù)的數(shù)據(jù)同步到其他存儲(chǔ)(比如 Elasticsearch)時(shí),至少要多預(yù)留一倍的存儲(chǔ)空間,即 2G,來(lái)應(yīng)對(duì)后面可能的數(shù)據(jù)增長(zhǎng)。業(yè)務(wù)的發(fā)展?jié)摿υ酱?#xff0c;冗余的倍數(shù)也可以越多,但也要注意不要過(guò)分冗余,畢竟資源也是很貴的啊!
其實(shí),冗余設(shè)計(jì)是一種重要的設(shè)計(jì)思想。當(dāng)我們?cè)O(shè)計(jì)業(yè)務(wù)或者系統(tǒng)架構(gòu)時(shí),不能只局限于當(dāng)前的條件,而是要考慮到以后的發(fā)展,選擇一種相對(duì)便于擴(kuò)展的模式。否則之后項(xiàng)目越做越大,每一次對(duì)項(xiàng)目的改動(dòng)都步履維艱。
13. 彈性擴(kuò)縮容
夢(mèng)想還是要有的,說(shuō)不定突然,我們?cè)戎挥?100 人使用的小項(xiàng)目突然就火了,有幾十萬(wàn)新用戶(hù)要來(lái)使用。
但是,由于我們的項(xiàng)目只部署在一臺(tái)服務(wù)器上,根本無(wú)法支撐那么多人,直接掛掉,導(dǎo)致這些用戶(hù)非常掃興,再也不想用我們的項(xiàng)目了。
這也是常見(jiàn)的風(fēng)險(xiǎn),我們可以使用彈性擴(kuò)縮容技術(shù),系統(tǒng)會(huì)根據(jù)當(dāng)前項(xiàng)目的使用和資源占用情況自動(dòng)擴(kuò)充或縮減資源。
比如當(dāng)系統(tǒng)壓力較大時(shí),多分配幾臺(tái)機(jī)器(容器),當(dāng)系統(tǒng)壓力較小時(shí),減少幾臺(tái)機(jī)器。這樣不僅能夠有效應(yīng)對(duì)突發(fā)的流量增長(zhǎng),還能夠在平時(shí)節(jié)約成本,并省去了人工分配調(diào)整機(jī)器的麻煩。
14. 異地多活
前面提到,服務(wù)器是不可信的,別說(shuō)一個(gè)服務(wù)器掛掉,由于一些天災(zāi)人禍,整個(gè)機(jī)房都有可能集體掛掉!
和備份不同,異地多活是指在不同城市建立獨(dú)立的數(shù)據(jù)中心,正常情況下,用戶(hù)無(wú)論訪問(wèn)哪一個(gè)地點(diǎn)的業(yè)務(wù)系統(tǒng),都能夠得到正確的服務(wù),即同時(shí)有多個(gè) “活” 的服務(wù)。
而某個(gè)地方業(yè)務(wù)異常的時(shí)候,用戶(hù)能夠訪問(wèn)其他地方正常的業(yè)務(wù)系統(tǒng),從而獲得正確的服務(wù)。
如此一來(lái),即使廣州的機(jī)房跨了,咱還有上海的,上海的跨了,咱還有北京的。
同時(shí)活著的服務(wù)越多,系統(tǒng)就越可靠,但同時(shí)成本也越高、越復(fù)雜,因此幾乎都是大公司才做異地多活。千萬(wàn)不要讓正常情況下的投入大于故障發(fā)生的損失!
15. 監(jiān)控告警
項(xiàng)目的運(yùn)行不可能一直正常,但是我們不可能 24 小時(shí)盯著電腦屏幕來(lái)監(jiān)視項(xiàng)目的運(yùn)行情況吧?又不能完全不管項(xiàng)目,出了 bug 等著用戶(hù)來(lái)投訴。
因此,最好的方式是給業(yè)務(wù)添加監(jiān)控告警,當(dāng)程序出現(xiàn)異常時(shí),信息會(huì)上報(bào)到監(jiān)控平臺(tái),并第一時(shí)間給開(kāi)發(fā)者發(fā)送通知。還可以通過(guò)監(jiān)控平臺(tái)實(shí)時(shí)查看項(xiàng)目的運(yùn)行情況,出了問(wèn)題也能更快地定位。
16. 線上診斷和熱修復(fù)
既然程序世界一切都不可信,危險(xiǎn)無(wú)處不在,那么干脆就做最壞的打算,假設(shè)線上程序一定會(huì)出 bug。
既然防不勝防,那就嚴(yán)陣以待,在 bug 出現(xiàn)時(shí)用最快的速度修復(fù)它,來(lái)減少影響。
通常,我們要改 bug,也需要經(jīng)歷改動(dòng)代碼、提交代碼、合并代碼、打包構(gòu)建、發(fā)布上線等一系列流程。等流程走完了,可能系統(tǒng)都透心涼了。
為提高效率,我們可以使用線上診斷和熱修復(fù)技術(shù)。在出現(xiàn) bug 時(shí),先用線上診斷工具輕松獲取項(xiàng)目的各運(yùn)行狀態(tài)和代碼執(zhí)行信息,提升排查效率。發(fā)現(xiàn)問(wèn)題后,使用熱修復(fù)技術(shù)直接修改運(yùn)行時(shí)的代碼,無(wú)需重新構(gòu)建和重啟項(xiàng)目!
在 Java 中,我們可以使用阿里開(kāi)源的診斷工具 Arthas,同時(shí)支持線上熱修復(fù)功能。也可以自己編寫(xiě)腳本來(lái)實(shí)現(xiàn),但是相對(duì)復(fù)雜一些。
看到這里,肯定有同學(xué)會(huì)吐槽,怎么寫(xiě)個(gè)程序要考慮那么多和功能無(wú)關(guān)的問(wèn)題。本來(lái)五分鐘就能寫(xiě)完的代碼,現(xiàn)在可能一個(gè)小時(shí)都寫(xiě)不完!
其實(shí),并不是所有的項(xiàng)目都要做到絕對(duì)的安全(當(dāng)然我們也做不到),而是我們應(yīng)該時(shí)刻保持居安思危的思想,把防御性編程當(dāng)做自己的習(xí)慣。
實(shí)際情況下,要根據(jù)項(xiàng)目的量級(jí)、受眾、架構(gòu)、緊急程度等因素來(lái)綜合評(píng)估將項(xiàng)目做到何種程度的安全,而不是過(guò)度設(shè)計(jì)、杞人憂(yōu)天。
讓我們把時(shí)間慢下來(lái),在開(kāi)發(fā)前先冷靜思考,預(yù)見(jiàn)并規(guī)避風(fēng)險(xiǎn),不要讓達(dá)摩克利斯之劍落下。
總結(jié)
以上是生活随笔為你收集整理的揭秘软件开发中的达摩克利斯之剑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: note3 android l,inot
- 下一篇: 【Windows 10 驱动程序 已解决