最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

揭秘軟件開發(fā)中的達(dá)摩克利斯之劍

2020-12-13 17:15 作者:程序員魚皮  | 我要投稿

為什么你的程序總是出現(xiàn) bug?

憑什么讓改 bug 占據(jù)了你大部分的時間?

看完本文,保證你能設(shè)計出更穩(wěn)定的程序,擺脫 bug 的纏繞,做項目更安心!

記得我在學(xué)校的時候,做的那些項目,不是為了應(yīng)付課程作業(yè),就是為了參加比賽時展示用,因此對項目的質(zhì)量要求非常低。

到底有多低呢?

大部分的項目,只要基本的功能可以使用,就算完成了,完全不考慮任何的異常情況。甚至只要能成功運(yùn)行一次,讓我截幾張圖放到 PPT 或者實(shí)驗(yàn)報告里,足夠向老師交差或者應(yīng)付比賽答辯就行。

那項目出現(xiàn) bug 怎么辦呢?

  • 如果測試的時候發(fā)現(xiàn)有些功能不可用,那很簡單,不管他,直接 PS 一張正常運(yùn)行的圖就行。

  • 如果比賽的時候發(fā)現(xiàn)有些功能不可用,那也很簡單,把鍋甩給 “現(xiàn)場網(wǎng)絡(luò)不好” 就行。

但是,這些 “小技巧” 在企業(yè)中是行不通的,企業(yè)級項目必須為企業(yè)帶來實(shí)際的價值,容不得半點(diǎn)馬虎和欺騙。

我第一次進(jìn)入企業(yè)實(shí)習(xí)時,還保留著自己在學(xué)校開發(fā)項目的狼性,只要能夠完成基本功能就行,保證以最快的速度完成開發(fā)。

有一天,當(dāng)我洋洋得意準(zhǔn)備早點(diǎn)下班時,測試同學(xué)走過來跟我說。

“喂,你的程序有 bug,這里用戶下單怎么金額是負(fù)的?”

寫個 bug

對于我一個初入職場的小白,這是人生中第一次有人說我的代碼有 bug,我有問題,我不對勁。

當(dāng)時,我腦海的第一個念頭竟然是怎么把這個 bug 糊弄過去,而不是怎么去更正!看來我已經(jīng)養(yǎng)成了非常不好的習(xí)慣。

那之后幾天,我又連續(xù)收到了測試提出的多個 bug,然后將他們一個個改正。如果將這樣一個漏洞百出的程序發(fā)布上線,帶來的損失是不可估量的,現(xiàn)在想想仍心有余悸。

二八原則 開發(fā) 1 天,改 bug 4 天

這件事之后,我意識到,在企業(yè)中開發(fā)項目,不能只追求開發(fā)時的效率,還要注重項目的穩(wěn)定性,否則帶來的額外返工時間遠(yuǎn)比開發(fā)時節(jié)省的時間要長,而且會影響同事對你的看法。如果將開發(fā)時產(chǎn)生的 bug 遺留到線上,后果更是不堪設(shè)想!

后來,在字節(jié)跳動和騰訊這兩家大公司工作后,我進(jìn)一步認(rèn)識到了項目穩(wěn)定性有多重要,并且積累了更多只有在大公司才能學(xué)到的提升項目穩(wěn)定性的經(jīng)驗(yàn)。

我總結(jié)了 10 個開發(fā)中通常不會考慮到的風(fēng)險點(diǎn),以及 16 個減少風(fēng)險、提升項目穩(wěn)定性的方法,分享給大家~

在分享這些之前,先講個故事。

達(dá)摩克利斯之劍

古希臘傳說中,達(dá)摩克利斯是公元前 4 世紀(jì)意大利敘拉古的僭主(古希臘統(tǒng)治者獨(dú)有的稱號)狄奧尼修斯二世的朝臣,他非常喜歡奉承狄奧尼修斯。

他奉承道:“作為一個擁有權(quán)力和威信的偉人,狄奧尼修斯實(shí)在很幸運(yùn)。”

于是狄奧尼修斯提議與他交換一天的身份,那他就可以嘗試到首領(lǐng)的命運(yùn)。

在晚上舉行的宴會里,達(dá)摩克利斯非常享受成為國王的感覺。當(dāng)晚餐快結(jié)束的時候,他抬頭才注意到王位上方僅用一根馬鬃懸掛著的利劍。他立即失去了對美食和美女的興趣,并請求僭主放過他,他再也不想得到這樣的幸運(yùn)。

達(dá)摩克利斯之劍

這個故事告訴我們什么呢?

  1. 在和平安寧之后,時刻存在著危險與不安。

  2. 當(dāng)一個人獲取多少榮譽(yù)和地位,他都要付出同樣多的代價。

  3. 地位越高,看似越安全,實(shí)則越危險。

  4. 居安思危,對隨時可能帶來的嚴(yán)重后果,要做到謹(jǐn)慎。

那么這和軟件開發(fā)又有什么關(guān)系呢?下面就讓我來揭秘軟件開發(fā)中的達(dá)摩克利斯之劍。

危機(jī)四伏

“在和平安寧之后,時刻存在著危險與不安?!?/p>

軟件開發(fā)正是如此,表面上機(jī)器是 “死” 的,只會按照人輸入的指令或編好的程序來執(zhí)行,一成不變,聽話的很。好像我們寫好代碼扔到機(jī)器上后,就可以高枕無憂。

但真的是這樣么?我們真的可以信任機(jī)器和程序么?

其實(shí),在程序世界中危機(jī)四伏,人為因素、環(huán)境因素等可能都會對我們的程序產(chǎn)生影響。因此,我們必須時刻堅守軟件開發(fā)的不信任原則,保持 overly pessimistic(過于悲觀),把和程序有關(guān)的一切請求、服務(wù)、接口、返回值、機(jī)器、框架、中間件等等都當(dāng)做不可信的,步步為營、處處設(shè)防。

程序世界里的不信任原則

那為什么寫個代碼要這么小心翼翼,什么都不信任呢?

大項目的苦衷

