每日最新頭條.有趣資訊

一行代碼引發的恐懼

作者 | 大飛

本文經授權轉自大飛碼字

我工作的前5年,都是從事基礎系統研發相關的工作。做過後台的接入層,後台的存儲系統,RPC框架。說來不怕你笑話,那個時期裡面,我對代碼一直有一種恐懼感。這種恐懼是怎麽來的呢?且讓我慢慢說來。

我們所構建的基礎系統,都是使用在億級甚至十億級用戶產品的業務系統之上的。從客戶端(前端)到後台業務邏輯層,再到基礎架構層,所寫的代碼是跑在整個調用鏈路的最後端的。

你可以認為,幾乎每個用戶的每個請求都會跑到我們寫得那部分的代碼。

這個對系統帶來的影響是: 一,代碼出問題後,影響的用戶範圍會很大;二,在這億級甚至十億級用戶量的情況下,每天所帶來的請求可能是千億級,兆級的,在如此龐大請求量的情況下,幾乎各種奇葩的異常,你都會遇到,代碼要極其的健壯,一個小異常沒處理好就會帶來大麻煩。

這讓我想起來,我們故障時候的情形。

幾年前,我們每做完一次版本變更,晚上基本都會睡不好,擔心變更的代碼有問題。對手機的報警短信特別敏感,一有風吹草動,立馬就會打開電腦vpn看看,即使是在深夜凌晨的時候。

我自己有個習慣,每次變更完,都要間隔幾個小時去看看監控曲線和日誌,看看有沒有異常的苗頭,一旦發現有不對勁地方,就會立即著手排查,直到確保沒有問題為止。

不過即使如此,還是不可避免的會出現問題。

半夜兩三點的時候,你的手機突然響起,報警語音機器人跟你說,你有一個重要的監控曲線出現異常,請查看。然後你的血壓立馬升高,心跳加速,你從床上,一躍而起,打開電腦,連上公司的VPN,立馬著手排查起來。

幾分鐘後,QA(質量工程師)電話過來,告知你,這個故障目前已經上報到部門故障系統,目前影響的用戶有XXX的數量,請你加快處理的速度,然後你的心跳再次加速。

半小時後,終於有了眉目,這時,你的leader, 電話過來,詢問你是怎麽回事,大概還需要多長的時間,才能處理完畢。待你語焉不詳地回復完你的leader, 你又開始埋頭,一行行的排查故障。

一個小時後,你終於,將問題定位出來,執行了故障處理方案,例如回滾新的代碼,或者屏蔽某些機器等。你才終於有了喘息的時間。

(ps: 這裡正確的流程是,出問題後,立馬回滾代碼,但存儲系統因為數據的關係,在沒有確定原因前不太敢回滾,怕對數據有影響)

你趕緊爬上床去,睡上2-3個小時,因為第二天還要早起,趕到公司,去處理故障的後遺症,數據損壞和數據錯亂。

那個時期,我們寫代碼都是特別小心的,變更,更是極度的謹慎。所以使得自己對代碼變更有了一種焦慮和恐懼的心理。至少在那時候,寫代碼不是一件輕鬆的事情。

這個事情,我現在回過頭來看。你可以認為有一部分是人的原因,但仔細的想想,寫代碼不出bug ,幾乎也是極難做到,所以這裡在研發流程上,其實也是有缺失的。

前期因為業務發展太快,團隊的整體人力跟不上,所以,一開始很多流程,都是很原始的,那時候,是想做但客觀條件不允許。

後來,業務穩定了,流程就規範了不少。比如引入了coverity的代碼檢查,也推行過測試用例覆蓋,持續集成等。

但最終,並不是所有的流程都延續了下去。比如,代碼測試用例覆蓋,有的團隊到後面就放棄了,需求變化太快,測試用例成本太高。

coverity倒是自動化程度高,沒啥人力投入,執行了下來。

但我相信不是所有的公司,所有的團隊,都會有這種規範的流程。一個是研發流程成熟度建設的問題,但除此,還有成本,業務迭代速度。在互聯網,產品高速迭代的時候,產品都還沒有存活下來,成熟流程就更不太可能有了。

綜合來看,一種規範,但相對較重的研發流程的建立,應該也是根據具體情況而定的。需要考慮產品的形態,產品迭代的速度,團隊的人力預算成本,產品的生命周期等等。當然,無論怎麽說,反正這不是個人可以決定的事情,如果你所在的團隊有完善的研發流程,那是最好的事情,但如果沒有那麽完善,自己又能夠做些什麽呢?

我的經驗來看,以下的一些措施,對於個人而言也有不錯的效果。

測試驅動的開發(TDD)

有段時間,因為業務高速發展,對性能的要求不斷提高,存儲模型也跟隨著不斷迭代改進,所以那段時間的代碼修改是比較多的,那個時間的焦慮感也特別重。

我記得是在 《重構:改善既有代碼的設計》中了解到TDD的。

