自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

多起宕機事故發(fā)生,竟都歸咎于最初的失敗設(shè)計……

開發(fā) 新聞
本文對面向失敗設(shè)計做了一些淺顯的思考。

2015 年 5 月,杭州市蕭山區(qū)某地光纜被挖斷,某公司支付軟件受到影響,用戶反復(fù)登錄卻無法使用,一時間#XXX炸了#成為微博熱詞;2021 年 7 月 ,某視頻網(wǎng)站深夜宕機,各系產(chǎn)品所有功能似乎全崩,直至次日凌晨才恢復(fù)服務(wù)。這兩個故事,導(dǎo)致吃瓜群眾對企業(yè)技術(shù)實力產(chǎn)生了質(zhì)疑和誤解,影響頗深……

從兩個故事可以看出,對于失敗場景考慮不充分對于企業(yè)聲譽的打擊有多大。站在程序員個體角度,面向失敗設(shè)計對于個人的影響也同樣巨大,企業(yè)的事故責(zé)任終究要落到程序員個人頭上,而事故也往往會消耗組織對于個人的信任,直接或者間接地影響個人的發(fā)展。在字節(jié)跳動,事故對個人的影響不算太大,但在其他一些公司,一次事故往往意味著程序員“一年白干”。

不同年限的程序員差異到底在哪里?這個問題,我的理解是,除了架構(gòu)設(shè)計能力、項目管理能力、技術(shù)規(guī)劃能力、技術(shù)領(lǐng)導(dǎo)力之外,面向失敗設(shè)計能力也是極其重要的一環(huán)。

業(yè)務(wù)開發(fā)的新同學(xué)有時候可能會有迷之自信,覺得自己寫的代碼與老鳥們沒有什么不同。實際上,編寫正常流程的業(yè)務(wù)代碼大家的差異不會太大,但是針對異常、邊界、不確定性的處理才真正體現(xiàn)一個程序員的功力。老鳥們往往在長期的訓(xùn)練下已經(jīng)形成多種肌肉記憶,遇到具體問題就會舉一反三腦海里冒出諸多面向失敗的設(shè)計點,從而寫出高可用的業(yè)務(wù)代碼。如何去學(xué)習(xí)面向失敗設(shè)計的方法論,并慢慢形成自己獨有的肌肉記憶,才是新手向老鳥蛻變的康莊大道。

基于這樣的考量,我寫了這篇文章,對自己這些年來的一些經(jīng)驗和教訓(xùn)做了一些總結(jié),希望能夠拋磚引玉,讓更多的老鳥們把自己的經(jīng)驗 share 出來,相互學(xué)習(xí)共同進步。

一、道

道的層面,我想講講面向失敗設(shè)計的世界觀。

1、失敗無處不在

理想中,機器硬件永不老化、系統(tǒng)軟件永不過期、流量總在預(yù)期范圍內(nèi)、自己寫的代碼沒有 bug、產(chǎn)品經(jīng)理永不改需求,但現(xiàn)實往往給你飽以老拳,給你社會的毒打:硬件一定會在某個時間點故障、軟件總在一個時間節(jié)點跟不上時代潮流、流量總在你意想不到的時候突增——即使你在婚禮上、沒有程序員不寫 bug、產(chǎn)品經(jīng)理不但天天改需求,甚至還給你提自相矛盾或者存在邏輯漏洞的需求。

無論是在傳統(tǒng)軟件時代還是在互聯(lián)網(wǎng)、云時代,系統(tǒng)終究會在某個時間點失敗。面向失敗設(shè)計不是消除失敗,而是減少乃至消除失敗造成的影響,守著企業(yè)和個人的錢袋子。

2、唯一不變的是變化

不但失敗無處不在,變化也無處不在。

1)不要寫死——你的 PM 為改需求而生

