當前位置:
首頁 > 科技 > DDD不夠好用,你需要學習如何進行彈性軟體系統設計

DDD不夠好用,你需要學習如何進行彈性軟體系統設計

作者 | Uwe Friedrichsen

譯者 | 無明

1

關鍵要點

當今的分散式系統環境必須採用彈性軟體設計。

關鍵挑戰不在於編碼,而在於「外圍」。

做好分散式其實很難,大多數人都嚴重低估了它的難度。

恰當的功能設計是構建健壯的分散式系統的關鍵,但人們卻知之甚少。

在公司中引入彈性軟體設計的主要挑戰是如何建立意識和可持續性。

寫這篇文章的起因是我在 2018 年 GOTO 柏林大會上所做的演講,我在演講中分享了在進行彈性軟體設計時會面臨哪些挑戰。我將簡要地介紹彈性軟體設計的「why」和「what」,中間部分是我近年來經常遇到的挑戰,最後,我添加了一些關於如何在組織中實現彈性軟體設計的最佳實踐。閱讀完本文之後,希望你能夠更好地了解彈性軟體設計所面臨的挑戰,並知道如何解決這些問題。

2

什麼是彈性軟體設計,為什麼它很重要?

彈性軟體設計(Resilient Software Design,簡稱 RSD)是一個無法用一兩句話解釋清楚的概念——有時候很難向習慣於「電梯遊說」(在很短時間內說清楚一個問題)的人解釋這個概念。儘管如此,我仍然試著儘可能簡短地解釋我所知道的 RSD,一個是「why」,一個是「what」。

讓我先從「why」開始。通常,我們試圖通過系統來實現某種商業價值:賺錢或者讓客戶滿意。顯然,只有當系統在生產環境中看用時,我們才有可能實現這一價值。這一直以來都是這樣的。

但是,現在有所不同的是,幾乎每個系統都是分散式的。系統之間相互通信,並且系統本身也可能被分成幾個部分,而這些部分進一步被拆分,並以此類推。微服務、移動計算和物聯網的發展使得協作系統之間的連接變得更加複雜,將這些系統的開發提升到新的水平。

系統及其部件之間相互交互所需的遠程通信會引入一些故障模式,這些故障模式只存在於跨進程邊界的情況,不會在進程內部出現。如果我們忽略它們的存在,這些故障模式,例如非響應性、等待時間、不完整或無序消息將在應用程序級別上引起所有類型的非預期故障。換句話說,如果你需要一個健壯高可用的系統,就不應該忽略分散式所帶來的影響。

這讓我想到了 RSD 的「what」:我傾向於將彈性軟體設計定義為「在理想情況下,如果發生意外故障(由分散式的非確定性行為造成的),用戶根本不會注意到,或者用戶至少可以繼續使用應用程序已定義範圍的部分功能(即服務的優雅降級)」。

請注意定義中的「已定義」一詞。這意味著,計劃在發生意外情況時該做些什麼,這個與你通常在不採用彈性軟體設計時遇到的意外系統行為有很大不同。

3

彈性軟體設計的任務

我的旅程通常從在代碼級別設計和實現彈性開始。但隨著時間的推移,我意識到,在代碼層面實現彈性是最容易的部分,實際的挑戰其實是在其他方面。

雖然在事後看來,這並不足為奇(大型項目的問題從來都不是在編碼層面),但我不得不承認,我剛開始還是感到有些奇怪。你只有長期接觸這個問題,才有可能清楚地了解這些挑戰。因此,我在「彈性軟體設計的 7 個任務」演講中分享了我的一些經驗,其中每個「任務」代表了我遇到過的一個挑戰。以下是從我的演講中挑選出來的挑戰。

了解 RSD 的「業務案例」。作為一名軟體工程師,如果你嘗試開發新的東西,通常會被問及相關的業務案例是什麼。雖然這是一個無可厚非的問題,但如果被應用在 RSD 上,就會導致方向跑偏,因為 RSD 不是為了賺錢,而是為了不損失錢。儘管如此,定義彈性預算仍然是有必要的,你可以看一下如果系統變得不可用會有哪些即時的損失,以及由於累積效應(例如感到厭煩的客戶會增加你的流失率)導致的更頻繁的不可用性造成的長期損失。通過這種方式,我們可以將 RSD 嵌入到一個理智的經濟框架中。