簡單來說, 就是先構建測試用例,再開始寫你的功能代碼。在設計測試用例之前,你需要先定義好模塊對外的接口,包括接口的種類,參數,返回值等。

然後,你針對定義好的接口,編寫測試用例。這過程中,你可能會發現接口設計不合理的地方,也需要隨著修改。待你測試用例寫完,基本你的接口也被修改的比較好了,所以TDD還能改善你的接口設計。

後續再為每個接口實現特定的代碼邏輯。

我當時將這種方法運用到了一個磁盤存儲引擎中,發現相當不錯。我特地花了一周左右的時間寫測試用例。後面,每天實現部分的功能後,都立馬跑測試用例,每次跑完通過,你的心裡都有穩的一B的感覺。有種媽媽再也不擔心我寫的代碼有bug,被被老闆叼,導致扣工資了。

因為有了完善的測試用例,而且隨著你測試用例不斷的增加和覆蓋,你的信心會越來越足,焦慮自然減少了很多。

不過這種方式,比較適合底層的系統和核心穩定的系統。對於需求多變的系統,構建測試用例的人力付出太大,而且需求一變,已有的測試用例可能失效,導致投入產出比不夠高。

灰度發布

簡單來說,就是一個特性要上線的時候,不是一下就開放給所有的用戶使用。有點像產品上的內測,只不過是用在技術上。

比如我新增加了一個產品需求,例如就微信裡面的 “看一看”入口,不是一開始就對所有用戶開放的。

首先會上線一個新的客戶端版本,代碼邏輯已經預埋,但設計了一個開關,對所有用戶都是關閉的。前期,可能會找個千分之一,甚至萬分之一的用戶(隨機或者特定的用戶群體),讓他們使用。

這過程中,收集各種log ,監控,用戶的反饋。來確認和fix 系統存在的各種問題。一般經過兩三周後,如果沒有大問題,就會進一步的放開使用的用戶。比如變成百分之一,十分之一,一直迭代,直至覆蓋全部用戶。

灰度這個思想,在互聯網是特別常用的。客戶端,前端,後台都可以使用。比如後台,上線一個新修改後,也不是一下就開放給所有用戶。而是按照某種規則,例如以QQ用戶為例,可能是這種規則

計算Hash值(QQ號) % 1000

放量的最小力度就是千分之一,被灰度到的用戶,看到新功能,沒灰度到的用戶不受影響。

這招用在新功能,用在系統優化,代碼重構上都很不錯。

付出的額外成本不大,有的公司有自研的灰度系統,那最好。沒有的話,在重大且沒有把握的功能上,自己加上幾行灰度控制代碼也不難。

監控和log

監控和log不是什麽新鮮的東西。

工作第一年,我們的技術總監在一次會議上跟我們說:你寫完的代碼是死的,只有在線上跑的代碼是活的。監控和log(特別是監控),就像是你代碼的體征信息,隨時反應著你代碼在實際環境中的運行情況,要高度的重視。

這段話,在後面,我深有感觸。通過完善設計的監控和log,預先發現了很多的問題,也避免了很多,或代碼bug,或系統設計缺陷導致重大故障。

後面,監控和log的設計,也成了我們方案設計的一部分。一般都會在方案最後,加上必須的監控的點和log點,例如請求數,成功數,失敗數,各種異常數,極端邏輯執行次數等等。

你應該要意識到監控和log的重要性,而且應該要花時間特別地設計。

經過良好設計的監控和log,能發揮的價值,是那種憑感覺隨便加的監控log不可比擬的。

雙寫,雙讀驗證

這招,新業務代碼用的不多。更多用於基礎系統或者核心系統的優化和重構上面。而且有前置條件,需要一個操作可以重複執行(例如隻讀操作和冪等的數據操作)。

簡單來說,就是將新舊代碼,劃分為兩個流程(兩個接口),上線到實際環境,然後在同個模塊裡面調用。

一個請求進來後,兩個流程分別執行一次,逐字節做對比(例如 memcmp)新舊流程的結果。新流程的結果隻用於對比,返回得依舊是舊流程的結果,所以不影響線上業務。

如果對比失敗,就可能存在異常,要查找並解決,在實際環境跑了幾天后,都沒問題,就可以採用灰度的方式,進一步放量。不過,一般業務不常使用,在基礎系統上使用比較多,這裡就不展開了。

其他

另外,對於客戶端,還有熱補丁機制,客戶端log收集系統等。不過這種需要的開發量比較大,一個人不一定可以搞定,可能需要有個小團隊來完成。

最後

軟體工程是個龐大的話題,我也沒能力論述這麽大的話題。這裡給大家講了個以前的故事,並且分享了我常用的一些低成本,但可以提高線上代碼質量的方法,給大家參考參考。大家有好的做法,也歡迎在留言裡分享出來。

【End】

熱 文推 薦

獲得更多的PTT最新消息
按讚加入粉絲團