“當(dāng)一個人獲取多少榮譽(yù)和地位,他都要付出同樣多的代價?!?/p>

軟件開發(fā)中,項目價值越大,需要承受的壓力也越大,來聽聽大項目的苦衷吧。

我是一個身價過億的大項目,每天服務(wù)著上千萬的用戶,幫助他們獲得知識與快樂。

我的小伙伴們只看到我身上的光環(huán)和榮耀,但是他們看不到我背負(fù)的壓力和風(fēng)險,今天終于有機(jī)會和大家傾訴我的苦衷了。

記得很多年前,我還是個孩子,只有幾個小主人開發(fā)我,那段時間,我成長的很快。雖然只有幾十個人使用我,但我感到非常輕松和快樂,偶爾偷會兒懶,也不會被人發(fā)現(xiàn)。

后來,我的功能越來越多,越來越強(qiáng)大。每天有數(shù)之不盡的新面孔來和我打招呼,并享受我提供的服務(wù)。漸漸地,更多開發(fā)者在我身上留下了印記,我感覺自己正在變得復(fù)雜,也開始感受到了壓力。我再也找不到機(jī)會偷懶,因?yàn)槲乙坏┬菹?,就會讓我的主人們損失一比不小的財富。

如今,我已經(jīng)是一個成熟的大項目了,每天有上千萬的用戶依賴我,我終于擁有了更大的價值,卻也增加了很多煩惱,感受到了更大的危險。

首先,同時服務(wù)千萬用戶,每秒鐘都可能會有幾十萬、甚至幾百萬個請求需要我來處理,因此我必須每時每刻無休止地高負(fù)載工作,且不說休息,哪怕稍微慢了一點(diǎn),就會遭到用戶的投訴,主人們也會因此受到批評。

我的運(yùn)行,必須依靠很多兄弟們的支撐,因此我必須和兄弟們好好相處,哪怕一個兄弟倒了,我都會受到影響。

在我強(qiáng)大的實(shí)力背后,有一顆非常脆弱的心。經(jīng)歷了那么多次的強(qiáng)化和改造,我的功能逐漸變多的同時,也因此被植入了各種框架和插件,體積像滾雪球一般越來越大,不知道什么時候就會爆炸。以至于主人們每次改動我時都要萬分謹(jǐn)慎,我的成長也變得十分緩慢。

復(fù)雜

然而最讓我感到恐懼的,是那些壞家伙們!

他們和正常的用戶不同,有的不斷制造請求,試圖將我擊垮。有的繞到我的背后,試圖直接控制我。有的對我虎視眈眈,監(jiān)視并記錄我的一舉一動。還有的嘗試各種非法操作,想從我身上牟取暴利。

作為一個大項目真是太累了,我不知道我還能堅持多久。

真的可信么?

“地位越高,看似越安全,實(shí)則越危險?!?/p>

如今是一個軟件開源和共享的時代,我們在開發(fā)項目時,或多或少會使用到網(wǎng)上現(xiàn)有的資源,比如依賴包、工具、組件、框架、接口、現(xiàn)成的云服務(wù)等等,這些資源能夠大大提升我們的開發(fā)效率。

就拿云服務(wù)來說,幾乎已經(jīng)成了我們開發(fā)必備的資源,以前我們想要做一個網(wǎng)站,可能需要自己買一臺物理服務(wù)器,然后連通網(wǎng)絡(luò),再把項目部署上去。而如今,直接登錄大公司的云官網(wǎng)(像騰訊云、阿里云),然后租一臺云服務(wù)器就行了,非常省事。

云服務(wù)

再說說現(xiàn)在主流的開發(fā)框架,以前做一個簡單的網(wǎng)站界面可能只會使用 HTML、CSSJavaScript 這三種最基礎(chǔ)的技術(shù),而如今,網(wǎng)站的樣式和交互越來越復(fù)雜,我們不得不使用一些知名的框架來提升開發(fā)效率,比如 VueReact。

聽起來好像沒有任何問題,你也根本不會去懷疑什么,因?yàn)?strong>我們天生帶著對大公司,或者說是對名氣的信任。

但是,你知道么,當(dāng)你決定使用其他人的資源時,你就已經(jīng)把項目系統(tǒng)的部分掌控權(quán)、甚至可能是半條命,都交出去了。

那么不妨思考一下,你使用的這些資源,真的可信么?

下面 10 個問題,可能改變你對開發(fā)的認(rèn)知。

1. 開發(fā)工具可信么?

我們通常是在大而全的開發(fā)工具中編寫代碼,比如 JetBrains IDEA 或者 Vscode。很多剛開始寫代碼的同學(xué)、甚至是一些經(jīng)驗(yàn)豐富的老手,都對開發(fā)工具保持絕對的信任。

比如你在鍵盤上敲擊 a,那編輯器界面上顯示的一定是 a

但是,由于內(nèi)存不足等種種原因,開發(fā)工具其實(shí)也會抽風(fēng)。

比如你想要調(diào)用某個函數(shù),通常敲擊函數(shù)名前幾個字母后,開發(fā)工具就會自動給你提示完整的函數(shù)名,但如果開發(fā)工具沒有給你提示,你首先懷疑的是這個函數(shù)不存在,而不是編輯器沒有按預(yù)期給出提示。遇到這種情況,可以稍等編輯器一下,或者進(jìn)一步確認(rèn)函數(shù)是否真的不存在,而不是立刻創(chuàng)建一個新的函數(shù)。

又或是項目無法運(yùn)行,怎么排查都覺得沒問題,這時不妨重新啟動下開發(fā)工具,或者清理一下緩存,說不定項目就能正常運(yùn)行了!

還有很多非常有意思的情況,比如編輯器一片大紅,各種提示錯誤,但是項目依然能成功運(yùn)行。

為什么不能運(yùn)行?為什么能運(yùn)行?

因此,不要絕對相信開發(fā)工具。

2. 開源項目可信么?