“不要寫死,你的 PM 為改需求而生”,這句話是我對口的一個產(chǎn)品經(jīng)理的飛書個性簽名,它深得我心。永遠對代碼寫死保持不安,根據(jù)墨菲定律,你越是認為不會改變的字段或功能,就越會發(fā)生改變。所以,多配置、少寫死,讓你在產(chǎn)品改需求時快速響應(yīng)從而令別人刮目相看,也能讓你在發(fā)生故障時有更多的手段做快速恢復(fù)。

2)隔離可變性——程序員應(yīng)軟件變化而生

如果系統(tǒng)軟件永不變化,我們還需要設(shè)計模式么?還需要面向?qū)ο竺??面向過程一把梭不是又快又好么?但是,永不變化的系統(tǒng)軟件,要程序員何用?抖音已經(jīng)如此強大,什么都不改也能給字節(jié)掙很多錢,那抖音的程序員都可以下崗了么?好像并非如此。

設(shè)計模式,是前輩們總結(jié)的應(yīng)對變化的利器。23 種設(shè)計模式,一言以蔽之,曰:隔離可變性。無論是創(chuàng)建型模式,還是結(jié)構(gòu)性模型,又或者是行為型模式,設(shè)計的目的都是為了把變化關(guān)進設(shè)計模式的籠子里。

3)定期回歸——功能在演化中變質(zhì)

定期回歸,也是應(yīng)對失敗的重要原則。互聯(lián)網(wǎng)的迭代實在是太快了,傳統(tǒng)軟件往往以年月為維度迭代,而互聯(lián)網(wǎng)往往以周乃至日迭代。每一天,系統(tǒng)的功能都可能在演化中變質(zhì),快速的迭代不但讓業(yè)務(wù)代碼迅速腐化變成屎山,也讓內(nèi)部邏輯日益臃腫,乃至相互沖突。終有一天,原本運行良好無 bug 的代碼,會變成事故的導(dǎo)火索。

3、對代碼的世界保持警惕

對代碼的世界保持警惕吧,不然總有一天你會經(jīng)歷血淚教訓(xùn)。

1)不要相信合作方的“鬼話”

對合作方給你的所有接口、方案保持懷疑,也不要相信合作方任何一個未經(jīng)你親身驗證的論斷。實踐才是檢驗真理的唯一標準,對世界始終保持懷疑是工程師的核心素質(zhì)。不要在出現(xiàn)故障之后跟合作方相互甩鍋時才追悔莫及,前期多做些驗證,保護了你也保護了他,更是保護了你們之間的塑料友情。

2)不要相信代碼注釋

一行錯誤的代碼注釋,把我從阿里帶到了字節(jié),親身經(jīng)歷的血淚教訓(xùn)。錯誤的代碼注釋不如沒有注釋,不要再用錯誤的注釋給后來人埋坑了,救救孩子吧。

3)不要相信函數(shù)輸入

NPE(NullPointerException 空指針異常)也許是程序員職業(yè)生涯中遇到過的最多的錯誤,這一點頗令人困惑,因為程序員從刷 LeetCode 第一道題開始,就知道需要對函數(shù)參數(shù)做檢查。

之所以出現(xiàn)這樣的結(jié)果,是因為線上生產(chǎn)環(huán)境所能遭遇的場景遠比一道代碼題復(fù)雜,這其實也是工業(yè)界與學(xué)術(shù)界的區(qū)別,學(xué)術(shù)界的問題是確定的,工業(yè)界的問題是不確定的。即使上游傳遞參數(shù)的是一個你認為極為可靠的系統(tǒng),即使你遍覽程序上下文確定不會出現(xiàn)空參數(shù),也最好去做一些防御性的設(shè)計,因為可靠的系統(tǒng)也會給你返回不合規(guī)范的參數(shù),當前不存在空參數(shù)的代碼在未來的某一天也會被改得面目全非。

4)不要相信基礎(chǔ)設(shè)施

即使是支付寶也會崩潰,即使是可用性 6 個 9 的系統(tǒng),全年也有 31 秒中斷。不要相信基礎(chǔ)設(shè)施,做好災(zāi)備,搞好混沌工程,才能讓你每個晚上睡得安穩(wěn),避免被報警電話打醒。

