論軟體工程

來源:互聯網
上載者:User

標籤:軟體工程   開發   

論軟體工程

昨天一位同學問我對軟體工程的體會?當時愣住了一下,不知道該怎麼回答,感覺話到嘴邊卻不知道怎麼表達,因為這個話題比較沉重,不知道該怎麼用簡短的語言來進行描述,要說可能一天都說不完,可能是自己的表達能力有限,語文沒學好(高考語文不到80分),當時只是給出了:“這叫我怎麼回答”這樣的答案。隨後在我的腦海裡一直回憶自己開發經曆,想從中找到一些資訊。第一次聽到軟體工程是大三的時候有一門課程叫做軟體工程,我依稀記得裡面幾個關鍵詞語:髙聚類.低耦合、可擴充性、可行性、可維護性,同時還包括一些軟體開發過程。下面將結合這幾點以及我的經曆來談談我眼中的軟體工程,同時也對自己進行一次總結。

髙聚類.低耦合

這個詞是自從我上過軟體工程的課之後一直沒忘記過,並且一直影響這我著設計開發過程。什麼叫髙聚類?一句話就可以概要:物以類聚,人以群分。什麼意思?就是將相同屬性的事物抽離出來,從而簡化對這一類事物的控制,以方便管理。那什麼叫低耦合?上面說了將相同屬性的事物抽離出來以達到分組,那麼低耦合就是降低各個分組之間的關係,這樣可以達到結構清晰,管理靈活。上面都是解釋字面上的意思,完全沒有扯到一點關於軟體方面的東西,那麼這兩個詞放在軟體設計裡面是怎麼體現的呢?

這個可以分為宏觀和微觀,宏觀上面,一句話:模組化,層次化。模組化和層次化是髙聚類和低耦合在軟體設計宏觀上面的體現,具體的又可分為縱向業務劃分,和橫向架構劃分(這也稱為二維架構,之前還聽過蔡學鏞老師說的四維架構這個就更加的抽象了)。將相同業務抽離到一個模組裡面,將整個業務線切分成多個層次,上層只能依賴下層,下層不能依賴上層的圖結構。從而使得整個軟體在結構上層次分明,業務線清晰。這裡舉個例子:比如我現在要做一個電商網站,首先要做的第一件事情不是探討技術上面是否有痛點,而是探討整個網站業務方向,從而確定整個業務模型,對裡面進行建模,分析裡麵包含的哪些領域模型。先不談複雜的電商,就說一個初期的C2C網站,首先它的業務方向是一個個人買家交易平台,一個使用者即可以扮演買家也可以扮演賣家,確定這個之後,我們再對裡面進行建模,從而可以進行模組劃分。上面的網站可以簡單劃分為商品模組,訂單模組,使用者模組等等,那麼這個模組就是在軟體架構裡面的髙聚類,那麼低耦合怎麼體現呢?模組劃分之後需要確定模組在整個系統裡面的定位,從而可以確定它們之間的依賴關係,以達到確定它們所處的層級。下面通過一個圖來描述上面三個模組的層次關係:

使用者模組依賴訂單和商品模組,訂單模組依賴商品模組,那麼可以確定的是商品模組是在整個系統正處於底層模組,而使用者屬於上層模組,那麼訂單模組不能反向去依賴使用者模組,商品模組也不能反向依賴訂單和使用者模組,這就是在架構設計上的分層,這樣做的好處就是系統的結構清晰,而且對功能的分層降級可以簡化業務的複雜度,並且可以達到對局部的最佳化不會對其他模組的影響,只要當前對外暴露的介面定義不變。那麼這樣分層之後,各個模組之間通過什麼來關聯呢?通過底層模組向上次模組之間暴露它內部的介面,比如商品暴露查詢商品的介面,那麼使用者就可以查詢使用者所上架的商品,訂單包含哪些商品,以及訂單暴露它的介面給外部,那麼使用者就可以查詢某個使用者所擁有的訂單。至於模組之間通過什麼方式通訊?如果是項目前期這些模組都放在一個工程裡面,這個就可以直接引用各個模組的介面實現,如果項目發展到最後,需要將各個模組獨立出來,單獨部署,那麼就需要一個遠程通訊的方案,這樣面有很多成熟的方案,比如比較通用的Webservice,效能比較高的RPC(這一塊有很多工具,比如最近聽到的跨語言的ICE(Internet communications engine),比如阿里系的dubbo和HSF,facebook的thrift),這就是軟體工程裡面的低耦合

