首頁 > theory > SOA > > 正文

系統架構設計—性能和代碼可維護性的取舍之道

發布人:zhoulujun    點擊:

不是需求變更驅動著軟件的不斷更改,而是“軟件可以隨意更改”的這種特性刺激了不斷的需求變更。但是,說這句話的前提就是,有足夠的時間給開發人員。不能是瞎扯!

軟件行業有各種各樣的系統,每一種系統的開發都可能會有不同的目標。比如導彈發射的系統,我們可以想象,目標(甚至是基本要求)肯定是:1、穩定(絕對不能走火);2、迅速反應(不允許按下發射按鈕后一分鐘導彈才開始發射)。你可能覺得這種要求很好啊!任何系統不都是應該滿足這樣要求的嗎?比如我在淘寶買T恤,結果給我發一條丁字褲,這怎么行?一個網頁半天打不開還有理了?我還真得答一句,它就是有理了。“存在即合理”,這里的合理,合理在成本。我們目前日常使用到的絕大部分軟件,都是有bug的,而且是一堆的bug,但我們仍然在使用它們。如果你想使用像“導彈發射”一樣穩定精確迅捷的軟件,可能最后的結果只有一個:你用不起。(請自行腦補)

一、性能不是不重要,而是他沒有可維護性重要。

要理解這一點,首先要理解可維護性的重要

很多的開發項目,是這樣一個流程:一群人根據文檔開始開發,幾個月后通過驗收上線;然后開發團隊解散,留下一兩個項目組里最菜的菜鳥做“維護”。Game Over!皆大歡喜。這種現象,在各種外包團隊(尤其是以項目計價的廉價外包團隊)中更加的突出(這或許也是大家普遍歧視外包公司的一個原因?)

既然是這樣一種開發模式,很多開發人員根本體會不到維護的痛苦。在他們看來,“維護嘛,修修補補,加一兩個if...else而已,讓我們開發人員做更高大上的工作吧!”但他們也不是總這么幸運,有時候,他們會被抓去“填坑”。據說最通常的做法,就是在“老坑”周圍再挖一堆“新坑”,填平之前的老坑即可。周而復始,直到有一天,“受不了啦!我們重寫吧!”——等等,為什么不重構?呵呵,好問題,你覺得呢?

變態的需求變更——腦殘的產品經理

很多程序員把這種困境歸咎于“需求變更”。如果不是那些傻逼客戶一天到晚的改需求,我一定會做出一個完美的作品!

不是需求變更驅動著軟件的不斷更改,而是“軟件可以隨意更改”的這種特性刺激了不斷的需求變更。

你裝修好的房子,是不是住一段時間之后就會覺得這里那里不合適?這里少了一個插座,陽臺上該加一個龍頭,櫥柜用著不順手……“要是能改改就更好了!”,只是這樣的改動太費力,所以大多數時間我們都還是算了。但軟件可以!理論上怎么改都可以。想想軟件真的是一種很特殊的商品——它是可以交付“半成品”的。你先用著,如果有問題我再改改,有新需求我再改改,一直可以改到面目全非。沒有在其他傳統行業里待過的程序員無法理解,“可以隨意更改”是一種多么出色的特質。這意味著產品可以自我進化,應對各種變化,可以永生!想象這樣一臺“汽車”,開始可以在馬路上跑,過段時間改一下就可以在水里游,再拆裝一下可以當摩托拉風,堵車的時候展開翅膀……這是什么樣一種屌爆天的體驗啊?

所以,“擁抱變化”絕不是一句口號,這是一種胸懷。

作為示例的這兩個系統,我是希望能用他們一輩子的。但我甚至無法想象一年之后他們會是什么樣子——他們需要接受市場的檢驗,應對技術的升級換代,會有各種想象不到的變化。所以,可維護性無疑是必須放到首位的

為了可維護

明確了架構的首要目標,我們就可以做一些基礎的選擇了。比如開發語言,可是是面向對象的C#,不需要“性能卓越”的C。

說道“面向對象”,可能有些同學就會比較high,腦子里就會冒出“抽象”、“封裝”、“設計模式”等各種高大上的東西出來。但我不得不提醒你們:首先,這些都是微觀層面考慮的東西,而架構是宏觀的;然后,這些都不是架構,而是潤滑黏合支持架構的東西;最后,在其他條件不變的情況下,系統中這些東西用得越少,說明架構越好。