4、設(shè)計原則

1)簡潔的方案最優(yōu)雅

如果你設(shè)計的技術(shù)方案沒有太多的花里胡哨,整體透露著一種大道至簡的美感,也許你就離成功很近了。簡潔的方案代表著更小的理解成本、更小的維護成本、更好的擴展性。

如果你的方案里面到處都是花里胡哨的炫技,看起來復(fù)雜而嚴謹,那么也許你離讓自己頭疼也讓別人頭疼不遠了,一頓操作猛如虎,一看月薪兩千五。

當然,并不是最簡潔的方案就是最合適的方案,舉個例子,核心交易鏈路的服務(wù)必然會比數(shù)據(jù)展示的服務(wù)穩(wěn)定性要求更高,因而做了較多高可用設(shè)計之后方案會更加復(fù)雜,因而在滿足穩(wěn)定性的前提下選用盡可能簡潔的方案才是推薦的做法。

2)開閉原則是設(shè)計模式的總綱

開閉原則是設(shè)計模式的總綱,大部分設(shè)計模式里面都有開閉原則的影子,軟件實體應(yīng)當對擴展開放,對修改關(guān)閉,可以通過“抽象約束、封裝變化”來實現(xiàn)開閉原則。開閉原則可以使軟件實體擁有一定的適應(yīng)性和靈活性的同時具備穩(wěn)定性和延續(xù)性。

基于開閉原則,很多常見的設(shè)計問題都有了答案:

① 大量 if-else 的屎山代碼問題

大量的 if-else 肯定是不符合開閉原則的,每一個 if-else 的代碼支路都是對原有代碼結(jié)構(gòu)的破壞,這里就可以應(yīng)用工廠+策略設(shè)計模式對 if-else 進行剝離,把邏輯的新增和修改限制在工廠模式子類的內(nèi)部。

② 冗長的業(yè)務(wù)工作流處理問題 

業(yè)務(wù)流程代碼往往非常冗長,封裝得不好的話閱讀和維護代碼都非常困難,可以考慮用命令+職責(zé)鏈設(shè)計模式對工作流做封裝。封裝的好處在于,整體的工作流讀起來將非常清晰,主流程代碼往往能從數(shù)百行精簡到十行以內(nèi),并且,對流程的修改僅僅是簡單的斷鏈或者增加鏈節(jié)點的操作,從而把修改的影響減到最低。

③ 歷史字段類型修改問題

互聯(lián)網(wǎng)開發(fā)過程中經(jīng)常需要修改歷史字段的類型,根據(jù)開閉原則,我們不該去修改原有字段的類型,而應(yīng)該新增一個字段,這樣才能保證對上下游鏈路的影響最小。

④ 對象屬性中途篡改問題

舉個實際的業(yè)務(wù)場景,在某些業(yè)務(wù)請求中,抖音極速版需要做與抖音相同的處理,把抖音極速版的 APPID 改成抖音的 APPID 是最簡單的方法,但是這種做法是不符合開閉原則的,對對象屬性中途的篡改,會改變對象在程序中的語義,總有一天它會有不符合預(yù)期的表現(xiàn),很多事故因此而起。正確的做法是,在上下文中傳遞一個新的字段,下游的每一步處理都可以選擇正確的字段做正確的處理,而不會被中途篡改的字段蒙蔽。

3)懶惰是程序員最大的美德

懶惰是程序員最大的美德,好的程序員往往是默默無聞的,越是在團隊里面滋哇亂叫到處救火刷存在感的程序員越可能是團隊的慢性毒藥。

