互聯網架構-感受

羅馬不是一天建成的,架構也不是一蹴而就的,需求-重構-上線不斷的循環纔有造就了架構之美或者架構之殤。

從事it開發工作已經8個年頭了,參與10多個項目的開發,主導數個互聯網項目的架構設計,主要是電商或者電商相關的項目,從開始的無從下手到現在的輕車熟路,過程磕磕絆絆,所幸都沒有夭折,基本上順利上線。讀過一些架構相關的書籍,書中的架構的思路,實現過程和方式方法和自己架構設計的過程差別很大(我都在懷疑是否接觸的層次太低了,哈哈),所以就想把自己的一些經驗整理一下,整理總結思路,做爲一種沉澱,另外一方面也可以和大家互相交流學習,知道自己的不足纔能有更大的進步。


1、什麼是架構設計

“架構設計是人們對一個結構內的元素及元素間關係的一種主觀映射的產物。架構設計是一系列相關的抽象模式,用於指導大型軟件系統各個方面的設計。” 來自百度百科。資料中的定義是準確、完備和書面化的,仍然很難理解架構設計的本質。通俗的描述,架構設計就像是小學考試中解答應用題的過程,但是解決的問題更復雜,構思設計的過程更龐大,解題的工作量更大。

2、項目的質量指標: 功能,性能和擴展性

軟件開發的最終目標是使用代碼去實現抽象的業務邏輯,可以從3個方面衡量:功能,性能和擴展性。

功能:功能目標是應用的基本要求,如果不能實現既定的功能邏輯,應用就失去了存在的意義,因此實現產品需求是應用的基本的目標。

性能:在基本的功能之上,會有一些性能的要求,但是很少有產品經理或者用戶能提前提出這樣的要求,因此架構師要有豐富的經驗去發現和解決(或者爲未來提升性能做準備)性能問題。性能的主要衡量有:單次請求的相應時間,單實例請求併發數,服務最大併發量等。

擴展性:目前互聯網應用的開發模式:快速響應,迭代開發;提出需求,快速相應,儘快上線,是騾子是馬拉出來溜溜。所以這就要求系統的架構設計要更好的響應新的需求和需求變更。

3、架構設計的主要過程

3年多來,數個項目的架構經驗,我自己的架構設計過程是 : 確定問題域,數據建模,模塊劃分,關鍵流程描述,技術選型,代碼實現,驗收測試

3.1、確定問題域

記得小學考試後拿到老師改過的卷子,對着一個個的大紅叉都會懊惱:”哎,又看錯題目了“。錯誤的方向危害大於錯誤的方法,沒有找對方向,項目就會南轅北轍,遠遠偏離目標。來自產品經理或者用戶的需求描述就是我們的問題域,但是來自於產品經理的需求描述會比較全面,內容也很多,來自用戶比較簡單,相對比較模糊。比如電商項目的需求文檔會非常大,拿到一個幾十上百頁的需求文檔(有程序員拍磚說,我家的產品只一段話”像XXX網站的功能copy一下吧”,哈哈)時,往往不知道從何處下手,所以我們要從繁雜的問題域中找到關鍵問題。

a、用戶可以在我們的網站上購買XXX商品。

從a條出發,又延伸出來幾個問題:

b、用戶訪問:用戶登錄,註冊

c、商品來源:商品的管理,增刪改查等

d、交易的過程:訂單的管理等

從d交易出發,又能延伸出來

e、用戶付錢:支付

f、商家配送:收貨地址,配送流程

不斷的展開問題域,就可以把整個流程轉起來。當然實際應用的時候我們不會把所有的問題域都總結出來,確定了關鍵問題就可以開始數據建模了。

上面我們分析的是功能問題域,這會也需要確定一下性能和擴展性的問題域。性能的問題域應該是針對關鍵路徑確定的,比如商品瀏覽,訂單創建,訂單支付等。針對於這些關鍵路徑問題,可以定義一些問題域,比如單實例支持1000pv/秒商品瀏覽,100單/秒的訂單提交等。