我們以“設計模式”為例。大家在學習設計模式的過程中有沒有這樣一種困惑,“這樣繼承封裝多態亂七八糟的繞來繞去的干嘛?”我花了很長一段時間才明白,要理解設計模式,必須要明白三個字:“不得已”。是迫不得已,才用設計模式來解決一些特定的問題,而不是說正常的代碼就應該這樣寫!這種迫不得已,有很多種原因。個人覺得最容易理解的就是“適配器模式”,因為出現了接口的沖突,所以我們不得不進行適配。但一個很自然的問題就是:為什么不直接改接口讓他們自然融洽呢?這不是一種更自然更直觀的解決方案嗎?答案很有可能就是因為架構——大的架構已經確立,局部必須服從整體。那么,如果一個完全理想化的架構,是不是根本就不應該出現這種問題接口沖突的問題,因而根本就不需要這種設計模式?

所以,我說設計模式之類的東西是潤滑劑是黏合劑,他們的作用是彌補架構的局部缺陷,更好的支撐架構。更極端的一種說法可以送給癡迷于設計模式的同學:設計模式是藥,沒病就不要吃藥!

那么,為了可維護性,架構中究竟應該注意些什么?這是一個很大的話題,開篇我們只說一點。

模塊劃分

模塊有大有小,大可以是一個分層一個項目,小可以是一個方法一個類。我們通常的做法是由大到小,逐步細分。

模塊的劃分是相當的考驗架構能力的。良好的模塊劃分,能夠讓我們方便的安排人手、合理的組織項目進度、迅速的定位代碼……各種好處說都說不完。所以還是說說不好的模塊劃分有什么問題更容易一些,嗯,這個好像根本就不要說,想想你在一堆亂七八糟的代碼里不斷的F11的情形吧!

我個人認為,模塊劃分的難度在于“整齊”和“靈活”之間取舍。通常來說,大的模塊我們都是“一刀切”,著重強調的是“整齊”,比如口熟能詳的UI層、BLL層和DAL層,但這種“一刀切”的做法,更多的是一種無奈。我們的人類的思維局限決定了我們在考慮復雜問題時無法深入到每一個細節,所以只能先“大而化之”的把一個復雜問題先進行簡單化。這樣帶來的一個嚴重的副作用就是,限制了代碼的靈活性;而靈活性,正是應對復雜變化的有效武器。所以,在更小一些的模塊(比如說:類)里,我們引入了豐富多彩的抽象繼承設計模式等一系列充滿各種靈活性的機制,以彌合“一刀切”造成的問題。這一松一緊一張一弛中“度”的掌握,就只能說是一種藝術了。

模塊劃分,籠統的說教用處不大,我們將在后面的文章中結合具體情況逐一說明。但我希望大家能夠明白:模塊劃分是必須的——這種必須,是一種無可奈何的選擇。所以,喜歡從頁面直接寫sql到數據庫的同學,老大讓你把你的代碼拆成幾段放到不同地方的時候,不要嫌麻煩;喜歡把一個簡單項目切成七層的同學,先仔細想不想這樣做是不是真的有必要。

代碼之外

為了代碼能夠長期有效的維護,我們還需要做很多工作,比如良好的文檔、完善的項目管理流程。但我想說的,還是不是這個,而是代碼之外的因素對項目架構的影響。比如開發團隊的背景能力偏好,一群C#程序員,你一定要整個node.js,這純粹是給自己找不痛快。除了這些稍稍用腦袋想一想就能明白的東西,有一件事,很多程序員并沒有意識到。

架構的一個天然目的就是:讓代碼更智能讓程序員更傻瓜。換一張說法就是,架構要“創造便利,讓程序員更關注業務”。

這可能是一個讓程序員感到悲哀的事實。正如機械師不停的發明,讓機器變得越來越聰明,取代流水線上的工人,最終取代了他們自己。從某種意義上說,我們都是自掘墳墓的人。一個良好的架構,就應該是讓每一個普通開發人員,都是一個個盡量廉價隨時可以替換的螺絲釘,這樣才能保證系統永遠健康正常的運行下去。告訴你這個事實可能讓你一整天都不開心,但接受這個事實之后能幫助你在工作中變得更加的“心平氣和”。螺絲釘就要有螺絲釘的覺悟;更何況,當好一顆螺絲釘也不是一件很容易的事。