為了讓自己懶惰,安安穩(wěn)穩(wěn)躺平就把業(yè)務(wù)做好,程序員必須掌握平臺化、工具化、自動化三板斧。平臺化,把程序員從無窮盡的重復(fù)勞動中解救出來;工具化,把程序員從水深火熱的人肉運維和 oncall 中解救出來;自動化,讓程序如流水線般順滑,從而提升程序員的人效。能將這三板斧揮舞到什么層次,也體現(xiàn)了程序員能力到達了什么層次。有了平臺化、工具化、自動化,就可以做標準化、規(guī)模化,助力公司和業(yè)務(wù)持續(xù)往上走。

二、術(shù)

術(shù)的層面,我想講講在組織和流程角度如何面向失敗設(shè)計。

1、組織

1)面向失敗設(shè)計的工種

測試工程師、測試開發(fā)工程師、風(fēng)控&安全合規(guī)工程師都是開發(fā)工程師最可靠的合作伙伴,也是企業(yè)為了面向失敗設(shè)計而設(shè)置的工種。

測試工程師是軟件質(zhì)量的把關(guān)者,他們是線上質(zhì)量的衛(wèi)士,對開發(fā)工程師代碼的質(zhì)量和性能負責(zé)。測試開發(fā)工程師是一個技術(shù)型的軟件測試工種,除了做常規(guī)的測試工作之外,還會寫一些測試工具和自動化腳本,用自動化的手段來提高測試的質(zhì)量和效率。風(fēng)控和反作弊工程師對業(yè)務(wù)的生態(tài)負責(zé),監(jiān)測業(yè)務(wù)的異常問題,提高業(yè)務(wù)風(fēng)控的效果。安全合規(guī)工程師則是對信息安全負責(zé),能夠?qū)τ陧椖刻峁┖弦?guī)咨詢、信息安全風(fēng)險評估。

2)面向失敗設(shè)計的組織形式

安全生產(chǎn)小組是一種面向失敗設(shè)計的組織形式。安全生產(chǎn)小組往往是橫向的技術(shù)團隊,對多個業(yè)務(wù)團隊提供規(guī)范制定和推行、生產(chǎn)過程管控、事故復(fù)盤組織等技術(shù)支持,為線上質(zhì)量負責(zé),通常還會在每個業(yè)務(wù)團隊設(shè)置系統(tǒng)穩(wěn)定性負責(zé)人,作為接口人來有效推行他們制定的制度。

結(jié)對編程,也是一種面向失敗設(shè)計的組織形式。嚴格意義的結(jié)對編程,要求兩個程序員在一個計算機上共同工作。一個人輸入代碼,而另一個人審查他輸入的每一行代碼。結(jié)對編程可以讓程序員寫出更短的程序,更好的設(shè)計,以及更少的缺陷,同時,結(jié)對編程也可以促進知識的傳播,讓新人快速進步,也讓老人在帶新的過程中總結(jié)自己的知識和經(jīng)驗,還可以規(guī)避在相應(yīng)開發(fā)人員請假或者離職帶來的工作交接的問題。

嚴格意義的結(jié)對編程,在互聯(lián)網(wǎng)行業(yè)極為罕見,很少有團隊會真正這樣實操,也許是因為在管理者看來,兩個人干同一件事情大大增加了人力的成本。但是,結(jié)對編程的一些思想和理念,也值得我們借鑒,比如我們可以讓兩個程序員結(jié)對做業(yè)務(wù) owner,互為 backup,相互 code review,從而在一定程度上獲得結(jié)對編程的好處。

2、流程

假設(shè)不做面向失敗設(shè)計,那么軟件開發(fā)流程也許可以簡化為編碼+發(fā)布兩步。但是成熟企業(yè)的開發(fā)流程大致如下:

  • 需求提出階段

需要先期做一些合規(guī)評估、反作弊評估、安全評估,在前期就把一些潛在的安全合規(guī)風(fēng)險排除。

  • 編碼階段

在設(shè)計技術(shù)方案時需要考慮止血/降級/回滾措施,并組織技術(shù)評審和安全技術(shù)評審,針對技術(shù)方案中的安全風(fēng)險做一些評估。除此之外,最好做一些單元測試,可以大大提高代碼的質(zhì)量。

  • 測試階段