現(xiàn)在是一個軟件開源的時代,在 GitHub 等開源項目平臺上能夠找到大量優(yōu)秀的開源項目,好的開源項目甚至可以得到 10 萬多個關(guān)注,那這些知名的開源項目可信么?

不完全可信!從每個開源項目的 Issues 就能看出這點(diǎn),而且通常越大的項目,被發(fā)現(xiàn)的問題越多,比如 Vue 項目,累積提出并關(guān)閉了 8000 多個問題。

Vue 項目的問題

我記得自己有一次使用知名的開源服務(wù)器 Tomcat,就遇到了 bug,每次接受到特定的請求都會報錯。剛開始我根本沒有懷疑是 Tomcat 的問題,而是絞盡腦汁地想自己的代碼哪里寫錯了。后來經(jīng)過反復(fù)的排查和搜索,終于確認(rèn)了就是 Tomcat 本身的 bug!

雖然開源項目并不完全可信,但是相對于私有項目而言,所有對項目感興趣的同學(xué)可以共同發(fā)現(xiàn)項目中的問題,并加以解決,在一定程度上還是能夠提高項目的可靠性的。

3. 依賴庫可信么?

我們在開發(fā)項目時,通常會用到大量的依賴庫。直接在官方依賴源(比如 Mavennpm)搜索依賴庫,然后使用包管理器,用一行命令或者編寫配置文件就能夠讓其自動安裝依賴,非常方便。

但是,這些發(fā)布到官方源的依賴庫,就可信么?

且不說基本每個開發(fā)者都有機(jī)會發(fā)布依賴庫到官方,就算是互聯(lián)網(wǎng)大公司的依賴庫,也未必可信。

給我印象最深刻的就是阿里巴巴的 JSON 序列化類庫 fastjson,幾乎無人不知、無人不曉,因?yàn)槠錁O快的解析速度廣受好評。但是,這個庫被多次曝光存在高危漏洞,可以讓攻擊者遠(yuǎn)程執(zhí)行命令!一般的開發(fā)者根本不會發(fā)現(xiàn)這點(diǎn),從而給項目帶來了極大的危害。

因此,在選用依賴庫的時候,要做好充分的調(diào)研,盡量確認(rèn)依賴庫的安全,并且保證不要和已有的依賴沖突。

4. 編程語言可信么?

Java 是一種強(qiáng)類型語言,具有健壯性。這句話我相信所有學(xué)過 Java 的同學(xué)都再熟悉不過了。但是,強(qiáng)類型編程語言就一定可信么?

這里可能有同學(xué)就要表示懷疑了,如果我們一直使用的最基礎(chǔ)最底層的編程語言都存在 bug,那我們怎么去相信建立在這些編程語言上的框架呢?

然而真相是,所有的編程語言都有 bug!而且基本每次編程語言發(fā)布新版本時都會對一些歷史 bug 進(jìn)行修正。就 Java 而言,甚至還有一個專門記錄 bug 的數(shù)據(jù)庫!

Java Bug 數(shù)據(jù)庫

但是,對于大多數(shù)開發(fā)者來說,我相信即使在程序中偶然觸發(fā)了編程語言本身的 bug,也沒有足夠的自信去質(zhì)疑,而是直接修改代碼來繞過。

確實(shí),質(zhì)疑編程語言需要一定的基礎(chǔ)和知識儲備,但是一旦發(fā)現(xiàn)了程序中莫名其妙的問題,建議大家不要直接忽略,可以花一些時間去探索研究,說不定你就成功地發(fā)現(xiàn)了一個重大的 bug,也能夠加深對這門編程語言的理解。

5. 服務(wù)器可信么?

服務(wù)器是項目賴以生存的宿主,服務(wù)器的性能和穩(wěn)定性將直接影響到項目進(jìn)程。

無論是個人開發(fā)者還是企業(yè),通常都會直接租用大公司提供的云服務(wù)器來部署項目,省去了自己搭建和維護(hù)的麻煩。

但是大公司的云服務(wù)器就可信么?

不完全可信!即使現(xiàn)在的云服務(wù)器提供商都承諾自己的服務(wù) SLA(服務(wù)級別協(xié)議)可以達(dá)到 5 個 9(99.999% 一年約宕機(jī) 5 分鐘),甚至 6 個 9(99.9999% 一年約宕機(jī) 30 秒),但是仍然存在一定的風(fēng)險。

有一個非常有名的案例,在 2013 年,中國最大的社交通訊軟件出現(xiàn)大規(guī)模的故障,多達(dá)幾億用戶受到影響。原因竟然是,市政道路建設(shè)的一個不注意,把網(wǎng)絡(luò)光纜挖斷了,就導(dǎo)致該軟件所在服務(wù)器的無法訪問。

除了可用性的不可信之外,可能還有一些安全隱私方面的問題。當(dāng)然云服務(wù)商通常是不會獲取用戶的數(shù)據(jù)的,但也沒有辦法絕對相信他們。畢竟數(shù)據(jù)的隱私對企業(yè)至關(guān)重要,這也是為什么大的公司都會搭建屬于自己的服務(wù)器機(jī)房和網(wǎng)絡(luò)。

機(jī)房

6. 數(shù)據(jù)庫可信么?

企業(yè)中的大多數(shù)業(yè)務(wù)數(shù)據(jù)都是存放在數(shù)據(jù)庫中的,通過項目后端程序來操作和查詢數(shù)據(jù)庫中的數(shù)據(jù)。

和服務(wù)器一樣,我們可以使用軟件自己搭建數(shù)據(jù)庫,比如 MySQL,也可以直接租用大公司的云數(shù)據(jù)庫,那么數(shù)據(jù)庫可信么?

其實(shí)在企業(yè)后端項目中,數(shù)據(jù)庫通常是性能瓶頸,相對比較脆弱,當(dāng)訪問并發(fā)量大一點(diǎn)時,數(shù)據(jù)庫的查詢性能就會下降,嚴(yán)重時可能整個宕機(jī)!即使是大公司提供的云數(shù)據(jù)庫服務(wù),遇到慢查詢(需要較長時間的查詢)時,可能也無從應(yīng)對。