解決性能問題,我們可以有很多代碼以外行之有效的方法,而可維護性基本上就只能靠代碼了;最后,還是要牢記:沒有犧牲,就沒有勝利!

二、在絕大多數情況下,當性能和可維護性相沖突的時候,性能讓位于可維護性。

我們采用其他辦法來彌補代碼性能不夠高的問題。

優化首先需要找到性能“瓶頸”。否則,任何人都可以隨手一指,“這段代碼需要優化”。

可讀性更強的代碼總是更好優化

硬件永遠比軟件便宜(即硬件比技術人員便宜,看系統規模而定)

合理浪費堆硬件

說了這么多,不知道有沒有引起同學們的反思。可能大家還是過不去心里那道坎:明明有一種性能更高的方法我們為什么不用?

因為浪費唄!

什么?你有沒有搞錯?我的代碼,至少省了一塊內存條!那是你還沒從“窮學生”的角色里轉換過來。你花一周的時間對代碼進行了優化(就先不考慮你的優化帶來的維護成本增加了),為老板省下了一塊內存條的錢。你以為老板會拍著你的肩膀表揚你么?老板打不死你!

兄弟,賬不是你那樣算的。當你是學生的時候,你的時間成本是0;但你進入工作崗位,每一天都是要發工資的。

通過代碼來調高性能,是一種無奈——對硬件性能不夠的妥協(參考:80年代游戲開發者的辛苦困境。這樣寫性能就高,但為什么現在沒有誰再這么寫代碼了?)。否則,絕大多數情況下,堆硬件比優化代碼的效果好得多,而且便宜得多。硬件的成本按摩爾定律往下降,我們程序員的工資也能按摩爾定律減么?(注:硬件越來越便宜,而人員工資越來越增加,就拿工廠的普通工人用工成本也在增加)

明明window 10 比window 95更耗性能,為什么今天沒人用window 95?為什么VS 2013要10G的空間我們都還屁顛屁顛的趕緊裝上?為什么現在大家都用C#,沒人用匯編?(注:匯編比高級語言性能強,接近機器)

我們站在人類文明積累的今天,就應該理所當然的享受這一切成 果。有打火機你不用,你要鉆木取火。如果你是因為要學貝爺荒野求生裝逼,可以理解;如果你說你是因為怕浪費天然氣,我……我……我怎么說你呢?“給做打火 機的一條活路,行不?”同樣的,程序員大神同學,你就當做好事,給下面寫底層做硬件的一條活路吧!你的代碼都是 010001000010000001010101……了,你讓其他人怎么活啊?

最后,我突然想到的一個程序員為什么對性能如此敏感瘋狂,對可維護性毫不在意的一個可能原因:

性能很好理解,卡得要死和跑得飛快;可維護性很不好理解,至少得跑個兩三年才能體現,那時候,誰知道爺在哪里偷著樂呢

性能上不來,程序員只有羞愧的低著頭,都是我的錯;需求有變更,開口就罵,“哪個SB又要改……”;

大家覺得是不是這樣的?所以,愿意把代碼百煉成鋼繞指柔的人少。想來,是一種莫名的悲哀和凄涼。

 某網友說:

 感覺性能問題屬于眼前問題,可維護性問題屬于以后的問題。很多人就是感覺先把眼前自己的事兒處理完就行,程序只要能運行,老板滿意就萬事大吉,至于以后維護愛誰誰吧,那個時候老子已經另謀高就了,爛攤子留給后面的倒霉蛋吧。

這段話的確說出了目前的現狀,被逼的,上面只看功能完成與否,功能完成的質量不管。那還關系代碼的可維護性干嘛。于是把代碼復制,拷貝函數,只要能跑起來就好了。

性能、可維護性從來都是要折中,過分從代碼中追求性能會增大開發成本、降低可維護性。

現實中老板、客戶真不在意那點硬件成本。只要項目快點上線。二期三期四期優化都好說。

張口閉口 談性能的 經理 我之前在北京 也見過 幾個。

最后 用幾個 實際的項目,堵住了他的嘴

—— 別跟我談性能,即使我用濫反射,性能也比你的快。

 同意,除了核心功能和高并發的頁面,

一直以來寫代碼的風格,首先就是可讀性,可維護, 然后再是性能.


 就比如php,很多功能函數廢棄掉了

今天拿之前的代碼,完全跑不通。各種報錯。比如