需要開發(fā)人員先做自測,再讓測試工程師參與功能測試、安全工程師做安全檢查,針對代碼改動可能造成的額外影響,做好做一次更大范圍的回歸測試,以排除一些預(yù)期外的影響。

  • 發(fā)布階段

需要采用灰度發(fā)布的機制,先發(fā)布小部分機器,或者僅針對部分地區(qū)用戶灰度,在灰度發(fā)布之后做灰度測試驗證功能正常,再繼續(xù)分批發(fā)布、全量發(fā)布。

  • 驗證階段

可以讓測試同學(xué)在發(fā)布完成之后做一次線上回歸,保證功能在線上環(huán)境穩(wěn)定可用。對于大型活動,往往還需要組織內(nèi)部用戶線上預(yù)演或眾測。針對非預(yù)期內(nèi)流量可能把系統(tǒng)打掛的風(fēng)險,可以做單鏈路壓測和全鏈路壓測。在大型活動開始前,如果條件允許,或者在小范圍做一次線上試玩,提前暴露一些風(fēng)險。

  • 運行階段

需要開發(fā)人員做好監(jiān)控報警和離在線數(shù)據(jù)對賬。對于項目的效果,可以用 AB 測試來量化收益。

  • 故障發(fā)生時

第一時間必須做好故障快速恢復(fù),盡可能減少線上損失,之后再考慮定位故障原因。

在項目結(jié)束或者故障處理結(jié)束之后,需要組織一次有效的復(fù)盤,并對過程中的問題做一些總結(jié),形成有效的改進方案,并持續(xù)跟進改進方案的落地

3、一些觀點

1)測試同學(xué)的重要性,怎么吹都不為過

測試工程師是線上質(zhì)量最重要的衛(wèi)士,他們的重要性,怎么吹都不為過。一個優(yōu)秀的測試同學(xué),可以做到以下事情:

  • 非黑盒測試,具備讀懂開發(fā)代碼的能力,根據(jù)代碼針對性地設(shè)計測試用例;
  • 設(shè)計完備的測試用例,覆蓋所有測試場景;
  • 編寫數(shù)據(jù)對賬腳本,能夠做離線數(shù)據(jù)對賬和實時數(shù)據(jù)對賬;
  • 編寫自動化測試工具;
  • 編寫數(shù)據(jù)一致性監(jiān)控腳本、資損防控工具。

2)單元測試最省時間

編寫單元測試用例,看似費時間,實則是最省時間的做法。單元測試保證了代碼的行為與我們期望一致,從而省下了大量的發(fā)布、自測、聯(lián)調(diào)、修改代碼的返工時間,另外,可以做單元測試的代碼往往職責(zé)更加清晰、分層分塊更加合理、穩(wěn)定性更好。

3)復(fù)盤是對齊做事高標準的一個必要方式

復(fù)盤是不斷優(yōu)化組織,對齊做事高標準的一個必要方式。通過 PDCA(Plan-Do-Check-Action,戴明環(huán))這樣的一個循環(huán),工作在不斷的改善后,最終形成知識沉淀,作用于下一次計劃執(zhí)行,團隊于是變得越來越有執(zhí)行力,個人則成為 Better Me。

4)研發(fā)紅線是程序員的保護傘

研發(fā)紅線是企業(yè)面向失敗設(shè)計行之有效的暴力機器,它由無數(shù)零件(規(guī)范和條目)組成、冰冷、機械、運行起來無法阻擋,不以個人意志為轉(zhuǎn)移。研發(fā)紅線強制要求程序員遵守企業(yè)的流程和規(guī)范,警告程序員不犯低級錯誤,看似冰冷無情,實則是程序員的保護傘。

三、技

在技的層面,我想談?wù)劽嫦蚴≡O(shè)計的具體技術(shù)細節(jié)。但是技術(shù)細節(jié)實在太多,限于篇幅,此處只列舉一些經(jīng)典技術(shù)問題的解法。

