系統設計思維的討論

我們經常說自己熟悉了spring,能夠搭建起一個項目基本框架,并且在此之上進行開發,用戶or客戶提出需求碰到不會的百度找找就可以實現。干個四五年下一份工作就去面試架構師了,運氣好一些可能在中小公司真的找到一份架構師、技術負責人的職位,但很難在大廠得到一份滿意的offer(可能大廠的碼農崗位都進不去)。我總結原因有2點:

  1. 大廠雇傭了一幫中小廠中非常優秀的架構師和技術專家做碼農
  2. 你覺得你熟練掌握了各種框架,甚至讀了一些源碼,但你不是架構師,而是框架師(這是我起的名字)

一個合格的架構師或技術專家我認為應當是這樣歷練出來的(或者說所具備的軟實力):

  1. 經歷過無數的事故和錯誤謙虛的從中總結經驗并吸收,從事故中爬出來
  2. 具有清晰的系統設計思維和獨立的思考能力
  3. 經歷過復雜的項目以及真正的大用戶量系統的設計和開發

以上3點可能在大廠才能歷練的到,首先系統只有運行且有用戶使用才能產生事故,其次業務復雜性、高并發或大數據量帶來的技術復雜性幾乎只有大廠才能存在。
難道在中小廠干到35歲真的要退休了么?大環境不好裁員真的會裁到自己頭上么?很多人想去大廠,沒有大廠hc所要求的經驗和技術能力,似乎在中小廠的程序員去大廠就是個悖論。
但我覺得以上不是問題,我們要想清楚問題的本質和社會運轉的基礎機制:

  1. 隨著疫情出現,社會經濟大蕭條,資本對互聯網產業投入逐漸降低,企業逐漸進入選擇更優秀的人、經驗豐富的人的招人模式,雖然20多歲的小伙子精力旺盛,能加班,但中國的計算機和互聯網發展時間與學校制度,注定80和90后這一代人到35+歲才能有足夠的經驗和技術實力。而剛畢業的學生在中國計算機教學方式下很難有所成就。目前來看,畢業即失業,工作2-3年的找不到工作,也印證了這點。所以我認為,35歲就失業這個事一直是被不懂行或一些既得利益者(培訓機構、自媒體運營號)大肆渲染導致的。
  2. 在10幾年前,中國互聯網上升期,java程序員最火熱的時候,一個培訓班40人中只有少于10個人畢業后能留在這個行業,而剩下30人則認為這個行業不行。而在現在所謂大環境不行的情況下,我身邊被裁員的同事陸陸續續進入了百度、字節等一線大廠。
  3. 大廠本質上是個圍墻,進去的想出來,出不來,外面的想進來,進不來。我們姑且不討論想出來的這些人,我覺得進不來的人問題在于是否擁有一個大廠所需的思維,即:系統設計思維和獨立思考能力。

對于一個中小廠的程序員,項目很可能處于從未上過線或上線沒幾個人用的尷尬局面。業務本身不復雜,技術要求更不高。對于個人來說毫無成長和升值空間,更無法得到上面說的3點歷練從而榮升架構師級別的大佬。這是無解的么?我覺得不是。
在這里,我總結出一些任何系統普適的設計思考原則,希望你能嘗試用在你的日常設計和開發中(無論是多么小的系統)。這些思考原則是大廠每個人所需具備的,這種思考方式能夠讓你獨當一面,能夠讓你實踐出更可靠、高效的系統。這些思考原則不是特指掌握具體某個技術(譬如:熟讀Linux內核源碼),實際的技術點掌握再精通,只是一個點,無法形成面或建立立體的知識體系,也就無法應用到大型工程中。沒有這樣的思維,可能在你面試的時候,面試官會給你一個思維混亂的評價。
無論是在工作中還是面試中,我都推薦使用下面的思路設計或討論系統:

系統設計要遵循3個原則:

  1. 合適原則:在系統設計的時候需要考慮采用的技術方案是否符合當下業務場景和團隊能力。不要過分追求所謂的大廠就是這么用的,能抗多少qps,多么靈活可擴展,盲目追求不切實際的指標和技術。
  2. 演進原則:系統的架構是演進過來的,可以預測未來1年的業務發展趨勢,選擇合適的架構模式,單體(甚至是單機)并不是干不了活何必上來就追求復雜的分布式環境?大廠也是由曾經的幾個人創業再到大廠的,用戶體量不是一天增長來的。上來就設計能夠支持高并發、大數據量的系統,技術實現和維護的復雜度、成本成直線上升。
  3. 簡單原則:系統架構一直圍繞著對抗復雜性和不穩定性的,多引入一個組件或模式就會增加一份復雜度。我們經常說屎山代碼不好維護,對于一個簡單的業務采用領域取代設計方法或引入mq、ES等中間件帶來的復雜度比屎山代碼還要恐怖。只有面試題會沒事讓人把if都轉成策略模式,不用線程池而使用mq。簡單原則也間接說明了系統是演進的。

如果你對面試官說你做的電商系統能夠支持百萬qps的下單,而你的公司并不是知名大廠,恐怕這次面試會失敗。面試官會覺得你過度設計了系統,引入更多的成本。

通常對于一個業務功能,我們可以考慮上線后一周的增長量、一個月的增長量、半年的增長量、一年的增長量。而對于一個完整系統的設計,可以評估1年-5年的增長量。

系統設計要考慮的4個重點:

  1. 模塊劃分:有哪些模塊,這些模塊作為拆分服務或工程內模塊(如maven模塊)的基準,比如用戶模塊、交易模塊等,劃分模塊的目的是專注在模塊內部實現具體的業務。這不代表你要為用戶啟動一個數據庫,交易也要啟一個數據庫。在邏輯上進行劃分,在物理部署上可以適當拆分,也可以合并在一起。當邏輯模塊劃分好,更有助于分配工作任務,并對每個模塊進行獨立的設計和實現。

  2. 邊界與職責:模塊協同運作才能完成整體的業務流轉,達到最終的目標。這里便出現了模塊之間的交互,模塊劃分專注與業務領域的內部,而邊界專注于與模塊之間的交互。我們經常吵著領域驅動設計,也是在劃分出業務領域(模塊)后區分出界限上下文(邊界),通過所謂的防腐層對外提供服務。為何要有防腐層?我們要可以站在自己模塊內部看外面,我調別人的接口或我提供的接口可能存在2個問題(注意,不只是接口,消息隊列通信也一樣存在這樣的問題):
    a. 故障和異常,作為服務者我們不應該對外暴露過多的自身錯誤,作為調用者我們不應該受外部影響而產生自身的錯誤。
    b. 變更,作為服務者我們不應該頻繁變更導致調用者跟著變動,作為調用者我們必須考慮接受到外部的變動后對自身產生何種影響。
    所以,通過防腐層對變更和故障進行屏蔽,確保之上應用的穩定。
    同時當我們確定邊界后就能明確了自己的職責,不要干預其他模塊的行為和決策,不要實現其他模塊的功能。在企業中工作也一樣,不要把手伸到別人的籃子里,更不要讓別人把自己的成果拿走。

  3. 面向異常做設計:從業10幾年見過無數大牛,沒有任何一個大牛能打保票說自己的系統百分之百穩定,而是制定出SLA,表明自己系統的穩定性指標和賠償方式。業內也通過幾個9(即系統可提供服務時間和故障的時間比例,如:99.99% 4個9)。系統架構師一直在與不穩定對抗,但實時證明做不到完全的可靠,這是宇宙的法則:熵增定理。我們能做到的是在出現故障后如何快速解決,人工介入或系統自適應。比如:合理的異常處理方式,合理的降級預案,流量灰度,快速回滾,設計冗余來確保掛了能繼續提供服務。異常狀態是個陷阱,很容易在設計的時候忽略掉。

  4. 核算成本:這包括了技術成本和資源成本,資源成本又包括了人員成本和投入資產的成本。所有的投入,要有足夠的產出才是值得的(ROI要高),否則都沒有意義
    技術成本:為了幾個用戶引入消息隊列、分布式數據庫顯然對于一個3、5個人的小型技術團隊是不合適的,引入一個組件或一種模式就需要有人能維護的下去,或能夠熟練掌握這些技術。一項技術是否有足夠的社區文檔支持、后續是否方便維護與監控,這都是在設計中技術選型時需要考慮的。
    人員成本:企業要為員工發工資的,一個架構師或團隊管理者實現一個系統不考慮人員配備開銷,這個團隊必定會因為錢而解散。
    資產的投入成本:引入各種中間件,就要多投入機器資源,在做設計時應該考慮是否有必要引入這筆開銷。