數(shù)據(jù)庫中的數(shù)據(jù)其實(shí)也未必可信,有時管理員的一個誤操作,不小心刪除數(shù)據(jù)或添加了一條錯誤數(shù)據(jù),可能就會影響用戶,造成損失。更有甚者,竟然刪庫跑路,不講碼德!

刪庫跑路

因此,不要過于信任數(shù)據(jù)庫,應(yīng)當(dāng)使用緩存之類的技術(shù)幫助數(shù)據(jù)庫分擔(dān)壓力,并定期備份。否則一旦數(shù)據(jù)庫宕機(jī)或數(shù)據(jù)丟失,帶來的損失是不可估量的!

7. 緩存服務(wù)可信么?

緩存是開發(fā)高性能程序必備的技術(shù),通過將數(shù)據(jù)庫等查詢較慢的數(shù)據(jù)存放在內(nèi)存中,直接從內(nèi)存中讀取數(shù)據(jù),以提升查詢性能。有了緩存之后,項目不僅能夠支持更多人同時查詢數(shù)據(jù),還能夠保護(hù)數(shù)據(jù)庫。

目前比較主流的緩存技術(shù)有 Redis、Memcached 等,可以自己在服務(wù)器搭建,也可以直接租用大公司提供的云緩存服務(wù)。

存儲鍵值對的緩存

那么緩存服務(wù)是否可信呢?

項目的并發(fā)量不是特別大的話,一般的緩存技術(shù)就足以支持了,但是如果項目的量級很大,可能緩存也無法承受住壓力,嚴(yán)重時就會宕機(jī)。而一旦緩存掛掉,大量的查詢命令會直接請求數(shù)據(jù)庫,于是數(shù)據(jù)庫也會在瞬間掛掉,嚴(yán)重時還會導(dǎo)致整個項目癱瘓!

因此,在使用緩存時,需要對并發(fā)量進(jìn)行評估,通過搭建集群和數(shù)據(jù)同步保證高可用性。此外,還要預(yù)防緩存雪崩、緩存穿透、緩存擊穿等問題,簡單解釋一下。

緩存雪崩:指大量緩存在同一時間過期,請求都訪問不到緩存,全部打到數(shù)據(jù)庫上,導(dǎo)致數(shù)據(jù)庫掛掉。

緩存穿透:持續(xù)訪問緩存中不存在的 key,導(dǎo)致請求繞過緩存,直接打到數(shù)據(jù)庫上,導(dǎo)致數(shù)據(jù)庫掛掉。

緩存擊穿:一個被大量請求高頻訪問的熱點(diǎn) key 突然過期,導(dǎo)致請求瞬間全部打到數(shù)據(jù)庫上,導(dǎo)致數(shù)據(jù)庫掛掉。

如果不預(yù)防這三個問題,即使是租用大公司的緩存服務(wù),也一樣吹彈可破。

8. 對象存儲可信么?

項目中,經(jīng)常會有用戶上傳圖片或文件的功能,這類數(shù)據(jù)通常較大,用數(shù)據(jù)庫存儲不太方便。雖然我們可以將文件直接存到服務(wù)器上,但更好的做法是使用專門的對象存儲服務(wù)。

可以簡單地把對象存儲當(dāng)做一個大的文件夾,我們可以通過它直接上傳和下載文件。大的云服務(wù)商也都提供了專業(yè)的對象存儲服務(wù),而無需自己搭建,那么對象存儲可信么?

一般情況下,上傳到對象存儲的文件是不會缺失或丟失的,而且還可以將已上傳的數(shù)據(jù)進(jìn)行跨園區(qū)同步,起到備份的作用。

跨園區(qū)同步

但是,記得有一次,上傳到對象存儲上的文件和源文件竟然不一致,大小足足少了 1M。起初我以為是文件上傳到對象存儲時,會自動被壓縮,但是將對象存儲中的文件下載到本地后,發(fā)現(xiàn)的確和源文件不一致!雖然出現(xiàn)這種情況的概率極其小,但從那一刻起,我再也不相信對象存儲了。

再用自己的真實(shí)經(jīng)歷來聊聊對象存儲的跨園區(qū)同步。因?yàn)閭€人負(fù)責(zé)的業(yè)務(wù)比較重要,萬一單個機(jī)房整體掛掉,可能分分鐘是幾十萬元的損失!因此我為對象存儲配置了自動跨園區(qū)同步,將文件先上傳至廣州機(jī)房,然后數(shù)據(jù)會自動同步到上海機(jī)房,且運(yùn)維同學(xué)承諾自動同步的延遲不超過 15 分鐘。

我相信大部分開發(fā)者配置數(shù)據(jù)同步后也就不管了,相信它一定會自動同步的。結(jié)果后面我編寫程序去做同步監(jiān)控、對比數(shù)據(jù)時,發(fā)現(xiàn)經(jīng)常出現(xiàn)數(shù)據(jù)未同步的情況,比例高達(dá) 10%!

因此,不能完全相信對象存儲,雖然大部分情況下大公司的對象存儲服務(wù)很可靠,但不能確保萬無一失。尤其是同步備份的場景下,是否真的同步成功了,又有多少同學(xué)關(guān)心過呢?不妨寫個程序去驗(yàn)證和保障。

9. API 接口可信么?

在開發(fā)中,我們經(jīng)常會調(diào)用其他系統(tǒng)提供的 API 接口來輕松實(shí)現(xiàn)某種功能。比如查詢某地的天氣,可以直接調(diào)用其他人提供的天氣查詢接口,而無需自己編寫。我們也可以提供 API 接口給其他人使用,尤其是在微服務(wù)架構(gòu)中,各服務(wù)之間都是以接口調(diào)用的形式實(shí)現(xiàn)交互協(xié)作的。

幾乎所有的 API 接口提供者都會說自己的接口有多安全、請放心使用,那么 API 接口真的可信么?

其實(shí),API 接口是最不可信的資源!

首先,API 接口的提供方可以是任何開發(fā)者,很難通過他們的一面之詞來確定接口的穩(wěn)定性和安全性。