了解分散式系統的非確定性行為及其後果。問題是分散式系統不僅難以駕馭,而且難以理解。遠程通信為系統行為添加了概率性因素。不幸的是,我們的大腦無法輕易地處理概率性行為。此外,幾乎所有的 IT 教育或培訓都是以進程內交互為前提,在這些環境中,我們面臨的是確定性的行為,導致大多數人對分散式不甚了解。我也不知道有哪些簡單的分散式解決方案(我其實認為不存在這樣的解決方案)。也許在我們的 IT 教育中添加更多與分散式系統相關的教材可能會有所幫助。數以百計的計算機科學論文已經清楚地告訴我們,進程內的東西一旦變成分散式的,即使再簡單也會變得很難,甚至是不可能的。但並不是所有人會去閱讀這些資料。然後有些人說,我們應該將遠程通信問題留給基礎設施去解決,讓應用工程師不受干擾。過去的很多分散式框架都遵循了這一理念——事實證明,這隻適用於非常小的系統,總體來說並不是可行的解決方案……

避免「100%可用」的陷阱。這與上一個挑戰有關,但影響範圍要小得多。由於典型的確定性思維,人們(無論是 IT 人員還是非 IT 人員)都傾向於假設他們連接的所有系統的可用性都可以達到 100%。你可以在需求、設計和代碼方面看到這種隱含的假設。並不是人們有不良意圖,也不是他們粗心大意,只是他們忘記了一些東西。在分散式系統中,問題不在於系統是否會發生故障,而是會在什麼時候發生故障。可用性始終會小於 1(或者說小於 100%)。因此,我們需要小心 100%可用性的陷阱,並檢查我們的要求、設計和代碼,避免掉入這個陷阱。

建立 OpsDev 反饋閉環。在很多公司,我們仍然看到開發和運維之間存在巨大的屏障,甚至已經觸及 C 級人員。造成這種局面有一定的原因,同時也給 RSD 帶來了一個大問題。彈性需要在應用程序層面實現,也即在開發中。但是,你只能通過運維來衡量實際的彈性效果。此外,在運維中,你會檢測到需要由開發來處理的應用程序缺陷。但是如果你在 Dev 和 Ops 之間有一堵大牆,就會破壞這個重要的反饋閉環。開發人員盲目地實施他們的彈性舉措,而運維發現的問題卻得不到修復。因此,無論你是使用 DevOps 或 SRE 等既定方法,還是使用自定義的方法,都需要建立反饋閉環。

正確的功能設計。如果分散式功能在設計方式上出了問題,那麼即使再好的彈性措施也無法幫你構建出健壯的系統。問題出在這裡:假設你在服務之間創建了緊密的功能依賴(「強耦合」)。如果被依賴的服務變得不可用,那麼所有依賴這個服務的服務也將變得不可用,因為這些服務需要藉助被依賴服務的業務邏輯來完成自己的任務。遺憾的是,我們學到的幾乎所有與設計系統相關的技術,即如何劃分功能,都會導致強耦合,因為它們側重於進程內設計,而進程內設計的可用性不受這種耦合性的影響。因此,我們需要重新學習分散式系統的功能設計,重點是降低功能耦合——這與進程內的低耦合不同。我將在下一節深入探討這個主題。

了解應該使用哪些模式以及如何合理地組合它們。在你學習完新模式後,總想著去用它們。問題是,使用彈性模式是要付出代價的。它們通常會增加實現和運維成本。更重要的是,它們增加了解決方案的複雜性,而複雜性是健壯性的敵人。解決方案越複雜,就越難以理解,意外故障給健壯性帶來不利影響的可能性也就越大。因此,關鍵在於不要使用太多的模式,而是要在彈性和複雜性之間找到一個平衡點。