系統設計要達到5個目標:

  1. 可靠性:可靠性保證系統可提供服務的時間,本質上我們引入分布式和各種中間件也是為了提高系統的可靠性。確保可靠性的最好的解決方法就是足夠的冗余,比如集群部署,一個機器掛了其他機器還能提供服務。不過對于日常的編碼,我們也應該考慮可靠性,包括:處理好異常給用戶一個友好的提示、合理的重試機制、限流的自我保護、上報監控數據有問題及時發現、設計可降級方案、灰度發布、壓測和故障演練。

  2. 可擴展性:可擴展性包括了系統物理上部署的可擴展性,和系統維護的可擴展性。在物理部署的可擴展性的角度來看,我們經常把應用實現成無狀態的,即不記錄用戶請求的信息,這使得可以部署多臺服務,形成對等集群并通過負載均衡技術實現新增機器或下線機器用戶無感知。而從系統維護的角度來看,一個良好的架構設計可以確保功能的迭代不用牽一發而動全身。那如何評價一個系統架構設計是否是良好的?即上面提到的4個重點中的3點,這其中包括了如何解決耦合性——模塊劃分并確定邊界和職責,拒絕引入難以維護的技術——核算成本中的技術成本。這些都有助于提高系統的可擴展性。

  3. 可觀測性:可觀測性是指一個系統具有良好的監控,能夠自我上報各種狀態,這樣能夠讓系統具有足夠的自適應型。異常出現也能夠快速發現并自動恢復或者人工接入。一個好的可觀測性包括:全鏈路跟蹤、數據指標可視化大盤、異常檢測和通知。Linux操作系統是一個可觀測性做的非常好的系統,比如我們可以讀取虛擬文件系統 /proc的運行時刻系統狀態,syslog服務能夠記錄系統關鍵日志,全訪問的監控命令(如top、netstat等),ebpf、systemtap技術實現內核級別的觀測。Linux成功的很大程度上具有足夠靈活的可觀測接口,讓用戶能夠掌握系統的狀態。

  4. 性能:我們無時無刻都在討論性能,衡量系統的性能包括如下指標:
    a. 能承載多少QPS/TPS
    b. 響應耗時(RT),P99、P50
    c. EPS(每秒鐘異常數量),這包括了:在要求的響應耗時內產生的異常請求與超過最大可容忍的響應耗時
    d. 系統內部的資源使用情況:CPU使用率與load、磁盤IOPS、內存分配速度與使用率、網卡過包量 丟包 重傳等
    討論和設計這些指標時,需要考慮單機和集群情況下,評估出單機可承載的最大量,預計系統的峰值和均值來決策集群中需要部署多少臺機器。
    設計高性能服務手段有很多:
    a. 利用緩存和緩沖平衡慢速設備與快速設備的時間差,在一個請求中緩存到處可見:客戶端緩存、cdn緩存、nginx和jvm內部緩存、redis緩存、cpu L1-L3緩存、操作系統文件系統的buffer與cache、數據庫的buffer pool等。可以說現在互聯網系統就是緩存為王。而引入了緩存,自然引入了緩存的一致性問題。
    b. 分而治之,這包括了把流量拆分到分布式環境下進行處理,加速整體請求處理速度。大數據量的拆分(分庫分表、分布式數據庫等),本質上也是利用并行計算或降低局部處理數據量級的手段。提供冗余服務來應對流量的增長,分攤計算壓力。服務拆分出核心與非核心部署,為核心服務提供更多的計算資源。
    c. 設計上的取舍,比如分布式情況下選擇最終一致性而不選擇強一致性。
    d. 利用合理的數據結構與算法,如:b+索引、LSM索引、倒排索引等。能夠評估一項技術所使用的核心算法的時間復雜度,有助于對系統性能的整體把控。
    在出現性能問題時,我們可以依賴可觀測性中提到的監控系統觀測各種指標從而進行宏觀上的性能分析。而采用一些性能分析工具,如:jvm的內存分析工具(jmap、jstack、jstat等)、第三方的profile工具(arthas、bcc等工具),利用這些工具能夠定位性能問題所在的根源。

  5. 用戶體驗:用戶體驗包含2個方面,一方面是系統具有一個良好的用戶界面并培養用戶習慣,這是產品與UI/UE設計人員需要考慮的事情。另一方面是說,對于服務端提供服務時,要提供一個友好的接口表達方式和服務狀態,這包括了對外的rpc或http接口,對內代碼和模塊中的接口。一個接口50多個參數,增加一個新的參數就會導致調用者也要跟著修改,這就是一個很差勁的接口表達方式。而接口和服務提的供者對調用者暴露了各種自己的內部異常,會讓調用者沉浸在處理各種異常狀態中,牽引著調用者自身進入不穩定的狀態,這就是一個很差勁的服務狀態。所以一個好的接口或服務要對外屏蔽復雜的狀態,處理好內部的異常,提供精簡合理的出入參(對于參數很多的情況,要進行封裝)。