即使這個接口性能很高、也很安全,但是你并不了解有多少人和你在同時使用這個接口,也許只有你,又也許是 100 萬個其他的開發(fā)者呢?在這個競爭條件下,接口的 qps(query per second 每秒查詢數(shù))還能達(dá)到預(yù)期么?接口返回時長真的不會超時么?

更有甚者,偷偷地把 API 接口改動了,卻沒有給調(diào)用者發(fā)送通知,這樣接口的調(diào)用方全部都會調(diào)用失敗,嚴(yán)重影響項目的運(yùn)行!

因此,我們在調(diào)用第三方 API 接口時,一定要慎重、慎重、再慎重!

此外,如果我們是 API 接口的提供者,也要注意保護(hù)好自己的 API 接口,避免同時被太多的開發(fā)者調(diào)用,導(dǎo)致接口掛掉。

API 存在復(fù)雜的調(diào)用關(guān)系

10. Serverless 可信么?

如果說服務(wù)器不可信,那我們干脆就不租服務(wù)器了,直接租用大公司提供的 Serverless 服務(wù)來作為項目的后臺不就行了?

Serverless 指無服務(wù)器架構(gòu),并不是真的不需要服務(wù)器,而是將項目接口的部署、運(yùn)維等需要對服務(wù)器的操作交給服務(wù)商去做,讓開發(fā)者無需關(guān)心服務(wù)器,專心寫代碼就好。

docker 容器

聽起來非常爽,那 Serverless可信么?

使用 Serverless,雖然能夠大大提升開發(fā)和運(yùn)維效率,但是其相對服務(wù)器等資源而言,更不可信!

首先,Serverless 本身就是部署在服務(wù)器上的,難免會受到服務(wù)器的影響。

其次,Serverless 服務(wù)不會長期保持應(yīng)用的狀態(tài),而是隨著請求的到來而啟動,存在冷啟動時期,雖然也有很多相關(guān)的優(yōu)化和解決方案,但仍無法精確地保證接口的性能,尤其是在高并發(fā)場景下,性能往往達(dá)不到預(yù)期。

最重要的是,當(dāng)你選擇使用 Serverless 服務(wù)時,你就和某云服務(wù)提供商綁定了,后續(xù)想要遷移是非常困難的!試想一下,你項目的所有功能都交給別人來維護(hù),真的是好事么?一旦云服務(wù)提供商改造了架構(gòu)或接口,你的代碼也要隨之改動,而這種改動卻不是由自己控制的!

當(dāng)然,Serverless 具有非常多的優(yōu)點(diǎn),也是云計算技術(shù)發(fā)展的必然趨勢,只是希望大家在使用前,考慮到那些可能的風(fēng)險,并做好應(yīng)對措施。

云計算時代

總結(jié):正是因?yàn)槲覀兲^信任那些名氣大、看似安全的資源,所以其背后的危險才更難以被察覺,帶來的后果往往也更致命!

防御性編程

“居安思危,對隨時可能帶來的嚴(yán)重后果,要做到謹(jǐn)慎。”

在軟件開發(fā)中,雖然項目表面上能夠正常運(yùn)行,但風(fēng)險無處不在,因此我們要學(xué)習(xí)防御性編程思想。把自己當(dāng)成一個杠精,不要相信任何人,盡力去發(fā)現(xiàn)程序中的風(fēng)險,積極防御。

下面給大家分享 16 個防御性編程的方法,學(xué)習(xí)之后,能夠大大減少程序中的風(fēng)險。

祈禱性編程

1. 編程習(xí)慣

要減少程序中的風(fēng)險,首先要養(yǎng)成良好的編程習(xí)慣。

首先,在寫代碼時,一定要保持良好的心態(tài),不要倉促或者以完成任務(wù)的心態(tài)去寫代碼。如果僅僅是為了完成需求,那么很有可能不會注意到代碼中的風(fēng)險,甚至是發(fā)現(xiàn)了風(fēng)險也懶得去修補(bǔ),這樣確實(shí)能夠節(jié)約開發(fā)的時間,但是后面出現(xiàn)問題后,你還是要花費(fèi)更多的時間去排查、溝通和修復(fù) bug。拔苗助長,適得其反。

在寫代碼時,如果在一個地方多次使用相同且復(fù)雜的變量名或字符串,建議不要手動去敲,而是用大家最喜歡的 “復(fù)制粘貼”,防止因?yàn)槭终`而導(dǎo)致的 bug。

復(fù)制粘貼一把梭

此外,我們在代碼中應(yīng)該加強(qiáng)對返回值的檢查,并且選擇安全的語法和數(shù)據(jù)結(jié)構(gòu),避免使用被廢棄的語法。不同的編程語言也有不同的最佳編程習(xí)慣,比如在 Java 語言中,應(yīng)該對所有可能為 NULL 的變量進(jìn)行檢查,防止 NPE(NULL Pointer Error 空指針異常),在開發(fā)多線程程序時,選用線程安全的 ConcurrentHashMap 而不是 HashMap 等等。還可以利用 Assert(斷言)來保證程序運(yùn)行中的變量值符合預(yù)期。

推薦使用一個自帶檢查功能的編輯器來書寫代碼,在我們編寫代碼時會自動檢查出錯誤,還能給出好的編碼風(fēng)格的建議,能夠大大減少開發(fā)時的風(fēng)險。此外,在代碼提交前,一定要多次檢查代碼,尤其是那些復(fù)制粘貼過來的文件,經(jīng)常會出現(xiàn)遺漏的修改。提交代碼后,也可以找有經(jīng)驗(yàn)的同事幫忙閱讀和檢查下代碼(代碼審查),進(jìn)一步保證沒有語法和邏輯錯誤。

編輯器語法檢查和提示

2. 異常處理

程序的運(yùn)行風(fēng)云變幻,同一段代碼在不同情況下也可能會產(chǎn)生不同的結(jié)果,甚至是異常。因此很多主流的編程語言中都有異常處理機(jī)制,比如在 Java 中,先用 try 捕獲異常、再用 catch 處理異常、最后用 finally 釋放資源和善后。

在編程時,要合理利用異常處理機(jī)制,來防御代碼中可能出現(xiàn)的種種問題。通常在異常處理中,我們會記錄錯誤日志、執(zhí)行錯誤上報和告警、重試等。

比如不信任數(shù)據(jù)庫,那就在查詢和操作數(shù)據(jù)時添加異常處理,一旦數(shù)據(jù)庫抽風(fēng)導(dǎo)致操作失敗,就在日志中記錄失敗信息,并通過郵件、短信等告警方式通知到開發(fā)者,就能第一時間發(fā)現(xiàn)問題并排查。必要時還可以實(shí)現(xiàn)自動重試,省去一部分人工操作。

異常啦

3. 請求校驗(yàn)

所有的請求都是不可信的,哪怕是在公司內(nèi)網(wǎng),也有可能因?yàn)橐恍┦д`,導(dǎo)致發(fā)出了錯誤的請求。