上面說的是軟體設計宏觀上的髙聚類.低耦合,那麼細化到具體代碼實現上面來說,這個就是在實現某個邏輯的時候需要帶著這個思維去看待你的代碼,這就是微觀的髙聚類.低耦合。在開發過程中我們一直提倡提高代碼的重用性,這就是髙聚類,同時也要保持代碼的結構清晰,這就是低耦合。我在寫代碼過程中有一個潔癖,就是如果寫一段商務邏輯,類似的邏輯出現了3次以上,那麼我就和得了強迫症一樣,把這一塊抽離出來,通過對外暴露變數,以達到具體業務的需求。在Java裡面提供介面和抽象的功能,而合適使用抽象也是一種髙聚類的體現,我們一般把一些公用的,模板的商務邏輯放在抽象類別裡面,這樣就避免了其子類的複雜度,子類只需要關注它自己需要體現的業務,而一些通用的抽象父類提供了實現,這是一種在代碼的重用度方面的髙聚類。在具體細節中其他地方也體現了髙聚類特性,比如我們總喜歡把對某一類操作放在一個類裡面或者一個包裡面,比如對檔案的操作,我們可以放在一個FileUtil,這樣做是為了方便管理整個項目對檔案的操作,而不是東一個,西一個,如果某一天業務要求我們對操作檔案的時候加上某些東西,這個時候如果所有的操作都放在FileUtil裡面,面對這樣的變更,我們是如此的得心應手,否則那你去一個一個的檔案裡面找對檔案的操作吧!這也是一種高聚類的體現。那麼低耦合怎麼體現的呢?比如我們寫代碼過程中經常採取MVC模型,將資料展示層,業務層,以及資料存放區層分離開來,這就是一種低耦合的體現,各個層次分工明確,比如我最佳化資料存放區層,那麼上層的業務層基本無感知,這種現象就表明你的耦合度很低,而不是一切東西都雜糅在一起。

可擴充性

如果軟體開發設計一直遵循上面高聚類.低耦合,那麼一個軟體的可擴充性理論上是沒有什麼問題的。這裡的可擴充性是只在軟體生命週期裡面能夠適應和滿足業務的發展需求,在原有的架構設計上將新的業務可以實現平滑的嵌入,或者可以實現外掛程式式將一個業務直接嵌入當前架構裡面,如果一個系統真正實現了外掛程式化,那麼它的擴充性應該是相當好的。還是拿上面C2C的電商網站來舉例,之前只是滿足一個簡單的C2C的交易平台,可能隨著業務的發展,為了能夠吸引更多的客戶,需要推出搶購業務。如何更好的將這個搶購業務嵌入到之前的C2C系統裡面,這就要看之前系統的架構師的設計是否合理,如果為了滿足這個搶購業務,把整個項目都進行了重構,那麼之間的架構是很不合理的,如果能夠對之前架構進行很小的調整就能把這個搶購業務上線就說明之前的架構設計是很合理。當然這個過程除了分析搶購商務程序,也要分析當前系統的架構,通過業務的最佳化和架構關鍵因素的分析以達到最佳的實現方式。針對上面畫出模組依賴關係,可以大致分為兩個途徑去實現這個搶購業務。