不要因為技術的更新換代每幾年就淘汰積累起來的社區知識。這不是 RSD 特有的,它適用於整個 IT 行業。作為一個社區,我們傾向於每隔幾年就將集體智慧淘汰,然後從頭開始。我們傾向於忽視我們已經知道的東西,然後尋找下一個被炒作起來的銀彈,以此來解決我們的問題,而不是像其他工程學科一樣建立和維護經過驗證的知識體系。同樣,這不僅限於 RSD,但我們現在可以觀察它們,因為大多數 RSD 概念不是新東西,其中一些已經存在了幾十年了。說實話,我也不知道該如何解決這個問題。因此,我能夠想到的最好的事情是提醒人們要注意這個問題,並希望如果有足夠多的人意識到這一點,最終將成長為一個真正的工程學科。

當然,還有其他更多的挑戰,但從我的角度來看,這些是最應該引起我們注意的。

4

基於彈性軟體設計創建更健壯的應用程序

根據我的經驗,理解分散式系統以及如何進行好的功能設計是創建健壯應用程序的最大障礙。因此,讓我們更深入地探討這兩個主題。

理解分散式故障模式的含義是非常困難的。單進程系統中一些很簡單的事情到了分散式系統中有會變得非常困難,甚至是不可能的,而且基礎設施無法為應用程序隱藏掉所有這些影響。另一方面,大多數(如果不是全部)大學和大學後的 IT 教育都是基於本地計算。此外,試圖掌握分布的非確定性影響對我們的大腦來說並不是一件容易的事。

根據我的經驗,IT 領域之外的大多數人幾乎不可能真正理解分散式,因為他們把計算機理解為「可以完成人類布置的任務的機器」,教會他們有關分散式計算的概念和所面臨的挑戰需要很長時間——我們通常沒有那麼多時間。

但其實對大多數開發人員來說也是非常困難的。開發人員在面對不可用的分散式系統時,他們通常也會不知所措。由於他們的 IT 教育完全忽略了分散式系統,他們甚至會避免處理與分散式有關的問題。這導致在設計和實現系統時忽略了分散式的影響,而這反過來讓系統變得既脆弱又慢。

我之前說過,我不知道這個問題有什麼簡單的解決方案,我其實認為並不存在所謂的簡單的解決方案。我能做的就是建議在我們的 IT 教育(大學期間和大學之後)中增加更多有關分散式系統設計的課程,因為我們的系統環境變得越來越分散,我們需要更好地了解設計和編碼的實際效果。

另一個巨大的阻礙是功能設計。如果功能傳播的方式是錯誤的,最終只會得到一個脆弱的系統。舉個簡單的例子:服務 A 接收外部請求,為響應該請求,它需要來自服務 B 的一些信息,這也就是所謂的在服務之間傳播功能。如果服務 B 被關閉,服務 A 就無法響應外部請求。

這就是所謂的級聯故障。彈性軟體設計的主要任務之一是避免級聯故障。通常,你可以使用簡單的超時檢測機制或斷路器來檢測服務 B 是否已關閉,然後回退使用服務 A 的備份計劃。但由於功能在服務之間傳播,不可能有備份計劃,即斷路器只會讓級聯故障可見,不會提供任何繞過它的方法。

這只是其中的一個例子,類似的情況還有很多。如果你將通常的「設計最佳實踐」應用於分散式系統,通常會出現這類問題。在給定的示例中,服務 B 是「可重用服務」。可重用性是單進程的理想屬性,但它也會帶來非常強的耦合性,在跨進程的環境中表現出非預期的特性。

在過去,如果我們的功能設計出現錯誤,到最後系統會變得難以維護——這已經夠糟糕的了。但是,在分散式系統中,糟糕的功能設計在運行時就會體現出脆弱性、不可靠和性能問題,這個更糟糕。問題是大多數有關如何做出「正確設計」的建議只適用於進程內設計。如果將這些建議應用在分散式系統上,大多數都無法正常工作。