因此我們編寫的每個接口,在實(shí)現(xiàn)具體的業(yè)務(wù)邏輯前,一定要先對請求參數(shù)加上校驗(yàn),下面列舉幾種常見的校驗(yàn)方式:

  1. 參數(shù)類型校驗(yàn):比如請求參數(shù)應(yīng)該是 Integer 整型而不是 Long 長整數(shù)類型。

  2. 值合法性校驗(yàn):比如整數(shù)的范圍大于等于 0、字符串長度大于 5,或者滿足某種特定格式,比如手機(jī)號、身份證等。

  3. 用戶權(quán)限校驗(yàn):很多接口需要登錄用戶或者管理員才能調(diào)用,因此必須通過請求參數(shù)(請求頭)來判斷當(dāng)前用戶的身份,被一個普通用戶下載了 VIP 付費(fèi)電影肯定是不合理的!

4. 流量控制

上面提到,所有的請求都是不可信的,不僅僅是請求的值,還有請求的量和頻率。對于所有接口,都要限制它的調(diào)用頻率,防止接口被大量瞬時的請求刷爆。對于付費(fèi)接口,還要防止用戶對接口的請求數(shù)超過原購買數(shù)。

此外,還有一種容易被忽視的情況,假如你的接口 A 中又調(diào)用了其他人的接口 B,也許你的接口 A 自身的邏輯能夠承受每秒 1000 個請求,但是你確定接口 B 可以承受么?

因此,需要進(jìn)行流量控制,不僅僅是預(yù)防接口被刷爆,還可以保護(hù)內(nèi)部的服務(wù)和調(diào)用。

什么,你說你的接口很牛逼,每秒能抗 100 萬個請求,也沒有調(diào)用其他的服務(wù),那我就找 100 萬 + 1 個人同時請求你的接口,看你怕不怕!

DDOS 分布式拒絕服務(wù)攻擊

常用的流量控制按照不同的粒度可分為:

  1. 用戶流控:限制每個用戶在一定時間內(nèi)對某個接口的調(diào)用數(shù)。

  2. 接口流控:限制一定時間內(nèi)某個接口的總調(diào)用數(shù)。

  3. 單機(jī)流控:限制一定時間內(nèi)單臺服務(wù)器上的項目所有接口的總調(diào)用數(shù)。

  4. 分布式流控:限制一定時間內(nèi)項目所有服務(wù)器的總請求數(shù)。

當(dāng)然,除了上面提到的幾種方式外,流控可以非常靈活,也有很多優(yōu)秀的限流工具。比如 Java 語言 Guava 庫的 RateLimiter 令牌桶單機(jī)限流、阿里的 Sentinel 分布式限流框架等。

Sentinel 流控面板

5. 回滾

有時,我們對項目的操作可能是錯誤的,可能是人工操作,也可能是機(jī)器操作,從而導(dǎo)致了一些線上故障。這時,可以選擇回滾。

回滾是指撤銷某個操作,將項目還原到之前的狀態(tài),這里介紹幾種常見的回滾操作。

數(shù)據(jù)回滾

有時,我們想要批量插入數(shù)據(jù),但是數(shù)據(jù)插入到一半時,程序突然出現(xiàn)異常,這個時候我們就需要把之前插入成功的數(shù)據(jù)進(jìn)行回滾,就好像什么都沒發(fā)生過一樣。否則可能存在數(shù)據(jù)不一致的風(fēng)險。

最常見的方式就是使用事務(wù)來處理數(shù)據(jù)庫的批量操作,當(dāng)出現(xiàn)異常時,執(zhí)行數(shù)據(jù)庫客戶端的回滾方法即可。

配置回滾

如果將項目的配置信息,比如數(shù)據(jù)庫鏈接地址,寫死到代碼中,一旦配置錯了或者地址發(fā)生變更,就要重新修改代碼,非常麻煩。

比較好的方式是將配置發(fā)布到配置中心進(jìn)行管理,讓項目去動態(tài)讀取配置中心的配置。如果不小心發(fā)布了錯誤的配置,可以直接在配置中心進(jìn)行回滾,將配置還原。

發(fā)布回滾

沒有人能保證自己的代碼正確無誤,很多時候,項目在測試環(huán)境驗(yàn)證時沒有發(fā)現(xiàn)任何問題,但是一上線,就漏洞百出。這就說明我們最新發(fā)布的代碼是存在問題的。

這時,最簡單的做法就是進(jìn)行版本回滾,將之前能夠正常運(yùn)行的代碼重新打包發(fā)布。大公司一般都有自己的項目發(fā)布平臺,能夠使用界面一鍵回滾,自動發(fā)布以前版本的項目包。

6. 多級緩存

上面提到,緩存對項目是非常重要的,不僅是提升性能的利器,也是數(shù)據(jù)庫的保護(hù)傘。

但如果緩存掛掉怎么辦呢?

有兩種方案,第一種是為緩存搭建集群,從而保證緩存的高可用。

Redis 集群

但是一切都不可信,集群也有可能掛掉!