第一、如果當前網站業務量很大,如果上了搶購,能夠刺激使用者參與度,可能需要單獨建立一個搶購模組,這樣就可以當搶購很火熱或者可能搶購業務因為流量太大,導致它的業務整體癱瘓了,那麼普通正常的業務依然能夠運行,因為你已經將整個搶購業務獨立出去了,它只是會通過調用使用者模組,商品模組和訂單模組介面來執行相關的商務邏輯,同時也可以單獨對這個模組進行最佳化,比如分析搶購模組是一個期間很短,資料比較集中,對某個資料的修改比較集中的操作,可以將資料的整體操作放在緩衝(這是大部分電商對搶購業務常用的方式),那麼這樣就可以降低對資料庫I/O的壓力,並且提高搶購模組的系統輸送量。這種實現方式可以降低系統的耦合度,也使得搶購業務以及整個系統的擴充性上面有所提高,但是對系統維護來說增加了一項工作以及對開發工作量來說有所增加了,因為一個新的模組建立畢竟也是重新開始的。

第二、如果當前網站流量還不是很大,只是想通過搶購來擷取更多的使用者,那麼可以將搶購業務嵌入到原有的架構模組裡面,而無需通過建立模組來達到目的。以我以前參與過的項目來說說這種方式的實現方案,由於我之前負責的一個電商網站流量不是很大,為了提供使用者的參與度以及擷取更多的使用者,需要上線搶購業務,但是又不想能夠快速上線,所以在商品模組對商品添加了一個搶購屬性,並且設定該商品的搶購時間區間,那麼這樣就可以簡單的將搶購業務簡單的嵌入到整個系統裡面。當然這個前提是你的業務量不是很大,不然上線一個搶購業務可能會將你整個系統弄得癱瘓,那個時候你再將搶購獨立出來,那就是無數日夜的加班熬夜了。

可行性

這個是體現上面所有工作做的是否有價值,如果你的架構很好,你的架構完全具備了髙聚類.低耦合,也具備了可擴充性,但是完全是異想天開,不具備可行性,這隻能說這是一種YY的架構。我認為的可行性可以分為:在滿足上面髙聚類.低耦合,可擴充性的條件下,能夠讓業務在這個架構上跑起來,以及整個系統是一種高效率的執行,整個系統在部署上可以輕鬆實現水平擴充以支撐後續的業務發展。那麼將結合這三點來說說可行性。

業務跑起來

這個應該是一個系統設計的最基本的要求,因為這是設計的初衷。所有的架構設計應該是不能脫離業務去異想天開,合適圍繞業務,站在業務的角度去設計當前系統,當然這裡需要設計者對業務有深入的理解,而且和業務方的理解達成一致,這樣設計的軟體最後才能通過UAT。這就好比寫作文,要緊貼標題,不然和跑火車一樣,跑題跑到十萬八千裡,這樣的作文,你把李白,杜甫哥倆搬上來也是白搭。

高效

這個也可以通過宏觀和微觀去看待,宏觀方面各個模組之間的通訊協定,比如webservice雖然可以跨語言,但是它的效能確實不敢恭維,或許RPC更高效點。同時也可以通過梳理簡化商務程序提高整個系統的執行效率,這就好比尋求一條最短路徑一樣,減少業務的流程對提高系統執行效率比任何方法都見效,因為它最佳化的不是一次通訊採取什麼協議,某段實現採用什麼演算法,而是對整個業務線進行抽象。那麼微觀的最佳化就涉及到代碼的具體實現了,比如一段商務邏輯的實現,避免沒必要的迴圈,採用更高效的演算法,減少一個變數的定義,減少記憶體的使用等等這些都是在具體細節方面的讓系統更加的高效執行。

部署的水平擴充

要說這個就要說到分布式叢集部署了,一談到這個或許真的談半天都說不完。圍繞這個,在軟體開發過程的每個細節都有很多內容。這裡我只是簡單的描述一下,一個系統要達到可叢集部署的要求,一定要達到系統的無狀態性,什麼叫無狀態?就是不能依託單個服務實體狀態來支撐業務的執行,比如通過將一使用者資訊緩衝到伺服器記憶體,比如你將任務放在本地的記憶體中,這些地方都是叢集部署的障礙點。