我從過去的經歷中學到的是,我們需要重新學習如何設計系統,即如何在分散式環境中傳播功能。

然後,大多數人會提到領域驅動設計(或簡稱「DDD」),但根據我的經驗,這也不是靈丹妙藥。不要誤會我的意思,實際上,DDD 為更好的設計提供了很多非常好的建議。但是當談到分散式系統的設計時,單靠 DDD 是不夠的,它還缺少了一些額外的建議。好的方面是:據我所知,人們正在嘗試擴展 DDD 的原始思想,加入分散式系統因素。因此,我對這一領域的未來發展非常期待。

5

發展構建彈性應用程序所需的技能

如果你想將 RSD 引入到你自己的公司,你可能會要求制定一個可以達到最佳效果的計劃。根據我的經驗,並不存在完美的計劃。我的建議是實現通用的「意識—能力—可持續性」模式。

首先,了解為什麼需要 RSD 以及如何將其傳達給不參與軟體開發的人員。這涉及理解和接受分散式系統的不可用性(包括避免「100%可用性」陷阱)和彈性軟體設計的業務案例。此外,你還需要學習如何在不使用深奧的 IT 知識的情況下將其傳達給人們。即使你知道需要通過 RSD 來構建健壯的系統,但卻不能與你的經理或你的企業主討論它,並幫助他們更好地理解這個主題,然後做出正確的決策,那麼這一切將無濟於事。

獲得知識可能是最容易的部分。同時,還可以找到一些有關這個主題的資源和培訓——只需要注意與分散式系統或微服務相關的會議的研討會部分,或者從閱讀文末參考部分提供的兩本書開始。當然,你需要在工作中應用它們。再強調一下,做出正確的功能設計是一項艱巨的任務,但彈性模式本身相對容易學習和應用。

要建立可持續性,首先需要一個有效的 OpsDev 反饋循環。如果沒有這種循環,任何彈性倡議都將註定失敗,因為你的彈性度量在實踐中缺少了反饋。

此外,你可能希望建立混沌工程計劃。混沌工程不僅有助於揭示系統缺陷,它還通過持續、可控的學習(了解系統的健壯性並進一步提高系統健壯性)幫你實現可持續的彈性。

「混沌工程」這個詞有點容易被誤解,它不是為了製造混亂,而是為了避免混亂。在混沌工程中,你可以設計受控的實驗,以便更好地了解系統的實際健壯性以及需要做出哪些額外的彈性措施。混沌工程師總是小心翼翼地控制實驗潛在的影響範圍,並在執行實驗之前與所有受影響的人進行溝通。

它從一個假設開始,例如「如果我們切斷與此伺服器的連接,將發生自動故障轉移,最終用戶不會察覺到任何差異」。然後,與受影響的開發人員和運營人員討論該假設。假設是有效的嗎?我們怎麼測試它呢?我們如何衡量正確性?如果我們錯了,怎樣才能以安全的方式停止實驗?

在討論和定義好所有內容之後,就可以進行實驗。根據試驗結果,可能需要定義(RSD)度量。除了幫你找出應用程序中之前未被發現的問題之外,混沌工程也會顯著提高你對系統的信心——這是一種很好的感覺。

6

總結

總的來說,在今天的分散式系統環境中,RSD 是一個必選項。雖然學習如何設計和實現彈性模式相對容易,但 RSD 所面臨的實際挑戰通常不在於編碼方面。

分散式系統本身的複雜性和分散式系統的功能設計讓實現可持續的彈性變得更加困難。同時,Ops 和 Dev 之間缺少反饋循環、過於複雜的彈性設計,或者缺乏對 RSD 業務案例的理解,等等,通常都會帶來阻礙。不過,了解挑戰是成功掌握它們的第一步……

英文原文

https://www.infoq.com/articles/towards-resilient-software-design

7

活動推薦


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 InfoQ 的精彩文章:

別動我的代碼!聊聊那些代碼保護的藝術
馬雲和塞爾弗里奇那不可不說的神秘關係

TAG:InfoQ |