請記住,為了上面5個目標,你必定會引入復雜度:
1.為了可靠性要引入集群或其他中間件
2.為了可擴展性要引入了復雜的模式(編碼的設計模式或系統的多模塊拆分)
3.可觀測性方面,為了完善精準和可靠的監控數據要犧牲性能上報數據,構建復雜的全鏈路監控和可視化大盤
4.為了解決性能引入各種中間件、緩存,甚至借助對硬件的深入理解進行編程(這些代碼通常不太好維護,這取決于人員的技術能力)
5.提供良好的用戶體驗要絞盡腦汁地設計出好看的界面或接口,并預測變化,想辦法屏蔽變化

系統設計要考慮:

  1. 存量與流量:
    存量代表系統中已經存在的量(包括數據存儲和用戶請求量)
    流量代表系統中流入和流出的量(包括數據的寫入量和刪除量,用戶請求流量的增加與減少)
    對流量進行合理的預估,能夠讓系統不會因為流量的徒增出現突發性的故障。而由于存量的存在,系統展現自身狀態會存在延遲,如:熱點事件發生,qps突增進行擴容,但系統需要部署,新的機器上緩存從冷到熱是需要一個過程的,所以新機器擴容上來并不能立刻分攤突增的qps,在這里緩存的從冷填充滿整個緩存空間步入到熱的狀態,緩存未填充的部分就是存量。很多情況下,我們正是忽略了存量的存在,導致系統出現嚴重的問題。

  2. 調節回路與增強回路:
    調節回路是依托與流量的變動,即觀測到系統的出入行為而進行的調節,如:用戶請求量增加,結合監控系統通過k8自動擴容。
    增強回路在系統中起到改變系統狀態走向的絕對因素,要么系統變好,要么崩潰。舉例:引入redis會提升性能么?不一定,原因:跨網絡請求redis會增加一些耗時,redis崩潰是否會導致系統不可用,要承擔額外的維護成本。如果redis部署在內網耗時基本可忽略,可存儲的空間上來看遠高于應用內的緩存,能夠在微妙級別對完成緩存的讀寫并實現極高的吞吐量。redis的引入作為一個增強回路,能夠讓系統變得更好,但要確保它的可靠性和性能。

  3. 信息流:
    信息流是指系統能夠反饋或上報自身的信息包括確保可靠的異常狀態、正常狀態,系統產生的業務數據(拿到合理的數據才能評估系統的存在是否有價值)。同時要關注信息流動的方向,正反饋或負負反饋,正反饋有助于讓我們看到系統好的一面,負反饋讓我們知道系統存在哪些問題。我們基于信息流,建立監控系統,建立出現問題后的自我修復系統(如:自動擴縮容、故障隔離、自動降級、限流熔斷等)。

  4. 無處不在的取舍:
    架構本質就是在做取舍,不存在既要又要還要。業內折騰了這么多年的分布式CAP原則,在CA、AP、CP中進行取舍,沒有任何一個方案能夠實現CAP全都要。降級自保也是一種取舍,丟掉不重要的,為重要的提供服務和算力。取舍也是對抗復雜性和不確定性的一種妥協,是控制系統向混亂發展的一種方式。老板對你說,我要把數據庫中一個億的用戶數據在1分鐘之內全部刪除掉,不影響線上業務,這是不可能的,他只能接受更長的時間或因為不受控的刪除操作導致的服務不可用。