1、將面向失敗當做系統(tǒng)設(shè)計的一部分

  • 針對非預(yù)期流量,可以做系統(tǒng)限流、系統(tǒng)過載保護、自適應(yīng)擴縮容;
  • 針對依賴服務(wù)超時或錯誤,需要對依賴系統(tǒng)設(shè)置超時時間,并對所有依賴做強弱依賴梳理,關(guān)鍵時刻降級非核心依賴;
  • 針對預(yù)期外的情況,可以提前準備好緊急預(yù)案,并做好預(yù)案演練;
  • 針對瞬時高流量,需要敏銳地判斷系統(tǒng)的極限,做好流量打散,并避免 DB 和緩存熱 key;
  • 針對可能出現(xiàn)的機房問題,做好同城雙(多)活和異地多活;
  • 針對人為失誤,可以使用平臺化、工具化、自動化的方法減少人肉操作;
  • 避免出現(xiàn)單點問題,做冗余設(shè)計來降低局部失敗對系統(tǒng)的影響;
  • 失敗重試時需謹慎,避免踩踏雪崩;
  • 故障只能減少,不能消除,做好監(jiān)控報警、故障演練、攻防演練,錘煉風(fēng)險應(yīng)急能力。

2、分布式鎖的六個層次

你只看到了第二層,你把我想成了第一層。實際上,我在第五層。

——蕪湖大司馬

Redis 實現(xiàn)分布式鎖有六個層次,看看大家平常用的分布式鎖處在第幾個層次。

分布式鎖設(shè)計原則:

  • 互斥性

在任意時刻,只有一個客戶端持有鎖。

  • 不死鎖

分布式鎖本質(zhì)上是一個基于租約(Lease)的租借鎖,如果客戶端獲得鎖后自身出現(xiàn)異常,鎖能夠在一段時間后自動釋放,資源不會被鎖死。

  • 一致性

硬件故障或網(wǎng)絡(luò)異常等外部問題,以及慢查詢、自身缺陷等內(nèi)部因素都可能導(dǎo)致 Redis 發(fā)生高可用切換,replica 提升為新的 master。此時,如果業(yè)務(wù)對互斥性的要求非常高,鎖需要在切換到新的 master 后保持原狀態(tài)。

1)層次一

redis.SetNX(ctx, key, "1")
defer redis.del(ctx, key)

使用 SetNx 命令,可以解決互斥性的問題,但不能做到不死鎖。

2)層次二

redis.SetNX(ctx, key, "1", expiration)
defer redis.del(ctx, key)

使用 lua 腳本保證 SetNX 與 Expire 的原子性,做到了不死鎖,但是做不到一致性。

3)層次三

redis.SetNX(ctx, key, randomValue, expiration)
defer redis.del(ctx, key, randomValue)


// 以下為del的lua腳本
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end

分布式鎖的值設(shè)定一個隨機數(shù),刪除時只刪除

func myFunc() (errCode *constant.ErrorCode) {
errCode := DistributedLock(ctx, key, randomValue, LockTime)
defer DelDistributedLock(ctx, key, randomValue)
if errCode != nil {
return errCode
}
// doSomeThing
}


func DistributedLock(ctx context.Context, key, value string, expiration time.Duration) (errCode *constant.ErrorCode) {


ok, err := redis.SetNX(ctx, key, value, expiration)
if err == nil {
if !ok {
return constant.ERR_MISSION_GOT_LOCK
}
return nil
}
// 應(yīng)對超時且成功場景,先get一下看看情況
time.Sleep(DistributedRetryTime)
v, err := redis.Get(ctx, key)
if err != nil {
return constant.ERR_CACHE
}
if v == value {
// 說明超時且成功
return nil
} else if v != "" {
// 說明被別人搶了
return constant.ERR_MISSION_GOT_LOCK
}
// 說明鎖還沒被別人搶,那就再搶一次
ok, err = redis.SetNX(ctx, key, value, expiration)
if err != nil {
return constant.ERR_CACHE
}
if !ok {
return constant.ERR_MISSION_GOT_LOCK
}
return nil
}
// 以下為del的lua腳本
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end