那么可以用第二種方案,一級緩存掛掉,我們就再搞一個二級緩存頂上!

通常,在高并發(fā)項目中,我們會設(shè)計多級緩存,即分布式緩存 + 本地緩存。當(dāng)請求需要獲取數(shù)據(jù)時,先從分布式緩存(比如 Redis) 中查詢,如果分布式緩存集體宕機(jī),那就從本地緩存中獲取數(shù)據(jù)。這樣,即使緩存掛掉,也能夠幫助系統(tǒng)支撐一段時間。

這里可能和一些多級緩存的設(shè)計不同,有時,我們會把本地緩存作為一級緩存,緩存一些熱點(diǎn)數(shù)據(jù),本地緩存找不到值時,才去訪問分布式緩存。這種設(shè)計主要解決的問題是,減少對分布式緩存的請求量,并進(jìn)一步提升性能,和上面的設(shè)計目的不同。

多級緩存設(shè)計

7. 服務(wù)熔斷和降級

每年的雙十一,我們會準(zhǔn)時守著屏幕上的搶購頁面,只為等待那一個 “請稍后再試!”

我們的項目其實(shí)遠(yuǎn)比想象的要脆弱,很多服務(wù)經(jīng)常因?yàn)楦鞣N原因出現(xiàn)問題。比如搞活動時,大量用戶同時訪問會導(dǎo)致對項目服務(wù)的請求增多,如果項目頂不住壓力,就會掛掉。

為了防止這種風(fēng)險,我們可以采用服務(wù)降級策略,如果系統(tǒng)實(shí)在無法為所有用戶提供服務(wù),那就退而求其次,給用戶直接返回一個 “友好的” 提示或界面,而不是強(qiáng)行讓項目頂著壓力過勞死。

配合服務(wù)熔斷技術(shù),可以根據(jù)系統(tǒng)的負(fù)載等指標(biāo)來動態(tài)開啟或關(guān)閉降級。比如機(jī)器的 CPU 被占用爆滿時,就開啟降級,直接返回錯誤;當(dāng)機(jī)器 CPU 恢復(fù)正常時,再正常返回數(shù)據(jù)、執(zhí)行操作。

Hystrix 就是比較有名的微服務(wù)熔斷降級框架。

Hystrix

8. 主動檢測

上面提到,即使是大公司的同步服務(wù),也可能會出現(xiàn)同步不及時甚至是數(shù)據(jù)丟失的情況。因此,為了進(jìn)一步保證同步成功、數(shù)據(jù)的準(zhǔn)確,我們可以主動檢測。

比如編寫一個定時腳本或者任務(wù),每隔一段時間去檢查原地址和目標(biāo)地址的數(shù)據(jù)是否一致,或者通過一些邏輯來檢查數(shù)據(jù)是否正確。當(dāng)然也可以在每次數(shù)據(jù)同步結(jié)束后都立即去檢測,更加保險。

主動檢測

9. 數(shù)據(jù)補(bǔ)償

當(dāng)檢測出數(shù)據(jù)不一致后,我們就要進(jìn)行數(shù)據(jù)補(bǔ)償,比如將沒有同步的數(shù)據(jù)再次進(jìn)行同步、將不一致的數(shù)據(jù)進(jìn)行更新等。

除了用來解決主動檢測出的數(shù)據(jù)不一致,數(shù)據(jù)補(bǔ)償也被廣泛用于業(yè)務(wù)設(shè)計和架構(gòu)設(shè)計中。

比如調(diào)用某個接口查詢數(shù)據(jù)失敗后,停頓一段時間,然后自動重試,或者從其他地方獲取數(shù)據(jù)。又如消息隊列的生產(chǎn)者發(fā)送消息失敗時,應(yīng)該自動進(jìn)行補(bǔ)發(fā)和記錄,而不是直接把這條消息作廢。

數(shù)據(jù)補(bǔ)償?shù)乃枷氡举|(zhì)上是保證數(shù)據(jù)的最終一致性,數(shù)據(jù)出錯不可怕,知錯能改就是好孩子。這種思想也被廣泛應(yīng)用于分布式事務(wù)等場景中。

10. 數(shù)據(jù)備份

數(shù)據(jù)是企業(yè)的生命,因此我們必須盡可能地保證數(shù)據(jù)的安全和完整。

很多同學(xué)會把自己重要的文件存放在多個地方,比如自己的電腦、網(wǎng)盤上等等。同樣,在軟件開發(fā)中,我們也應(yīng)該把重要的數(shù)據(jù)復(fù)制多份,作為副本存放在不同的地方。這樣,即使一臺服務(wù)器掛了,也可以從其他的服務(wù)器上獲取到數(shù)據(jù),減少了風(fēng)險。

數(shù)據(jù)備份

11. 心跳機(jī)制

接口可是個復(fù)雜多變的家伙,如果我們的項目依賴其他的接口來完成功能,那么最好保證該接口一直活著,否則可能會影響項目的運(yùn)行。

舉個例子,我們在使用銀行卡支付時,肯定需要調(diào)用銀行提供的接口來獲取銀行卡的余額信息,如果這個接口掛了,獲取不到余額,用戶也就無法支付,也就損失了一筆收入!

因此,我們需要時刻和重要的接口保持聯(lián)系,防止他們不小心死了??梢圆捎眯奶鴻C(jī)制,定時調(diào)用該接口或者發(fā)送一個心跳包,來判斷該接口是否仍然存活。一旦調(diào)用超時或者失敗,可以立刻進(jìn)行排查和處理,從而大大減少了事故的影響時長。

心跳檢測

12. 冗余設(shè)計

在系統(tǒng)資源和容量評估時,我們要做一些冗余設(shè)計,比如數(shù)據(jù)庫目前的總數(shù)據(jù)量有 1G,那么如果要將數(shù)據(jù)庫的數(shù)據(jù)同步到其他存儲(比如 Elasticsearch)時,至少要多預(yù)留一倍的存儲空間,即 2G,來應(yīng)對后面可能的數(shù)據(jù)增長。業(yè)務(wù)的發(fā)展?jié)摿υ酱?,冗余的倍?shù)也可以越多,但也要注意不要過分冗余,畢竟資源也是很貴的?。?/p>