以上是我總結的一些架構思考方式和經驗,不知道你是否讀懂?架構從來不是閱讀一些框架源碼和技術書籍,這些只是幫助你實現目標的一種手段和過程,而架構是一種思考問題的方式,是不斷磨煉和沉淀出的方法論,這些方法論是腳踏實地的。

工作了四五年的你可能對以上經驗嗤之以鼻,認為我一個30多歲的老東西怎么能和我這20多歲思維活躍的年輕小伙抗衡?你覺得寫好一個ppt,列上自己干了多少活,達到了什么指標,就能夠忽悠住客戶與老板就行,拿這個方式跟面試官聊就能成為新公司的架構師,我的一位阿里朋友稱之為ppt架構師。那么,我們35歲再看看,你是否還在這個行業拿著高薪。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/75839.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75839.shtml
英文地址,請注明出處:http://en.pswp.cn/web/75839.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

項目部署-(二)Linux下SpringBoot項目自動部署

一、項目部署架構 二、項目部署環境 192.168.138.100(服務器A): -Nginx :部署前端項目、配置反向代理 -MySQL:主從復制結構中的主庫 192.168.138.101(服務器B): -JDK:運行Java項目 -Git:版本控制工具 -Maven:項目構建…

【LeetCode 熱題 100】哈希 系列

📁1. 兩數之和 本題就是將通過兩層遍歷優化而成的,為什么需要兩層遍歷,因為遍歷 i 位置時,不知道i-1之前的元素是多少,如果我們知道了,就可以通過兩數相加和target比較即可。 因為本題要求返回下標&#xf…

【Kubernetes基礎】--查閱筆記1

目錄 Kubernetes 是什么為什么要用 KubernetesKubernetes 概念MasterNodePodLabelReplication ControllerDeploymentHorizontal Pod AutoscalerStatefulSetService服務發現機制 JobVolumePersistent VolumeNamespaceConfigmap Kubernetes 是什么 Kubernetes是一個開放的開發平…

卷積神經網絡CNN到底在卷些什么?

來源: 卷積神經網絡(CNN)到底卷了啥?8分鐘帶你快速了解!_嗶哩嗶哩_bilibili卷積神經網絡(CNN)到底卷了啥?8分鐘帶你快速了解!共計2條視頻,包括:卷…

Axios 的 POST 請求:QS 處理數據的奧秘與使用場景解析

在現代前端開發中,Axios 已經成為了進行 HTTP 請求的首選庫之一,它的簡潔易用和強大功能深受開發者喜愛。當使用 Axios 進行 POST 請求時,我們常常會遇到一個問題:是否需要使用 QS 庫來處理請求數據?什么時候又可以不用…

java面試題帶答案2025最新整理

文章目錄 一、java面試題集合框架1. 請簡要介紹 Java 集合框架的體系結構2. ArrayList 和 LinkedList 的區別是什么3. HashMap 的工作原理是什么,它在 JDK 7 和 JDK 8 中有哪些不同4. 如何解決 HashMap 的線程安全問題5. TreeSet 是如何保證元素有序的 二、java面試…

4.B-樹

一、常見的查找方式 順序查找 O(N) 二分查找 O(logN)(要求有序和隨機訪問) 二叉搜索樹 O(N) 平衡二叉搜索樹(AVL樹和紅黑樹) O(logN) 哈希 O(1) 考慮效率和要求而言,正常選用 平衡二叉搜索樹 和 哈希 作為查找方式。 但這兩種結構適合用于…

CTF--shell

一、原題 (1)提示: $poc"a#s#s#e#r#t";$poc_1explode("#",$poc);$poc_2$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET[s]) (2)原網頁:一片空白什么都…

基于51單片機的正負5V數字電壓表( proteus仿真+程序+設計報告+講解視頻)