$mysql_conn = mysql_connect ( "$dbserver", "$dbusername", "$dbpassword" ) or die ( "Mysql connect is error." );

在php5.5.12版本運行會提示

Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in D:\soft\develop\php\wamp\2.5\wamp\www\generate_mysql.php on line 16

今天是:

Uncaught Error: Call to undefined function mysql_connect() in 

看來會廢棄了,不建議使用了,程序無法運行的。使用mysqli or PDO 來替代。到高版本,根本無法使用這個函數了。

我想知道哪個php版本開始就會開始不建議使用這個函數了,所以去官網www.php.net搜索這個函數。有這樣的介紹:

本擴展自 PHP 5.5.0 起已廢棄,并在將來會被移除。應使用 MySQLi 或 PDO_MySQL 擴展來替換之。參見 MySQL:選擇 API 指南以及相關 FAQ 以獲取更多信息。用以替代本函數的有:

給我們做接口服務的啟發

到高版本,沒有兼容舊太舊版本的函數,為什么這樣子?從php官方組織維護源碼角度來說,這個函數肯定是沒啥優勢了,去進行優化這個函數,還不如對它進行廢棄掉

我們在網站,需要提供給接口給公司內部其他子系統調用。也會存在接口升級,原來的接口設計有缺陷,不想去修復了。干脆建議使用新版本的接口了。

思考,我何不也弄一個類似的提升呢。提示調用方升級到新接口去使用。

突然發現,從溝通成本角度考慮,把提示信息放在接口返回給調用方,會推進改進的速度。

為什么官方要把這個函數禁用掉?而不是去修復,優化呢?

性能考慮,安全考慮。去官網找答案

Recommended API

It is recommended to use either the mysqli or PDO_MySQL extensions. It is not recommended to use the old mysql extension for new development, as it has been deprecated as of PHP 5.5.0 and will be removed in the future. A detailed feature comparison matrix is provided below. The overall performance of all three extensions is considered to be about the same(三個擴展的性能不相上下). Although the performance of the extension contributes only a fraction of the total run time of a PHP web request(性能考慮在php的web請求中是一小部分考慮,即不僅僅是性能考慮). Often, the impact is as low as 0.1%(影響是很小的,0.1%)

php接口支持

看官網介紹,不是性能考慮才棄用那個擴展。反正就是棄用,不想去維護那個了。我也沒搞清楚。不過深有同感,舊的擴展代碼量也不少,去優化,接口結構始終無法達到質的提升,還不如新開一個?

 我的思考:

性能問題:關鍵是找到性能的瓶頸點。而不是糾結于細微的。也就是主要矛盾。把主要矛盾解決掉后,就好多了

就拿php來說,是解釋性語言,解釋性語言是比不上編譯型語言快。但是在web應用中,不涉及到復雜的cpu計算(簡單的增刪查改數據庫,不是分詞這么復雜的算法,這種屬于cpu密集型),瓶頸在磁盤讀寫(磁盤i/0)和數據庫上。

所以看不出php的劣勢出來。當達到facebook這樣的應用,他們就會感到一點點php優化,對整個成本的減低,省去很多服務器。

所以有規模,就是規模成本。經濟學中有個規模效益。通俗例子理解,生產100個要這么多成本,生產1000也是差不多的成本,但是可以得到更多利潤。所以只能把規模擴大,才能賺到錢。

你達不到那個規模的時候,去談優化性能,帶來的是得不償失的。

而且,就算是解決性能問題,關鍵是找到瓶頸在哪里,解決了瓶頸,就會帶來質的變化。而不是糾結于細節優化。主次矛盾要分清楚。

單純說性能,那直接用匯編寫代碼,發明高級語言干嘛,匯編語言更加接近機器二進制,所以性能更強。但是匯編語言不容易維護,因為難懂,難上手。不接近人類的思維習慣。

記得國外有本書中提到一個關掉:代碼是寫給人(對象是程序員)看的,不是寫給機器看的,如果是寫給機器看的,那么干嘛不直接使用二進制01011這樣的方式去寫代碼呢,機器識別就是二進制。而且,性能更強,不用經過中間轉換,是不是。

參考文章:

http://www.cnblogs.com/wangtao_20/p/4809799.html

http://www.cnblogs.com/wangtao_20/p/4823088.html

http://www.cnblogs.com/freeflying/p/4788494.html

http://www.cnblogs.com/freeflying/p/4752415.html