這篇文章就來回顧下自己近八年來所做的一些系統(tǒng)設計,看看犯的一些比較大的血淋淋的錯誤(很多都是推倒重來)抑进,這八年來主要做了三個基礎技術產品强经,三個橫跨三年的大的技術項目(其中有兩個還在進行中),發(fā)現(xiàn)大的錯誤基本集中在前面幾年寺渗,從這個點看起來能比較自豪的說在最近的幾年在系統(tǒng)設計的掌控上確實比以前成熟了很多匿情。
除了自己犯的錯以外,也還看過其他同學犯的一些錯信殊,這個在后面再寫一篇文章來分享下炬称,同時也征集大家印象深刻的推倒重來的系統(tǒng)設計的錯。
第1個錯
在設計服務框架時涡拘,我期望服務框架對使用者完全不侵入玲躯,于是做了一個在外部放一個.xml文件來描述spring里的哪些bean發(fā)布為服務的設計,這個版本發(fā)布后鲸伴,第一個小白鼠的用戶勉強在用府蔗,但覺得用的很別扭晋控,不過還是忍著用下去了汞窗,到了發(fā)布的時候,發(fā)現(xiàn)出現(xiàn)了兩個問題赡译,一是這個xml文件研發(fā)也不知道放哪好仲吏,所以到了發(fā)布的時候都不知道去哪拿這個xml文件。
這個設計的關鍵錯誤就在于在設計時沒考慮過這個設計方式對研發(fā)階段蝌焚、運維階段的影響裹唆,后來糾正這個錯誤的方法是去掉了這個xml文件,改為寫了一個Spring FactoryBean只洒,用戶在spring的bean配置文件中配置下就可以许帐。
因此對于一個架構師來說,設計時在全面性上要充分考慮毕谴。
第2個錯
服務框架在核心應用上線時成畦,出現(xiàn)了前端web應用負載高,處理線程數不夠用的現(xiàn)象框仔,當時處理這個故障的方式是回滾了服務框架的上線离斩,這個故障排查了比較長的時間后跛梗,查到的原因是服務框架用的JBoss Remoting在通信時默認時間是60s棋弥,導致一些處理速度慢的請求占據了前端web應用的處理線程池嘁锯。
上面這里故障的原因簡單來說是分布式調用中超時時間太長的問題,但更深層次來思考蝗羊,問題是犯在了設計服務框架時的技術選型耀找,在選擇JBoss-Remoting時沒有充分的掌握它的運行細節(jié)野芒,這個設計的錯誤導致的是后來決定放棄JBoss-Remoting狞悲,改為基于Mina重寫了服務框架的通信部分妇斤,這里造成了服務框架的可用版本發(fā)布推遲了兩個多月站超。
因此對于一個架構師來說,在技術選型上對技術細節(jié)是要有很強的掌控力的融求。
第3個錯
在服務框架大概演進到第4個版本時生宛,通信協(xié)議上需要做一些改造,突然發(fā)現(xiàn)一個問題是以前的通信協(xié)議上是沒有版本號的七芭,于是悲催的只能在代碼上做一個很齷蹉的處理來判斷是新版本還是老版本狸驳。
這個設計的錯誤非常明顯缩赛,這個其實只要在最早設計通信協(xié)議時參考下現(xiàn)有的很多的通信協(xié)議就可以避免了酥馍,因此這個錯誤糾正也非常簡單旨袒,就是參考一些經典的協(xié)議重新設計了下。
因此對于一個架構師來說施无,知識面的廣是非常重要的猾骡,或者是在設計時對未來有一定的考慮也是非常重要的兴想。
說到協(xié)議赡勘,就順帶說下,當時在設計通信協(xié)議和選擇序列化/反序列化上沒充分考慮到將來多語言的問題顽悼,導致了后來在多語言場景非常的被動,這也是由于設計時前瞻性的缺失映胁,所謂的前瞻性不是說一定要一開始就把未來可能會出現(xiàn)的問題就解掉甲雅,而是應該留下不需要整個改造就可以解掉的方法,這點對于架構師來說也是非常重要的弛姜。
第4個錯
在服務框架切換為Mina的版本上線后廷臼,發(fā)布服務的應用重啟時出現(xiàn)一個問題,就是發(fā)現(xiàn)重啟后集群中的機器負載嚴重不均寂恬,排查發(fā)現(xiàn)是由于這個版本采用是服務的調用方會通過硬件負載均衡去建立到服務發(fā)布方的連接初肉,而且是單個的長連接牙咏,由于是通過硬件負載均衡建連眠寿,意味著服務調用方其實看到的都是同一個地址焦蘑,這也就導致了當服務發(fā)布方重啟時例嘱,服務調用方重連就會集中的連到存活的機器上,連接還是長連奢浑,因此就導致了負載的不均衡現(xiàn)象雀彼。
這個設計的錯誤主要在于沒有考慮生產環(huán)境中走硬件負載均衡后徊哑,這種單個長連接方式帶來的問題聪富,這個錯誤呢還真不太好糾正,當時臨時用的一個方法是服務調用方的連接每發(fā)送了1w個請求后萧豆,就把連接自動斷開重建涮雷,最終的解決方法是去掉了負載均衡設備這個中間點轻局。
因此對于一個架構師來說嗽交,設計時的全面性要非常的好夫壁,我現(xiàn)在一般更多采用的方式是推演上線后的狀況,一般來說在腦海里過一遍會比較容易考慮到這些問題梅肤。
第5個錯
服務框架在做了一年多以后姨蝴,某個版本中出現(xiàn)了一個嚴重bug左医,然后我們就希望能通知到用了這個版本的應用緊急升級同木,在這個時候悲催的發(fā)現(xiàn)一個問題是我們壓根就不知道生產環(huán)境中哪些應用和機器部署了這個版本彤路,當時只好用一個臨時的掃全網機器的方法來解決洲尊。
這個問題后來糾正的方法是在服務發(fā)布和調用者在連接我們的一個點時,順帶把用的服務框架的版本號帶上躯护,于是就可以很簡單的知道全網的服務框架目前在運行的版本號了榛做。
因此對于一個架構師來說检眯,設計時的全面性是非常重要的昆淡,推演能起到很大的幫助作用昂灵。
第6個錯
服務框架這種基礎類型的產品,在發(fā)布時會碰到個很大的問題管削,就是需要通知到使用者去發(fā)布含思,導致了整個發(fā)布周期會相當的長含潘,當時做了一個決定线婚,投入資源去實現(xiàn)完全動態(tài)化的發(fā)布塞弊,就是不需要重啟游沿,等到做的時候才發(fā)現(xiàn)這完全就是個超級大坑,最終這件事在投入兩個人做了接近半年后循集,才終于決定放棄咒彤,而且最終來看其實升級的問題也沒那么大镶柱。
這個問題最大的錯誤在于對細節(jié)把握不力模叙,而且決策太慢。
因此對于一個架構師來說厂庇,技術細節(jié)的掌控非常重要权旷,同時決策力也是非常重要的贯溅。
第7個錯
服務發(fā)布方經常會碰到一個問題它浅,就是一個服務里的某些方法是比較耗資源的,另外的一些可能是不太耗資源鄙麦,但對業(yè)務非常重要的方法黔衡,有些場景下會出現(xiàn)由于耗資源的方法被請求的多了些導致不太耗資源的方法受影響腌乡,這種場景下如果要去拆成多個服務与纽,會導致開發(fā)階段還是挺痛苦的,因此服務框架這邊決定提供一個按方法做七層路由的功能影所,服務的發(fā)布方可以在一個地方編寫一個規(guī)則文件猴娩,這個規(guī)則文件允許按照方法將生產環(huán)境的機器劃分為不同組卷中,這樣當服務調用方調用時就可以做到不同方法調用到不同的機器蟆豫。
這個功能對有些場景來說用的很爽懒闷,但隨著時間的演進和人員的更換,能維護那個文件的人越來越少了帮辟,也成為了問題织阅。
這個功能到現(xiàn)在為止我自己其實覺得也是一直處于爭議中震捣,我也不知道到底是好還是不好...
因此對于一個架構師來說蒿赢,設計時的全面性是非常重要的羡棵。
減少犯錯誤的方法:多閱讀、多實踐店展、多總結
第8個錯
服務框架在用的越來越廣后赂蕴,碰到了一個比較突出的問題概说,服務框架依賴的jar版本和應用依賴的jar版本沖突糖赔,服務框架作為一個通用技術產品轩端,基本上沒辦法為了一個應用改變服務框架自己依賴的jar版本基茵,這個問題到底怎么去解,當時思考了比較久声怔。
可能是由于我以前OSGi這塊背景的原因醋火,在設計上我做了一個決定,引入OSGi柿冲,將服務框架的一堆jar處于一個獨立的classloader假抄,和應用本身的分開宿饱,這樣就可以避免掉jar沖突的問題脚祟,在我做了引入OSGi這個決定后由桌,團隊的1個資深的同學就去做了,結果是折騰了近兩個月整個匹配OSGi的maven開發(fā)環(huán)境都沒完全搭好铭乾,后來我自己決定進去搞這件事炕檩,即使是我對OSGi比較熟淮野,也折騰了差不多1個多月才把整個開發(fā)的環(huán)境,工程的結構经瓷,以及之前的代碼基本遷移為OSGi結構舆吮,這件事當時折騰好上線后队贱,效果看起來是不錯的柱嫌,達到了預期。
但這件事后來隨著加入服務框架的新的研發(fā)人員越來越多与学,發(fā)現(xiàn)多數的新人都在學習OSGi模式的開發(fā)這件事上投入了不少的時間,就是比較難適應晕窑,所以后來有其他業(yè)務問是不是要引入OSGi的時候杨赤,我基本都會建議不要引入疾牲,主要的原因是OSGi模式對大家熟悉的開發(fā)模式、排查問題的沖擊说敏,除非是明確需要classloader隔離、動態(tài)化這兩個點枫匾。
讓我重新做一個決策的話干茉,我會去掉對OSGi的引入很泊,自己做一個簡單的classloader隔離策略來解決jar版本沖突的問題,保持大家都很熟悉的開發(fā)模式戳鹅。
因此對于一個架構師來說枫虏,設計時的全面性是非常重要的隶债。
第9個錯
服務框架在用的非常廣了后死讹,團隊經常會被一個問題困擾和折騰曲梗,就是業(yè)務經常會碰到調用服務出錯或超時的現(xiàn)象,這種情況通常會讓服務框架這邊的研發(fā)來幫助排查仅颇,這個現(xiàn)象之所以查起來會比較復雜忘瓦,是因為服務調用通常是多層的關系耕皮,并不是簡單的A-->B的問題,很多時候都會出現(xiàn)A-->B-->C-->D或者更多層的調用粱年,超時或者出錯都有可能是在其中某個環(huán)節(jié)台诗,因此排查起來非常麻煩赐俗。
在這個問題越來越麻煩后,這個時候才想起在09年左右團隊里有同學看過G家的一篇叫dapper的論文粱快,并且做了一個類似的東西叔扼,只是當時上線后我們一直想不明白這東西拿來做什么瓜富,到了排查問題這個暴露的越來越嚴重后食呻,終于逐漸想起這東西貌似可以對排查問題會產生很大的幫助。
到了這個階段才開始做這件事后每辟,碰到的主要不是技術問題渠欺,而是怎么把新版本升級上去的問題挠将,這個折騰了挺長時間,然后上線后又發(fā)現(xiàn)了一個新的問題是乳丰,即使服務框架具備了Trace能力产园,但服務里又會調外部的例如數據庫什燕、緩存等屎即,那些地方如果有問題也會看不到技俐,排查起來還是麻煩虽另,于是這件事要真正展現(xiàn)效果就必須讓Trace完全貫穿所有系統(tǒng)饺谬,為了做成這件事募寨,N個團隊付出了好幾年的代價拔鹰。
因此對于一個架構師來說列肢,設計時的全面性宾茂、前瞻性非常重要,例如Trace這個的重要性欧聘,如果在最初就考慮到怀骤,那么在一開始就可以留好口子埋好伏筆,后面再要做完整就不會太復雜弓摘。
第10個錯
服務的發(fā)布方有些時候會碰到一個現(xiàn)象是韧献,服務還沒完全ready,就被調用了蓝撇;還有第二個現(xiàn)象是服務發(fā)布方出現(xiàn)問題時虽抄,要保留現(xiàn)場排查問題独柑,但服務又一直在被調用车酣,這種情況下就沒有辦法很好的完全保留現(xiàn)場來慢慢排查問題了索绪。
這兩個現(xiàn)象會出現(xiàn)的原因是服務框架的設計是通過啟動后和某個中心建立連接湖员,心跳成功后其他調用方就可以調用到,心跳失敗后就不會被調到瑞驱,這樣看起來很自動化娘摔,但事實上會導致的另外一個問題是外部控制上下線這件事的能力就很弱。
這個設計的錯誤主要還是在設計時考慮的不夠全面唤反。
因此對于一個架構師來說凳寺,設計時的全面性非常重要。
第11個錯
在某年我和幾個小伙伴決定改變當時用xen的模式彤侍,換成用一種輕量級的“虛擬機”方式來做肠缨,從而提升單機跑的應用數量的密度海蔽,在做這件事時猜扮,我們決定自己做一個輕量級的類虛擬機的方案惠窄,當時決定的做法是在一個機器上直接跑進程霜运,然后碰到一堆的問題藕各,例如從運維體系上來講膘魄,希望ssh到“機器”、獨立的ip洛波、看到自己的系統(tǒng)指標等等,為了解決這些問題够话,用了N多的黑科技欣尼,搞得很悲催,更悲催的是當時覺得這個問題不多,于是用一些機器跑了這個模式驻子,結果最后發(fā)現(xiàn)這里面要黑科技解決的問題實在太多了馅袁,后來突然有個小伙伴提出我們試用lxc吧,才發(fā)現(xiàn)我們之前用黑科技解的很多問題都沒了,哎座云,然后就是決定切換到這個模式璧帝,結果就是線上的那堆機器重來苏潜。
這個設計的主要錯誤在于知識面不夠廣,導致做了個不正確的決定,而且推倒重來物咳。
因此對于一個架構師來說压鉴,知識面的廣非常重要婉宰,在技術選型這點上非常明顯馒铃。
第12個錯
還是上面這個技術產品,這個東西有一個需求是磁盤空間的限額,并且要支持磁盤空間一定程度的超賣,當時的做法是用image的方式來占磁盤空間限額波附,這個方式跑了一段時間覺得沒什么問題仅财,于是就更大程度的鋪開了碎罚,但鋪開跑了一段時間后宫峦,出現(xiàn)了一個問題,就是經常出現(xiàn)物理機磁盤空間不足的報警肮雨,而且刪掉了lxc容器里的文件也還是不行壳坪,因為image方式只要占用了就會一直占著這個大小,只會擴大不會縮小。
當時對這個問題極度的頭疼江解,只能是刪掉文件后,重建image,但這個會有個要求是物理機上有足夠的空間煌张,即使有足夠的空間,這個操作也是很折騰人的务傲,因為得先停掉容器,cp文件到新創(chuàng)建的容器诺祸,這個如果東西多的話,還是要耗掉一定時間的答恶。
后來覺得這個模式實在是沒法玩,于是尋找新的解決方法挤渐,來滿足磁盤空間限額,允許超賣的這兩需求榛泛,最后我們也是折騰了比較長一段時間后終于找到了更靠譜的解決方案覆享。
這個設計的主要錯誤還是在選擇技術方案時沒考慮清楚,對細節(jié)掌握不夠擦盾,考慮的面不夠全掉弛,導致了后面為了換掉image這個方案,用了極大的代價,我印象中是一堆的人熬了多次通宵來解決焕檬。
因此對于一個架構師來說碰辅,知識面的廣羹蚣、對技術細節(jié)的掌控和設計的全面性都非常重要。
第13個錯
仍然是上面的這個技術產品绷落,在運行的過程中荣月,突然碰到了一個虛擬機中線程數創(chuàng)建太多捐下,導致其他的虛擬機也創(chuàng)建不了線程的現(xiàn)象(不是因為物理資源不夠的問題)咽白,排查發(fā)現(xiàn)是由于盡管lxc支持各個容器里跑相同名字的賬號授段,但相同名字的賬號的uid是相同的,而max processes是限制在UID上的表锻,所以當一個虛擬機創(chuàng)建的線程數超過時契沫,就同樣影響到了其他相同賬號的容器昔汉。
這個問題我覺得一定程度也可以算是設計問題懈万,設計的時候確實由于對細節(jié)掌握的不夠,考慮的不全導致忽略了這個點靶病。
因此對于一個架構師來說会通,對技術細節(jié)的掌控和設計的全面性都非常重要。
第14個錯
在三年前做一個非常大的項目時娄周,項目即將到上線時間時涕侈,突然發(fā)現(xiàn)一個問題是,有一個關鍵的點遺漏掉了煤辨,只好趕緊臨時討論方案決定怎么做裳涛,這個的改動動作是非常大的,于是項目的上線時間只能推遲众辨,我記得那個時候緊急周末加班等搞這件事端三,最后帶著比較高的風險上了。
這個問題主要原因是在做整體設計時遺漏掉了這個關鍵點的考慮鹃彻,當時倒不是完全忽略了這個點郊闯,而是在技術細節(jié)上判斷錯誤,導致以為不太要做改動。
因此對于一個架構師來說团赁,對技術細節(jié)的掌控是非常重要的育拨,這里要注意的是,其實不代表架構師自己要完全什么都很懂然痊,但架構師應該清楚在某個點上靠譜的人是誰至朗。
讀者福利
分享免費學習資料
針對于Java程序員,我這邊準備免費的Java架構學習資料(里面有高可用剧浸、高并發(fā)锹引、高性能及分布式、Jvm性能調優(yōu)唆香、MyBatis嫌变,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)
為什么某些人會一直比你優(yōu)秀,是因為他本身就很優(yōu)秀還一直在持續(xù)努力變得更優(yōu)秀躬它,而你是不是還在滿足于現(xiàn)狀內心在竊喜腾啥!希望讀到這的您能點個小贊和關注下我,以后還會更新技術干貨冯吓,謝謝您的支持倘待!
資料領取方式:加入Java技術交流群963944895
,點擊加入群聊组贺,私信管理員即可免費領取