基于51單片機的正負5V數字電壓表( proteus仿真程序設計報告講解視頻) 仿真圖proteus7.8及以上 程序編譯器:keil 4/keil 5 編程語言:C語言 設計編號:S0101 1. 主要功能: 設計一個基于51單片機數字電壓表 1、能夠…

hive數倉要點總結

1.OLTP和OLAP區別 OLTP(On-Line Transaction Processing)即聯機事務處理,也稱為面向交易的處理過程,其基本特征是前臺接收的用戶數據可以立即傳送到計算中心進行處理,并在很短的時間內給出處理結果,是對用…

【實戰手冊】8000w數據遷移實踐:MySQL到MongoDB的完整解決方案

?? 本文將帶你深入解析大規模數據遷移的實踐方案,從架構設計到代碼實現,手把手教你解決數據遷移過程中的各種挑戰。 ??博主其他匠心之作,強推專欄: 小游戲開發【博主強推 匠心之作 拿來即用無門檻】文章目錄 一、場景引入1. 問題背景2. 場景分析為什么需要消息隊列?為…

運行小程序需要選擇什么配置的服務器

主要是看有多少人瀏覽,如果是每天有幾十個人瀏覽,通常2核或者4核就可以滿足需求,內存的話建議4g或者8g,足夠的內存可以使服務器同時處理多個請求,避免因內存不足導致的卡頓或程序崩潰。 硬盤存儲方面,50GB…

基于SpringBoo的地方美食分享網站

作者:計算機學姐 開發技術:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源碼”。 專欄推薦:前后端分離項目源碼、SpringBoot項目源碼、Vue項目源碼、SSM項目源碼、微信小程序源碼 精品專欄:…

Solidity私有函數和私有變量區別,私有變量可以被訪問嗎

web3面試題 私有函數和私有變量區別,私有變量可以被訪問嗎 ChatGPT said: 在 Web3 開發,尤其是使用 Solidity 編寫智能合約時,關于私有函數和私有變量的區別是常見的面試題。下面是詳細解析: ? 私有函數(Private Fu…

mongodb 安裝配置

1.下載 官網下載地址:MongoDB Community Download | MongoDB 2.使用解壓包 解壓包安裝:https://pan.baidu.com/s/1Er56twK9UfxoExuCPlJjhg 提取碼: 26aj 3.配置環境: (1)mongodb安裝包位置: &#xf…

多模態大語言模型arxiv論文略讀(十九)

MLLMs-Augmented Visual-Language Representation Learning ?? 論文標題:MLLMs-Augmented Visual-Language Representation Learning ?? 論文作者:Yanqing Liu, Kai Wang, Wenqi Shao, Ping Luo, Yu Qiao, Mike Zheng Shou, Kaipeng Zhang, Yang Yo…

[LeetCode 45] 跳躍游戲2 (Ⅱ)

題面: LeetCode 45 跳躍游戲2 數據范圍: 1 ≤ n u m s . l e n g t h ≤ 1 0 4 1 \le nums.length \le 10^4 1≤nums.length≤104 0 ≤ n u m s [ i ] ≤ 1000 0 \le nums[i] \le 1000 0≤nums[i]≤1000 題目保證可以到達 n u m s [ n ? 1 ] nums[…

前端面試寶典---閉包

閉包介紹 使用閉包: 在函數內聲明一個變量,避免外部訪問在該函數內再聲明一個函數訪問上述變量(閉包)返回函數內部的函數使用完畢建議閉包函數null;譯放內存 function createCounter() {let count 0;return function () {coun…

GPT4O畫圖玩法案例,不降智,非dalle

網址如下: 玩法1:吉卜力(最火爆) 提示詞:請將附件圖片轉化為「吉卜力」風格,尺寸不變 玩法2:真人繪制 提示詞:創作一張圖片,比例4:3,一個20歲的中國女孩…

4.12~4.14【Q】cv homework6

我正在寫GAMES101作業6,在這段代碼中,我十分想知道inline Intersection Triangle::getIntersection(Ray ray) 是由哪個函數,哪段代碼調用的?什么是Inline?詳細解釋,越細節越好 我正在寫GAMES101作業6&…