// 如果你的Redis版本已經(jīng)支持CAD命令,那么以上lua腳本可以改為以下代碼
func DelDistributedLock(ctx context.Context, key, value string) (errCode *constant.ErrorCode) {
v, err := redis.Cad(ctx, key, value)
if err != nil {
return constant.ERR_CACHE
}
return nil
}

解決超時且成功的問題,寫入超時且成功是偶現(xiàn)的、災(zāi)難性的經(jīng)典問題。

還存在的問題是:

  • 單點問題,單 master 有問題,如果有主從,那主從復(fù)制過程有問題時,也存在問題;
  • 鎖過期然后沒完成流程怎么辦。

5)層次五

啟動定時器,在鎖過期卻沒完成流程時續(xù)租,只能續(xù)租當前線程/協(xié)程搶占的鎖。

// 以下為續(xù)租的lua腳本,實現(xiàn)CAS(compare and set
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("expire",KEYS[1], ARGV[2])
else
return 0
end


// 如果你的Redis版本已經(jīng)支持CAS命令,那么以上lua腳本可以改為以下代碼
redis.Cas(ctx, key, value, value)

能保障鎖過期的一致性,但是解決不了單點問題。

同時,可以發(fā)散思考一下,如果續(xù)租的方法失敗怎么辦?我們?nèi)绾谓鉀Q“為了保證高可用而使用的高可用方法的高可用問題”這種套娃問題?開源類庫 Redisson 使用了看門狗的方式一定程度上解決了鎖續(xù)租的問題,但是這里,個人建議不要做鎖續(xù)租,更簡潔優(yōu)雅的方式是延長過期時間,由于我們分布式鎖鎖住代碼塊的最大執(zhí)行時長是可控的(依賴于 RPC、DB、中間件等調(diào)用都設(shè)定超時時間),因而我們可以把超時時間設(shè)得大于最大執(zhí)行時長即可簡潔優(yōu)雅地保障鎖過期的一致性。

6)層次六

Redis 的主從同步(replication)是異步進行的,如果向 master 發(fā)送請求修改了數(shù)據(jù)后 master 突然出現(xiàn)異常,發(fā)生高可用切換,緩沖區(qū)的數(shù)據(jù)可能無法同步到新的 master(原 replica)上,導(dǎo)致數(shù)據(jù)不一致。如果丟失的數(shù)據(jù)跟分布式鎖有關(guān),則會導(dǎo)致鎖的機制出現(xiàn)問題,從而引起業(yè)務(wù)異常。針對這個問題介紹兩種解法:

① 使用紅鎖(RedLock)

紅鎖是 Redis 作者提出的一致性解決方案。紅鎖的本質(zhì)是一個概率問題:如果一個主從架構(gòu)的 Redis 在高可用切換期間丟失鎖的概率是 k%,那么相互獨立的 N 個 Redis 同時丟失鎖的概率是多少?如果用紅鎖來實現(xiàn)分布式鎖,那么丟鎖的概率是(k%)^N。鑒于 Redis 極高的穩(wěn)定性,此時的概率已經(jīng)完全能滿足產(chǎn)品的需求。

紅鎖的問題在于:

  • 加鎖和解鎖的延遲較大。
  • 難以在集群版或者標準版(主從架構(gòu))的 Redis 實例中實現(xiàn)。
  • 占用的資源過多,為了實現(xiàn)紅鎖,需要創(chuàng)建多個互不相關(guān)的云 Redis 實例或者自建 Redis。

② 使用 WAIT 命令