擴展性是最難把握的,因爲每個人經歷不同,針對同樣的項目會對未來需求有不同的預期,因此怎麼把握當前的功能和未來的變化,如何平衡性能和擴展的關係,是架構師設計的關鍵。以我的的經驗來看,擴展把我關鍵問題,優先滿足關鍵問題的性能,確定最小功能集。確定最小功能集的優勢可以快速實現,快速驗證需求的準確性,每次需求開發都完成最小和最關鍵的需求。設計的時候要滿足一些思想和原則,OOP(面向對象設計)原則:1、單一職責原則;2、開放閉合原則;3、里氏替換原則;4、依賴倒置原則;5、接口隔離原則;數據庫設計三範式等等。擴展的問題域也可以參考友商或者與有經驗的產品運營溝通,大致瞭解存在的擴展性。電商項目可能會有:搶購,預定,團購等業務都是電商的一些擴展需求。

3.2、數據建模

確定了問題域就可以開始答題了,確定數據模型。大部分的應用基本使用的仍然是關係型數據庫,所以我們針對問題域先創建數據表,當然也存在一些項目使用NoSQL存儲或者不持久化數據,這裏確定的就是問題域的實體類。3.1中描述的問題域每個名稱都是一個數據表(或者實體對象),用戶,商品,訂單,支付流水,收貨地址,配送單。

我比較喜歡使用powerdesigner做數據庫模型,可以直觀的看到表結構,方便修改,可以生成大部分DB的DDL SQL。爲上面找出的名詞(實體結構)創建表結構,然後根據產品需求文檔一條一條的閱讀判斷,當前表結構是否可以滿足需求,如果不能滿足,在表中添加列或者添加新的表來滿足此需求,不斷的去豐富表結構直到完全滿足需求。當然在建模的過程中也會調整原來的表結構,畢竟不斷的增加需求,會引起數據模型的變化,所以最初建立的肯定不完整,不斷調整直到滿足所有需求。

數據建模時的幾個心得:

a、數據表包含自增id,創建時間createTime,更新時間updateTime和版本號version


自增的id:主鍵,根據id查詢或者更新時,速度畢竟快。

createTime和updateTime記錄創建時間和最後的更新時間,排查問題的關鍵點

version:編輯時version++,是一個很方便的樂觀鎖,能比較大的提升數據庫的性能

b、不使用外鍵,這點有一些和數據庫設計的規範相悖,但是這是來自真實經驗總結,外鍵約束帶來的數據完整性的優勢遠遠小於更新邏輯實現的難度。從性能和擴展性來看不使用外鍵也是利大於弊,大數據高併發大流量的互聯網應用提供性能的常用方法是:提高數據庫的訪問速度,緩存數據,數據庫分庫分表支撐高併發等,外鍵是對這些方法的一個制約。

c、不使用id作爲表關聯,雖然我們不創建外鍵約束,但是不代表表之間沒有關聯關係。所以表之間仍然會有外鍵,但是沒有外鍵約束,設計這個外鍵的時候要考慮數據的增長型,數據沒有確定的規模,那麼參考增長的速度,我們可以設定一個未來3-5年的數據規模,如果單表不能滿足,則數據存在分表的可能性,那麼表間的關聯使用全局的唯一id的方式一個更好的選擇。全局唯一id的方式有很多算法,使用數據庫(oracle的sequence和mysql的自增id,這裏是爲生成id的特殊表的自增)生成是一個比較好的方式,當然也可以使用組合方式添加數據類型,時間,地域等方式,也有使用uuid算法計算的方式,只要可以滿足不重複的特點,選取那種方式可以參考一下產品的意見,因爲這個字段用戶可能感知。

d、表有沒有多少列的標準?記得剛開始做設計的時候,經常懷疑自己是不是分的表太多或者太少,太大了是不是會影響性能,太少了是不是有點畫蛇添足。應用最初的設計最符合設計原則和設計思想的,沒有收到工期,團隊分割,實現難度等非設計因素的影響,所以我們應該儘量的堅持最初的設計,克服其他因素的影響。