舉一個簡單的例子:在標準的J2EE裡面或者是標準的web開發,每次使用者的訪問都會有Session的概念,如果你的Session是依託單個服務實體,那麼該系統要進行叢集部署,這個使用者資訊同步就是一個問題,因為你進行叢集部署,使用者的請求具體是落到哪個伺服器上,完全是有前端的負載平衡器(nginx,apache或者F5)決定的,如果使用者登入是在A伺服器上執行的,你將使用者登入態儲存在A伺服器的session裡面,那麼下次使用者的請求可能落到B伺服器上,由於你將session放在了A伺服器上,而B伺服器沒有使用者登入資訊,這個時候使用者可能就不能完成一次請求,這對使用者來說不能理解,因為他請求的就是你們系統,他並不知道你具體是把他的請求落在你們哪個伺服器上。針對這個問題,業界有很多解決辦法,有依託容器的session同步,有通過第三方快取服務實現session共用或者CAS的單點登入方案。個人比較偏向通過第三方快取服務實現session共用方案,因為通過容器來實現session同步,那麼就對系統跨平台增加了一種限制條件,只能在具體的容器下面才能運行。這裡就簡單描述一下session共用怎麼來做到的,session共用是將使用者的登入資訊不儲存在具體的伺服器上面,而是儲存在所有伺服器都能訪問的第三方緩衝裡面(redis或者memcache,當然資料庫也可以只是效能稍微不能接受),這樣依賴所有的伺服器均從第三方緩衝裡面擷取使用者的登入資訊,這樣使用者的登入請求不管是落到哪台伺服器上,所有的伺服器都能感知到,這樣不管你後續的請求落在哪台伺服器上,伺服器都能擷取你的登入態。使用者登入態是系統叢集部署首先需要解決的問題。

上面從三點描述了軟體的可行性,但是一個系統,可以說一個大型系統可行性遠遠不知從這三點來分析和最佳化。針對不同地方有不同的最佳化手段:

  1. 針對提高使用者訪問速度,可以採用CDN和反向 Proxy,將我們的資料放在離使用者更近的地方
  2. 針對頁面渲染速度,可以通過對JS,CSS以及HTML頁面壓縮或者提高頁面載入的並發數來進行最佳化
  3. 針對提高系統的輸送量,可以在部分環節採取非同步MQ方式進行業務的執行
  4. 針對降低資料庫的壓力,可以通過兩級緩衝(本機快取和分布式緩衝)的方式來最佳化整個資料的查詢
  5. 針對提高系統的處理能力可以進行並發處理,從而提高系統的執行效率
  6. 針對提高每台伺服器的使用率,可以使用負載平衡,使得伺服器資源利用充分
  7. 針對提高系統的容災能力,系統需要一定的冗餘,一套系統需要具備一個後備環境,這裡要說的東西也很多,舉個簡單的情境,當切到後備環境,應該先進行緩衝預熱,否則可能切到新的環境,由於緩衝沒有資料,導致緩衝的命中率很低,導致巨大壓力全部落到了資料庫,會導致整個資料庫瞬間爆棚。
  8. 針對提高資料庫的效能,需要進行資料庫的分布數部署,一主一從或者一主多從甚至多主多從進行資料庫端的最佳化

上面聊了關於系統可行性方面的討論,上面只是將我知道的方案進行了一次總結,但不一定全部都實踐過。要知道能夠將上面都實踐過的只要大型公司才具備這樣的機會(比如像BAT這樣的公司才會有很多這樣的機會,但不是所有人都能參與進去),所以只是有感而發。

可維護性