Redis 的 WAIT 命令會阻塞當前客戶端,直到這條命令之前的所有寫入命令都成功從 master 同步到指定數(shù)量的 replica,命令中可以設(shè)置單位為毫秒的等待超時時間??蛻舳嗽诩渔i后會等待數(shù)據(jù)成功同步到 replica 才繼續(xù)進行其它操作。執(zhí)行 WAIT 命令后如果返回結(jié)果是 1 則表示同步成功,無需擔(dān)心數(shù)據(jù)不一致。相比紅鎖,這種實現(xiàn)方法極大地降低了成本。

3、熱點庫存扣減

秒殺是非常常見的面試題,很多面試官上來就讓面試者設(shè)計一個秒殺系統(tǒng),面試者當然也是“身經(jīng)百戰(zhàn)”,很快可以給出熟背的“標準答案”。

但是,秒殺還是相對簡單的熱點庫存扣減問題,因為扣減的庫存量不大。更加典型的熱點庫存扣減問題是春節(jié)紅包雨,同一個資金池數(shù)億人搶紅包。對于春節(jié)紅包雨介紹兩種方案:

1)方案一

存在問題:

  • 不同分桶之間,庫存消耗不均,可能導(dǎo)致部分用戶無法扣減庫存,但其他用戶可扣減庫存,從而引發(fā)用戶投訴。

2)方案二

小量多次地分派庫存,從而緩解分桶庫存消耗不均問題。

2021 年抖音春節(jié)紅包,將用戶進入的時間打散,減少瞬時請求峰值,也是一個很好的技術(shù)思路。

3)如何體現(xiàn)面向失敗設(shè)計

① 為何用定時任務(wù)調(diào)度主動分配庫存,而不是在分桶庫存不足時被動拉庫存?

答:因為主動分配庫存 QPS 比被動拉庫存低幾個量級。

② 如何應(yīng)對超大流量?

答:流量不觸達 DB、分桶、打散。

③ Redis 庫存總池為何不用某個 master 機器維護,而用定時任務(wù)調(diào)度隨機挑選機器?

答:防單點。

編程之美,蔚為大觀。好的代碼,往往結(jié)構(gòu)清晰,表意明確,設(shè)計精巧,無論是讀代碼還是寫代碼都可以給程序員一種直擊心靈的美感,甚至讓讀者愛不釋手,讓作者引以為傲,引之為自己的代表作。但是,為了留住這種美,我們還需要去做面向失敗的設(shè)計,充分考慮失敗場景,才能減少失敗的概率,向死而得生。

本文對面向失敗設(shè)計做了一些淺顯的思考,歡迎探討、補充和指正。

責(zé)任編輯:張燕妮 來源: dbaplus社群
相關(guān)推薦

2022-08-30 00:14:43

勒索軟件安全漏洞

2010-03-31 15:59:17

統(tǒng)一通信UC

2021-02-07 10:43:34

人工智能人才缺口AI

2018-08-01 15:06:45

2009-11-05 19:07:12

2021-06-10 07:08:47

Fastly漏洞

2021-10-20 09:23:33

物聯(lián)網(wǎng)設(shè)備制造商物聯(lián)網(wǎng)安全

2024-09-26 06:18:08

2024-12-19 18:26:13

2021-05-13 07:11:49

stagramTwitter系統(tǒng)故障

2021-03-01 20:10:23

密碼網(wǎng)絡(luò)攻擊網(wǎng)絡(luò)安全

2019-01-13 17:22:27

云計算宕機微軟

2021-12-08 10:54:13

亞馬遜宕機事故云服務(wù)

2020-05-17 15:54:21

密碼漏洞攻擊

2015-10-29 17:55:32

存儲雙活宕機銀行

2011-05-27 09:04:39

Skype宕機

2015-06-01 16:25:07

數(shù)據(jù)中心云計算

2020-09-07 08:42:13

宕機業(yè)務(wù)運維

2019-01-16 09:20:42

架構(gòu)設(shè)計JVM FullGC宕機事故

2023-04-21 14:17:45

點贊
收藏

51CTO技術(shù)棧公眾號