e、冗餘字段是否有必要?我的做法是不使用,保持原有的設計,如果系統真的流量比較大,查詢性能太低,可以通過把讀服務從業務系統中分離(這裏要注意不是數據庫的讀寫分離)。從業務上把讀寫分開,做一些便於查詢和提高性能的設計,通過一些數據抽取方式同步數據。這裏會有人提出異議,這樣做會導致用戶的讀延遲,其實展示性數據對數據的延遲是有很大的容忍度的,只有業務系統需要做到數據的一致,那麼業務系統對數據的讀取是針對性的,很少會出現需要很多關聯數據的情況,所以最初設計系統時儘量少的使用冗餘字段去提供查詢的方便性和性能。

以下是電商應用部分表(商品和訂單業務相關)設計,僅供參考:

jUFfYb7.jpg!web

3.3、模塊劃分

如果數據模型主要爲滿足功能目標的話,模塊劃分會比較多的兼顧性能和擴展性,常用的互聯網應用的模塊劃分和部署結構有如下幾種(這裏討論的劃分和部署是互聯網應用的服務器端),不考慮瀏覽器和APP等客戶端,當然介紹的幾種也是常用的結構。

單實例結構:

E7JnymE.png!web

優勢: 結構簡單,便於開發部署

劣勢:可能存在性能瓶頸,擴展性差,系統耦合性高

集羣結構:

qqyii2y.png!web


備註:db層作爲整體描述,可能存在單DB,讀寫分離,分庫分表或者數據cluster等技術

優勢:性能大大提供(理論上可以無上限)

劣勢:負載存在平衡的可能,仍然會存在性能瓶頸,擴展性差

分佈式結構:

3IBBzay.png!web

分佈式系統是把不同的業務切分到不同的實例中,功能相關的聚合到同一個實例中,系統間使用網絡協議通信的一種結構。

優勢:擴展性強,高內聚,低耦合

劣勢:結構複雜, 事務控制難度大,開發工作量大

混合結構:

FNVVZza.png!web

混合結構是分佈結構基礎之上每個模塊又實現集羣結構,所以這個模式放大了分佈式結構的優勢和劣勢

優勢: 擴展性強,高內聚,低耦合,健壯性高

劣勢: 結構複雜, 事務控制難度大,開發工作量大

大部分的互聯網應用的結構都可以用上面的4個結構描述,當然這裏只是簡單的描述,一些應用爲了提高數據庫訪問會加上DB緩存,爲了提高頁面的訪問速度做頁面靜態化和CDN,爲了應對大數據的存儲和檢索使用NoSQL數據庫等,但是我們設計的結構是不受影響的。如何選擇系統結構,可以從如下幾方面考慮:

1、系統是否能滿足未來2-3年的增長,如果採用混合結構的系統工作量要遠遠大於單例結構的系統,對初創企業來說,上線纔是最大的需求,所以拼速度的時候就要放棄優雅。如果公司有一定的規模,開發的應用是核心業務或者未來的核心業務,採用擴展性強的混合結構應對未來的快速發展的業務需求是一個更好的選擇。

2、人力因素,混合結構要比單實例的結構工作量增加很多,並且對團隊整體的技術水平有較高的要求,所以要”量力而行”。

3、時間因素,很多互聯網公司的工期不是技術評估的,是由”市場”確定的,所以火燒眉毛的時候就別講究性能和擴展了,上線再說。

4、架構不是一成不變的,不斷增加的訪問和不斷變化的需求改變着系統的架構。快速響應,不斷迭代纔是互聯網應用的方式。所以最初的架構,儘量的做到高內聚低耦合,這樣不斷的提高系統的短板,逐漸完善系統結構。

分佈式結構電商模塊描述:

Yjqimy3.png!web

顯示層:

前臺:用戶端界面顯示層,依賴用戶服務,商品服務,交易服務和支付服務

後臺:運營端界面顯示層,運營人員管理各種數據的界面。 依賴用戶服務,商品服務,交易服務和支付服務;

服務層:爲界面提供RPC服務

用戶服務:註冊,登錄,用戶管理等

商品服務:商品瀏覽,庫存展示,商品管理,庫存管理等

交易服務:購物車服務,訂單計算, 訂單提交,訂單列表等

支付服務:生成支付鏈接,支付成功跳轉,支付成功邏輯處理等

基礎組件:

DB:數據存儲

Redis:使用redis實現用戶狀態session機制,便於將來集羣部署;實現購物車功能,用戶購物車服務端持久化,便於用戶跨瀏覽器購物車管理。

第三方支付組件:用於與第三方支付服務交互


3.4、關鍵流程描述

關鍵流程描述是檢查系統架構是否滿足需求和指導開發的必要條件。關鍵流程描述是使用流程圖解決關鍵問題的過程,它的使用者是團隊其他成員和自己,所以格式不重要,其他人能明白就好。

一些書寫的經驗如下:

1、有始有終,流程應該是從用戶進入應用開始到離開應用的完整過程,比如交易的過程,應該從用戶開始瀏覽商品到用戶支付成功這一個過程。

2、流程圖突出重點,比如上面舉例的交易過程,應該突出交易相關的流程判斷,不必描述用戶註冊,找回密碼等過程。

3、簡要說明,避免過於詳細,比如交易的過程中需要更新庫存,但是不需要描述更新庫存前的庫存校驗這些是提交訂單的內部實現。

示例如下:

EbyAJzZ.jpg!web


3.5、技術選型

1、如果非必要請使用常用的技術,框架等,常用技術和框架使用者多,所以會比較少的遇到非業務問題。曾經參與的一個項目,其中一個模塊由一個比較熟悉python的同學負責,系統剛剛上線,因爲一些原因要離職,沒有人可以接下來,只好找其他的語言重新開發了一遍。

2、熟悉的優於強大的,儘量採取團隊比較熟悉的技術或者使用團隊中有人可以指導的技術。記得5年前爲甲方公司做一個需求和Bug跟蹤的工作流的系統,輕率的決定使用JBPM,因爲團隊中沒有人研究過,所以花了大量的時間使用這個框架,最後也沒有很好的使用,導致項目步履蹣跚。


3.6、代碼實現

代碼首先是給人讀的,其次纔是給機器讀,所以良好的代碼結構是項目存活更長時間的良藥。

1、代碼分層:功能單一原則,mvc是互聯網應用的一種基礎模式,從功能層次上劃分爲,v顯示層,c控制層,m業務層。以Java實現業務分層如下:

uUjMzuZ.png!web

自上而下的層級,

controller:頁面控制層,用於頁面出參入參轉換和頁面跳轉

vo:貧血實體對象,用於頁面和業務層的數據傳輸

bo:業務實現層

dao:數據訪問層,用於處理與數據庫的交互

po:貧血實體對象,用於dao與數據庫傳輸,和數據庫表列意義對應

2、命名規範

代碼中使用的類,方法,變量,參數等,採用統一風格命名,英文或者中文拼音,駝峯或者下劃線分隔,儘量採用業界常用風格。類,變量和參數採用使用名詞,方法使用動詞等。

3、註釋風格

採用統一的註釋風格,方法內一般採用行註釋,其他地方採用段註釋。

3.7、驗收測試

積極配合測試團隊對項目的測試,他們是爲項目健康上線保駕護航的人,不是挑刺的人。

4、總結

1、同樣的題目有多種解法,我們做的只是其中的一種,所以要接受別人的質疑和建議,這樣才能使系統完善。

2、沒有銀彈,沒有解決一切問題的方法,那麼也不可能使用一種方法解決所有問題, 所以要根據需求,團隊,時間等選擇合適的方式方法。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章