本文資訊量略大,建議閱讀時間:10分鐘
張逸老師曾說,軟體系統的穩定性,主要決定于整體的系統架構設計,然而也不可忽略程式設計的細節,正所謂“千裡之堤,潰于蟻穴”,一旦考慮不周,看似無關緊要的代碼片段可能會帶來整體軟體系統的崩潰。本文将和大家聊一聊軟體品質穩定性問題與求解。

納西姆·尼古拉斯·塔勒布(nassim nicholas taleb)寫了兩部超級暢銷書《随機緻富的傻瓜》和《黑天鵝》,并且被譽為[黑天鵝之父]。何為黑天鵝?
在發現澳洲之前,17世紀之前的歐洲人認為天鵝都是白色的。但随着第一隻黑天鵝的出現,這個不可動搖的信念崩潰了。黑天鵝的存在寓意着不可預測的重大稀有事件,它在意料之外并且後果非常嚴重。
一個黑天鵝事件,具有這三個特點:
(1)稀缺、通常史無前例(rarity),
(2)影響很極端(extreme impact),
(3)雖然它具有意外性,但人的本性促使我們在事後為它的發生編造理由,并且或多或少認為它是可解釋和可預測的。
在it系統、社會事件尤其是金融市場,[黑天鵝事件]屢見不鮮。列舉著名的黑天鵝事件如下:
在1933~1934年,經曆過大蕭條之後誕生的羅斯福新政,宣布私人持有黃金為非法,規定以每盎司20.67美元将私人黃金上收,然後由國會立法将黃金定價為每盎司35美元,美元很快貶值69%。
2001年9月11日上午,美國人剛準備開始一天的工作,恐怖分子劫持了四架飛機撞向美國紐約世貿中心與華盛頓五角大樓。3000多人在這次黑天鵝事件中喪生,美國的經濟此後一度處于癱瘓狀态,巨大的經濟損失無法用數字來統計
2013年8月16日11點05分上證指數出現大幅拉升大盤一分鐘内漲超5%。最高漲幅5.62%,指數最高報2198.85點,盤中逼近2200點。11點44分上交所稱系統運作正常。下午2點,光大證券公告稱政策投資部門自營業務在使用其獨立的套利系統時出現問題。有媒體将此次事件稱為“光大證券烏龍指事件”。
對于烏龍指的事故複盤,觸發原因是系統缺陷,政策投資部使用的套利政策系統出現了問題。該政策投資部門系統完全獨立于公司其他系統,甚至未置于公司風控系統監控下,是以深層次原因是多級風控體系都未發生作用。
向經驗學習的局限性
弗朗西斯·培根就曾經發出這樣的警告:當心被我們自己思想的絲線絲絲束縛。無論是“光大證券烏龍指事件,還是泰坦尼克的沉沒,如果業态沒有類似的案例,其學習的參考是脆弱的,無從學起。即使有業界案例,不同組織,不同公司未必擁有相應的處置經驗,那麼其實[自己的思想],[自己的經驗]也是非常有局限性的。我們把自己知道的東西太當回事了警醒地指出:我們把自己知道的東西太當回事了,而不知道的事比知道的事更有意義。隻有反常地思考一切,才有可能發現更多“不知道的事”。
上個世紀70年代,美國一個名叫洛倫茲的氣象學家在解釋空氣系統理論時說,亞馬遜雨林一隻蝴蝶翅膀偶爾振動,也許兩周後就會引起美國得克薩斯州的一場龍卷風。蝴蝶效應的意思是初始條件十分微小的變化經過不斷放大,對未來狀态也許會造成極其巨大的影響。
我們來讀一則呂氏春秋,大抵也有這樣的例子。[楚之邊邑曰卑梁,其處女與吳之邊邑處女桑于境上,戲而傷卑梁之處女。卑梁人操其傷子以讓吳人,吳人應之不恭,怒,殺而去之。吳人往報之,盡屠其家。卑梁公怒,曰:“吳人焉敢攻吾邑?”舉兵反攻之,老弱盡殺之矣。吳王夷昧聞之,怒,使人舉兵侵楚之邊邑,克夷而後去之。吳、楚以此大隆。(《呂氏春秋·察微》)]
呂氏春秋裡面說個姑娘因遊戲起沖突而引發了2個國之間的持續戰争,比較形象的放大這樣一個道理:如不能見微知著,則其後果無法預知。
對it系統而言,對于非預期的錯誤比如:
非預期error
非預期的調用抖動
極少數場景下的規則未被正确處理
錯誤的優惠處理邏輯
未正确設定的營銷活動
……
如果不具備快速、智能的感覺能力,那麼可能影響的使用者變多、影響的商戶增加、資金損失增加、業務不可用時間變長…..
“墨菲定律”是一種心理學效應,是由愛德華·墨菲(edward a. murphy)提出的。
主要觀點:
任何事都沒有表面看起來那麼簡單;
所有的事都會比你預計的時間長;
會出錯的事總會出錯;
如果你擔心某種情況發生,那麼它就更有可能發生。
墨菲定律在生活中屢見不鮮。比如關鍵時刻掉鍊子(那些駕考被教練最看好的精英們,往往會多補考2次!!!),你出去買爆米花的時候,銀幕上偏偏就出現了精彩鏡頭,是以評論一部電影如何精彩往往會用一個詞:無尿點!
對于it系統而言,墨菲定律的例子太多了。
舉個例子,小明在做系統遷移,曆時半年。小明是一位經驗豐富的架構師,他對系統遷移過程中的自校驗、核對、切流政策、灰階能力、復原機制、容錯處理都進行了充分的考慮。但是對于老系統的一種流程處理的缺陷未充分考慮備案或者處理方案。想想,半年很快就過去了,去年才發生1起這樣的特殊規則,我在新系統上完全規避了這個問題…但是不湊巧,這個特殊規則不約而至,而老系統還未遷移完…
再說一個例子,前公司有一個非常古老的系統,一直活得好好的。但是由于rpc調用中有重試機制,在網絡異常的情況可能下會被觸發。而該系統對于重複請求的機制處理不是很好,導緻如果重複了,就需要一個處理機制。而該系統的處理機制在95%的情況下是有效的,而網絡重發的機率經過經驗測算是一億分之一。看起來論據很充分了,真心是小機率事件。但是随着業務的發展,以及某些未預期的因素(比如某應用逾時的幾率)增大,則重發的機率也将增大,導緻後來這樣的問題連續幾周都出現了,我們不得不下決心從根本上解決這個問題。
第三個例子,是我們團隊的一個親身經曆。某一天有客戶投訴,按理說對于該問題的處理預案是有的,并且團隊有充分的備份機制,好幾個人都可以解決。but我們并未按預期的速度處理好這個問題。原因是團隊的一位同學大婚,大家都去迎親去了,tl同學隻能臨時把車停到路邊,處理問題。
由于人類認識的局限性、驕傲心态、問題域的複雜性、不可把握性等因素,導緻軟體從業人員在處理軟體品質穩定性方面如履薄冰,你今天志得意滿,明天就可能傷心欲絕。那麼軟體品質問題的棘手主要有那些因素導緻的呢?
我們從黑天鵝事件談到了蝴蝶效應、墨菲定律。一言以蔽之為,要把軟體研發中的品質搞好并非易事。品質是一個綜合的大命題,涉及到業務準确性、穩定性、可用性等方面。比如xx大促,某廠商幾小時無法交易;收藏夾商品丢失;使用者享受的優惠不符合預期,營銷規則複雜難以解釋等。
黑天鵝告訴我們要走出經驗主義,不要把自己知道的東西太當回事了,而不知道的事比知道的事更有意義。蝴蝶效應告訴我們要及時感覺問題,止損和熔斷,避免問題範圍擴大包括傳染到其它相關領域。墨菲定律告訴我們,你知道的但是忽略的漏掉終會出問題,在黎明破曉前!
那麼,我們得思考一下為什麼會這樣。在n年前,就有人想把軟體從業者變成像制造勞工一樣的,不斷流水線工作。但是這幾乎沒什麼可能,因為要解決的問題域太複雜。雖然業界有很多規範、标準、套裝軟體,但是仍然未解決問題之萬一。我們來看一下是如何複雜的。
以我們的一個team為例,7個人1年做了400多個需求。平台能力在不斷發展,有人說高速公路換輪胎,有人說飛機飛行換引擎,這樣的事情也沒少幹。大家都知道滿足需求,實作業務價值是軟體的天職,至于為了更好适應未來發展的平台化能力也好,新特性也好隻能在業務發展的過程中做掉。
在這麼多需求的過程中,除了技術以外,對于業務包括規則要有深度把握,包括上下遊的一些問題。如有評估不到位,問題就大了。分析到設計階段的缺失,到代碼、測試、釋出這些階段可能一如既往的缺失了。早些年,某些系統已經複雜到隻有1-2個人能搞懂部分了,幸好這些系統今天都完成了拆分和治理。
技術債務是由ward cunningham在1992年的報告中創造的一個比喻,被定義為當我們有意或無意地做了錯誤的或不理想的技術決策所累積的債務。它和金融債務非常相似。一個人貸款了就會産生債務。如果他定期還款,那麼所建立的債務是可以接受的,不會産生進一步的問題。但是,如果他不還款,就會以利息作為懲罰,并随着不還款次數的增加而增加。如果這個人很長一段時間不能支付任何款項,那麼應計利息使得他更難以償還債務。在極端情況下,該人不得不宣布自己破産。
為了快速做業務,采取簡單粗暴的方案是大家表示支援的。[臨時方案]的毛病不在于這2個月臨時了,而在于這個臨時上線之後,再無人管了,俗稱[有人生、沒人養]。1年如果做5個臨時方案,就等于欠了5筆債務。欠債并不可怕,怕的是沒有償還計劃,或者借口沒有時間,或者接口等業務不那麼高速發展的時候。吊詭的是好的業務一定是永遠都沒有時間的,而差的業務确實不用發展了,因為被下線了,或者整個公司close了。so,珍惜高速發展的業務,記得去更換引擎,償還債務,欠的,遲早要還的。有關技術債,推薦當當史大官人在周評比中名列前茅的文章:當技術宅遇上技術債,我個人覺得是這方面有調皮調性的好文章。
人、流程、制度、文化都是需要的。但面對複雜的問題域,人類有簡單化思考的傾向。比如某兄弟把手工修改的代碼通過自動生成工具覆寫掉了,這時我們有2種選擇。1是通過2個目錄來區分,1是搞一個檢查工具來做合并前的check。這些做法都沒有問題,但是不要都靠腦子去記住。
一個存在3年以上的産品,當新人來到的時候,在第一個月他們總會提一個問題,xx産品咋采取這樣的技術啊;進入慢、一點文檔都沒有;隻能看代碼,代碼寫得還不咋的。當新人成了老人,他們對更新的新人解答的時候,會說我們當年更慘,隻有看代碼是靠譜的。對了,小強是非常了解某一塊的,有問題找他,比看文檔有用。我們不得不承認的事實包括:
寫多少文檔,如何維護[活文檔]仍是一個大問題
關鍵時刻靠人傳、幫、帶,靠傳承
技術人員有采用新工具、新語言、新開源産品的追求。當全棧、多語言風潮席卷各社群的時候,不羅列一堆名詞真心都不好意思出來打招呼。另外還涉及到技術帶頭人的偏好問題。
某電商的朋友這樣講述了他們的故事:......可能更多時候是業務方變動太大,因為創始人不懂技術。然後這種模式沒有可借鑒的,是以在業務上一直都是在變動,然後就形成了一種業務混亂的感覺......後來,招了cto,cto是以前做移動語音雲平台的,他們都是用資料庫oracle來做業務,是以來公司後,改第二版時候把大量的業務邏輯轉移到資料庫裡用存儲過程來解決,現在的系統就是亂七八糟了。 此為cto經驗偏好症!
另外,組織架構、業務架構和技術架構息息相關。銀行的某兄弟使用esb之後,如是說,esb核心的理念還是服務化,但是我們行裡實際情況,服務的梳理一開始就沒做好,而且從目前的組織架構來說也很難有起色(服務梳理這塊主要靠科技,業務參與程度太低),而且所有服務的釋出都是通過esb,造成esb這邊是個開發熱點。而且,服務化要業務參與進來還需要大boss那邊驅動了~銀行的業務很多時候都是打太極,隻管各自的一畝三分地...
一般的文章也好,分享也好。多是講成功的case,講講走過的彎路其實對于廣大人民群衆是更喜聞樂見的。[雪球服務化實踐曆程] 這篇文章談到他們采用 finagle的情況,頗有啟發性。
唐福林演講中說到:“雪球的 scala 技術團隊免不了有一些人員更替,有轉産品的,有轉管理的,有轉去做另外的業務項目的,導緻後面的架構更新和二次開發力量嚴重不足。加上 finagle 自己疊代速度快,向後兼任又差,整個一個 no zuo no die why you try 的感覺”。于是,唐福林個人花了差不多兩周的時間,做了一個簡單版本 rpc 架構的嘗試。得益于在微網誌做 motan 架構的經驗和教訓,架構開發很快,開發出來後,拿給整個技術團隊做讨論的時候,才發現問題很多:再後來,團隊在針對 rpc 架構的接下來需求的讨論過程中,越讨論越覺得方向有一些偏:大家對基礎設施需求的重點并不是在 rpc 調用架構,而更多在于:大量的小服務,開發業務邏輯的便捷性,更新基礎包的便捷性,單節點的運作狀态,資料收集,監控報警的便捷性等等。于是,在未來,會把接下來服務化工作的重點定義成:微服務化,具體來說,就是開發并維護一個滿足雪球自己業務需要的微服務容器。
免責聲明:作者并未反對xxx,xxx,xx。 隻是舉例說明再好的技術也要和團隊的知識體系和能力比對,同時要考慮究竟要解決什麼,需要什麼。
随着業務的發展,對于可用性的訴求也越來越高。比如你曾經是單機房部署、然後演變成同城異機房、最後又發展到異地機房。因為有異地機房,對于rpc的路由複雜度就增加了,我得知道是路由到那個機房的;同樣的,在開發環境、線下測試環境要模拟幾種部署的情況亦是有複雜度的。
再舉一個例子,有一個系統作支付鍊路的規則決策,起初可能就4萬行代碼;後來增加到8萬,現在又增加到10萬。代碼行增加了,該應用的職責增加了,也可能調用邏輯的運算複雜度。那麼如何保持對外api的tps不降低,rt不降低?
每次release不僅要完成功能用例的建構,亦要完成性能的測試。
意識是很重要的,比如值班oncall,随時待命。如果沒有被叫醒或者打不通電話就比較糟糕了。我們一般還有備份ab角機制。著名的朱赟博士有一篇文章就談了oncall的故事,叫[工程師oncall那點事]。
ps: 擁有衆多title的阿朱公衆号推薦, 嘀嗒嘀嗒 不是滴答滴答呃!
其實經過分析,80%的線上問題不是那麼難的。其本質是未遵循規定動作。比如修改了代碼未充分驗證。就修改了幾行,咋看都不會出錯。從内建品質的角度,我們一般有n條品質防線,一個問題被遺留到線上,往往有3個以上的措施都失守了。
大家知道,航空飛行是馬虎不得的。最近看到一篇報紙談飛行作風問題,我覺得對軟體研發品質仍有類比作用和啟發效果。
文章提到幾點經驗,頗有借鑒意義:
不忘初心:嚴格遵守sop(标準操作程式)
減少僥幸心理、增強風險意識
狠抓作風養成
嚴厲處罰無後果違章,既要結果,又要過程正确
文章深刻的提到,随着飛行經曆的增加,許多程式顯得啰嗦、許多檢查顯得多餘、以至于越飛程式越少、多做越簡化且不把規章當回事...
總之,品質問題就是達摩克利斯之劍高懸于空中。你不注意它,它就會在某一天降落。上述提到的複雜度問題、業務高速發展、技術債、對于工具的掌握能力等都可能為品質埋下了隐患。能不能破,如何破?
在前文中,我們從黑天鵝事件談到了蝴蝶效應、墨菲定律。一言以蔽之為,要把軟體研發中的品質搞好并非易事。品質是一個綜合的大命題,涉及到業務準确性、穩定性、可用性等方面。比如xx大促,某廠商幾小時無法交易;收藏夾商品丢失;使用者享受的優惠不符合預期,營銷規則複雜難以解釋等。
我們讨論了導緻品質問題難以掌握把控的原因:
業務高速發展帶來的變化
技術債問題
人、流程、文檔的博弈
采用不能cover的工具和架構
複雜的問題域
品質意識
為了破解這個複雜的問題,我們打算從以下幾個方面來思考這個問題。如同沒有銀彈一樣,也沒有萬能解藥。
靈活宣言如是說:
我們一直在實踐中探尋更好的軟體開發方法,身體力行的同時也幫助他人。由此我們建立了如下價值觀: 個體和互動 高于 流程和工具 工作的軟體 高于 詳盡的文檔 客戶合作 高于 合同談判 響應變化 高于 遵循計劃 也就是說,盡管右項有其價值,我們更重視左項的價值。
靈活模組化(agile modeling,am)的價值觀包括了xp(extreme programming:極限程式設計)的四個價值觀:溝通、簡單、回報、勇氣,此外,還擴充了第五個價值觀:謙遜。至少我們明白了快速回報有什麼好處,做錯了做偏了,可以馬上校正。如果瀑布模型式的開發回報必然是慢的。此前有一位做醫療單機軟體的朋友說,他們半年才釋出一次,可見這裡面提升的空間有多大。
申導提及的第二層組織,我了解有組織層的支援,比如pmo,項目團隊是否按scrum等;
第三層操作方法則是持續內建、單元測試、tdd這些具體的實踐。
總結一下:基于靈活的思想可以快速release,快速糾正和調整,滿足使用者的需求。
我了解系統化的思想就是體系、舉一反三、通過類比、抽象、階層化等手段挖掘本質。往往出了一個bug,故障複盤會開了,action有了,但下次又出錯了。究其本質,我覺得兩點。一是複盤的内容focus在單個事件,對于更體系的影響洞察不夠。二是未持續學習,對于問題是不情願的承擔。
前一段在一個讨論中,關于技術債務有4個有趣的觀點。
由于技術人員水準不行,意識不夠,是以越寫越爛。
developer都有一顆積極向上的紅心,但是他們不知道如何做才是好的。
上行下效,沒有靠譜的leader,架構師,大家都是懶惰的,不想去還債。
華為的同學表示,永遠的業務壓力;其實所有公司都一樣滴,趕着傳遞,那有時間去清償。
關于債務清償,總結了務實接地氣的幾個思路。
建構品質保障體系,讓我有勇氣動手動刀。比如接口測試、單元測試,通過ci持續回報,這樣我有50%的勇氣來做重構。
對于遺留系統,沒出問題的地方,你不要動它。出問題的,修複并補充測試代碼。如果更進一步,對于高危的功能和子產品可以做定向增強。
招募優秀的工程師,好的作風和習慣可以影響整個團隊。
抓住痛得不行的時候大作文章。平常說持續內建,可能團隊成員沒有感受到好處,出bug了,再回顧看看;copy-paste代碼總有一天不能滿足更複雜的需求,這樣從心理認同上,大家覺得必須重寫了。抓住這樣的機會,建構品質高一些的代碼和品質防線。改變一個人是非常難的,抓住這樣的機會很重要!
前文談技術債務,談回報都提及到了持續內建。那麼整體來講,内建品質就是更短建構,更快回報。
同時,除了正确的做事,還要思辨【做正确的事】。通過atdd/引流對比/串講反串講/評審/錄制回放驗證/接口回歸測試 等手段可以保證需求到設計、實作是否發生了變形。
内建品質=正确的做事*做正确的事
就技術選型而言,不迷信、不唯新、不唯上,建議盡早做小範圍的可行性論證。比如好久前流行的osgi,上這個東西,獲得了什麼,風險是什麼。引入一個新的技術棧就帶來一層複雜度,而複雜度有一條天性。複雜度隻能增加,不能減少,就像宇宙中的熵一樣。就如同一個接口一旦開放使用,就很難把這個接口停用。一旦以一個高耦合的方式引入到一種技術,要調整或者去除,帶來的成本是幾何級數一樣的。
總結,對于技術選型請看看成熟度、團隊人員的掌握程度、收益和風險評估,一旦陷入泥潭則無異于夢魇之旅。要活得好好的,可以采取可行性論證,基于風險的架構設計。
在文章中提到過,問題域的複雜度會導緻出bug的風險增加。
那麼要如何破呢?對于第二個例子而言,除了建構功能回歸測試環境,還需要有性能回歸測試環境,以保障每次release的産品性能是沒有降低的。對于多機房這個case而言,技術架構要關注可測試性、易用性。不僅僅考慮複雜度帶來諸多依賴産品的修改成本,還要考慮易用性,如何測試,甚至是如何友善的,低侵入的測試。因為一旦從單機房到多機房了,問題的外延就翻倍了。
比如一個定時任務會不會同時在2個機房運作導緻資料重複;機房切換時是否有緩存切換的問題導緻業務不可用;資料複制延遲帶來的不一緻性對業務的影響;對于就得有資料複制的監控、緩存的監控、資料在全局的唯一性管理政策,業務相容性方案等等。
一個公司的文化由創始人決定,而一個團隊的品質追求往往由其leader決定。leader不自覺的保護主義,報喜不報憂等做法會讓這個問題變得嚴重。見微知著,一葉而知秋。
如果有足夠的意識,依靠體力至少也能拿過80分。團隊成員都是看leader。leader是看重品質,大家就看重;leader是欺瞞,他們就欺瞞,leader熟視無睹,這個團隊就壞透了。:)
perl設計者在著作programming perl中提到:優秀的程式員具有三大美德: 懶惰、急躁和傲慢 ( laziness, impatience and hubris) 為了減少總能量支出而不遺餘力地努力的素質,就是懶惰. 忍受不了程式執行的低效就是急躁. 容不得對錯誤不管不顧就是傲慢.
要發揚程式員的美德,比如對于規則系統如果不能建構上遊上千種場景,就可能思考新的解法;有人說做業務系統沒啥追求和挑戰,但是又有滿目蒼夷的bug,小事不想做,大事做不來。其實【小事不小】!
分享者簡介:
<b>于君澤,</b>花名右軍,螞蟻金服成都研發中心技術團隊建立者之一,先後負責或參與過轉賬業務、賬單類業務、社群支付、開發平台、支付平台、資金核算平台、類營銷類支付工具的建設;之前有數年電信業務研發經驗,設計bss | oss | 針對性營銷等平台。
本文轉載自微信公衆号 中生代技術 freshmantechnology