其實(shí),冗余設(shè)計是一種重要的設(shè)計思想。當(dāng)我們設(shè)計業(yè)務(wù)或者系統(tǒng)架構(gòu)時,不能只局限于當(dāng)前的條件,而是要考慮到以后的發(fā)展,選擇一種相對便于擴(kuò)展的模式。否則之后項目越做越大,每一次對項目的改動都步履維艱。

13. 彈性擴(kuò)縮容

夢想還是要有的,說不定突然,我們原先只有 100 人使用的小項目突然就火了,有幾十萬新用戶要來使用。

但是,由于我們的項目只部署在一臺服務(wù)器上,根本無法支撐那么多人,直接掛掉,導(dǎo)致這些用戶非常掃興,再也不想用我們的項目了。

夢想破碎了

這也是常見的風(fēng)險,我們可以使用彈性擴(kuò)縮容技術(shù),系統(tǒng)會根據(jù)當(dāng)前項目的使用和資源占用情況自動擴(kuò)充或縮減資源。

比如當(dāng)系統(tǒng)壓力較大時,多分配幾臺機(jī)器(容器),當(dāng)系統(tǒng)壓力較小時,減少幾臺機(jī)器。這樣不僅能夠有效應(yīng)對突發(fā)的流量增長,還能夠在平時節(jié)約成本,并省去了人工分配調(diào)整機(jī)器的麻煩。

14. 異地多活

前面提到,服務(wù)器是不可信的,別說一個服務(wù)器掛掉,由于一些天災(zāi)人禍,整個機(jī)房都有可能集體掛掉!

和備份不同,異地多活是指在不同城市建立獨(dú)立的數(shù)據(jù)中心,正常情況下,用戶無論訪問哪一個地點(diǎn)的業(yè)務(wù)系統(tǒng),都能夠得到正確的服務(wù),即同時有多個 “活” 的服務(wù)。

而某個地方業(yè)務(wù)異常的時候,用戶能夠訪問其他地方正常的業(yè)務(wù)系統(tǒng),從而獲得正確的服務(wù)。

如此一來,即使廣州的機(jī)房跨了,咱還有上海的,上海的跨了,咱還有北京的。

同時活著的服務(wù)越多,系統(tǒng)就越可靠,但同時成本也越高、越復(fù)雜,因此幾乎都是大公司才做異地多活。千萬不要讓正常情況下的投入大于故障發(fā)生的損失!

餓了么異地多活技術(shù)實(shí)現(xiàn)(一)總體介紹

15. 監(jiān)控告警

項目的運(yùn)行不可能一直正常,但是我們不可能 24 小時盯著電腦屏幕來監(jiān)視項目的運(yùn)行情況吧?又不能完全不管項目,出了 bug 等著用戶來投訴。

因此,最好的方式是給業(yè)務(wù)添加監(jiān)控告警,當(dāng)程序出現(xiàn)異常時,信息會上報到監(jiān)控平臺,并第一時間給開發(fā)者發(fā)送通知。還可以通過監(jiān)控平臺實(shí)時查看項目的運(yùn)行情況,出了問題也能更快地定位。

Grafana 監(jiān)控平臺

16. 線上診斷和熱修復(fù)

既然程序世界一切都不可信,危險無處不在,那么干脆就做最壞的打算,假設(shè)線上程序一定會出 bug。

既然防不勝防,那就嚴(yán)陣以待,在 bug 出現(xiàn)時用最快的速度修復(fù)它,來減少影響。

通常,我們要改 bug,也需要經(jīng)歷改動代碼、提交代碼、合并代碼、打包構(gòu)建、發(fā)布上線等一系列流程。等流程走完了,可能系統(tǒng)都透心涼了。

為提高效率,我們可以使用線上診斷和熱修復(fù)技術(shù)。在出現(xiàn) bug 時,先用線上診斷工具輕松獲取項目的各運(yùn)行狀態(tài)和代碼執(zhí)行信息,提升排查效率。發(fā)現(xiàn)問題后,使用熱修復(fù)技術(shù)直接修改運(yùn)行時的代碼,無需重新構(gòu)建和重啟項目!

Java 中,我們可以使用阿里開源的診斷工具 Arthas,同時支持線上熱修復(fù)功能。也可以自己編寫腳本來實(shí)現(xiàn),但是相對復(fù)雜一些。

Arthas Logo

看到這里,肯定有同學(xué)會吐槽,怎么寫個程序要考慮那么多和功能無關(guān)的問題。本來五分鐘就能寫完的代碼,現(xiàn)在可能一個小時都寫不完!

超兇

其實(shí),并不是所有的項目都要做到絕對的安全(當(dāng)然我們也做不到),而是我們應(yīng)該時刻保持居安思危的思想,把防御性編程當(dāng)做自己的習(xí)慣。

實(shí)際情況下,要根據(jù)項目的量級、受眾、架構(gòu)、緊急程度等因素來綜合評估將項目做到何種程度的安全,而不是過度設(shè)計、杞人憂天。

讓我們把時間慢下來,在開發(fā)前先冷靜思考,預(yù)見并規(guī)避風(fēng)險,不要讓達(dá)摩克利斯之劍落下。


揭秘軟件開發(fā)中的達(dá)摩克利斯之劍的評論 (共 條)

分享到微博請遵守國家法律
米林县| 军事| 临沂市| 宝坻区| 永康市| 杭州市| 榆社县| 庆元县| 丰镇市| 营口市| 新沂市| 科尔| 精河县| 马公市| 鄂托克前旗| 体育| 大厂| 晋州市| 营口市| 定远县| 宁陕县| 马关县| 治县。| 承德县| 舟曲县| 重庆市| 济源市| 乌苏市| 邻水| 崇义县| 水城县| 郑州市| 南木林县| 绿春县| 山东省| 元阳县| 云安县| 改则县| 常德市| 双流县| 镶黄旗|