上面探討的大部分是軟體架構和開發設計過程的問題,而一個系統的發展不能是一個人或者一個團隊一直去開發和維護,那麼要使得一個系統能夠在不同人的手裡運作的更好,這需要從系統初期就規定開發規範,開發流程以及開發文檔的整理和搜集。使得整個軟體在後續的交接過程中能夠平緩的過渡,而不需要過多的“進入狀態”的過渡期。一個系統的可維護性是小到一個普通開發,再到開發主管,大到首席架構需要在工作過程中去關注的事情,只有一直關注這件事情,整個系統的品質才能經得住時間的考驗。這就是我理解的可維護性

軟體開發流程

其實這裡最能體現軟體工程的思想,上面大部分都是軟體設計架構方面。一個軟體開發過程一般遵循一下幾個流程:

  1. 產品確定業務需求產出PRD檔案
  2. 產品,開發,測試以及互動進行需求評審
  3. 通過之後那麼開發,測試以及互動同時進行產出設計文檔,測試案例以及UX
  4. 接下來是對上面三個進行評審設計評審,用例評審,UX互動評審
  5. 進行完之後那麼接下來就是開發階段
  6. 開發完畢體測,進入系統煙霧測試 (Smoke Test)
  7. 系統功能測試
  8. 系統迴歸測試
  9. 系統UAT測試
  10. 系統部署上線

有些公司在第10步分為兩部分,分為預發布和線上,這是為了提高系統上線的整體品質,預發布環境的條件是資料以及環境和線上一樣,只是系統是用最新分支的代碼運行,部署預發布之後將會進行預發布驗證,如果驗證不通過則打回給開發修複,再次進入第7

有些人認為軟體工程其實就是這些流程化的東西,如果這麼認為那理解就有點太過於局限於工程這個概念了。其實軟體工程的目的就是使得軟體開發的過程以及系統後續反覆式開發法能夠像當前硬體發展一樣,通過提供統一的介面,只需要將自己喜歡的配置組合在一起就能得到滿足自己需求的硬體裝置。那麼要使得軟體開發成為這樣的模式,必然會涉及到開發每個細節,以及開發的整體過程當中,不僅僅是流程化的東西,而是系統的架構模式,代碼的實現風格,以及系統上線部署維護方面,所以軟體工程是一個比較大的話題,可能隨著工作經驗的累計有不同的理解,或者不同的崗位有不同的理解。可能今天我理解的軟體工程是這樣的,過了一到兩年,可能會認為我現在的認識是錯誤的。我現在對軟體工程的理解是開發過程所設計到的一切事物都是軟體工程的一部分,開發過程中一切產出都是軟體工程的目的。

最後我舉個例子,也是前幾天以為網友向我諮詢過的問題,他現在有一個系統需要改造,他系統主要做的事情就是接受第三方通知,然後再將結果通知給另一個第三方系統。下面划出簡單的一個系統拓撲圖:

整個通訊過程是第三方系統A將處理結果告知轉寄系統,然後轉寄系統將結果告知第三方系統B當作任務放入本地一個隊列中,然後有一個線程專門處理這個隊列的任務將結果告知第三方系統B。這種架構存在什麼缺陷呢?我下面列舉一下:

  1. 存在單點過障,如果流量增加單台轉寄系統可能根本處理不過來整個業務
  2. 存在叢集部署的障礙點,轉寄系統是將任務放在本地的記憶體裡面,如果叢集部署分擔壓力,那麼在原生任務其他伺服器根本無法感知導致無法分擔壓力
  3. 沒有很好的模組劃分,將接受系統A的和轉寄系統B的代碼全部雜糅在一起,導致後面對系統進行擴充很難
  4. 存在資料丟失情境,如果系統上線新版本,服務重新部署,那麼緩衝在記憶體裡面的任務如果擠壓很多一時處理不完,那麼重啟必然導致記憶體裡面的任務資料丟失,導致不能再通知第三方系統B

針對上面幾個問題我提出了下面的解決方案,先看看改進後的拓撲圖:

將原來系統拆分成兩個模組,一個是接受處理結果模組通知處理結果模組,然後這兩個模組之間通過非同步MQ來進行通訊,現在的執行流程是:第三方系統A將處理結果發送給轉寄系統的前置Nginx,Nginx將請求通過負載平衡到後端具體伺服器上,某台伺服器即受到處理請求之後,將結果受理並執行相關業務,然後將通知任務放入MQ的訊息佇列中,這個時候可以立馬響應結果給第三方系統A,這個時候第三方系統A通知處理結果就處理完畢,最後再就是通知處理結果模組由於監聽了MQ的訊息佇列,那麼如果有任務訊息過來,將會接收到,此時將結果發送給第三方系統B。這個時候接受處理結構模組的輸送量增加了,處理能力也增加了,因為將任務不再是緩衝在本地,而是放入MQ中,那麼它是無狀態的,可以隨意水平擴充,從而可以應對高並發的業務處理。而通知處理結果模組由於是監聽MQ中的訊息佇列,業務壓力上來並不會給它帶來壓力,它依然按照它的節奏在處理任務,如果想讓處理速度快一點,可以在叢集中多部署幾台機器來解決,這樣使得整個系統的水平擴充方面得到了很大的提高,也使得整體系統的處理效率以及系統結構得到一定的最佳化。

可能有人會說,我這樣把一個簡單的系統拆分成這麼多模組,把整個項目弄得複雜了,需要維護和管理這麼多模組,同時還加了一個MQ的維護。當然這麼拆分的前提條件是你的業務量上來了,或者為以後做準備,如果你現在的系統一天才處理不到一萬個請求,我也建議沒必要這麼搞。但是如果作為一個可以持續發展的項目,前期很好個規劃可以很好的避免後面的大規模重構。上面這種方案對於即時性要求很高的系統也不是很適合,因為MQ是非同步,所以存在一定的延遲,所以對於允許有一定延遲的業務可以採用MQ來提高整條業務的響應速度,以達到業務處理的輸送量,當然這裡所指的延遲並不是延遲幾個小時,這種延遲一般不會超過一分鐘。

在我們現實生活中這這裡模式也很多,比如我們刷銀行卡,然後我們手機立馬會收到銀行一個簡訊通知我們有一筆消費,一般從你刷卡到你收到簡訊不會過太久就會收到簡訊,這個過程總就涉及到非同步處理。它這裡的流程一般是這樣的:一般你刷卡處理的後台系統是銀聯(當今中國線下清結算老大,線上你們都應該知道),銀聯通知對應銀行對某張卡進行扣款(裡面具體的操作我也不太清楚,一般做法是銀聯通知對應銀行將錢轉到銀聯的一個帳號上,然後商戶和銀聯進行結算),銀行將扣款結果通知給銀聯,這個時候銀行也會將一條簡訊儲存在一個類似訊息中心中,並有專門的系統處理這些訊息去通知使用者,銀聯在返回給POS機,最後POS機出票,這個時候手機也會收到一條銀行簡訊。在這個過程中銀行產生簡訊並不是立馬去發送,而是放入訊息中心(這個類似於一個MQ),讓其他系統非同步去做,為什麼要這麼做呢?因為傳送簡訊並不是業務主線內容,如果扣款和傳送簡訊放在一起同步去做,那麼會導致整個業務響應很慢,也可能由於簡訊發送失敗導致整個業務失敗,因為傳送簡訊誰能保證百分之百的成功,而且誰能保證百分之百的快速響應?所以將這種附加在主業務分支上的業務通過非同步執行來提高整條業務的成功率和處理效率,這也是企業級系統開發常用的手段。

以上是我所認識的軟體工程,也不知道最後表達了什麼思想,只是總結了一下到如今我對軟體開發的一個認識。以上也是我主觀的認識,並不具備參考意義,還望不要誤導大家。

論軟體工程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.