一個 App 服務(wù)端架構(gòu)改造升級之路
各位肯定都聽過這樣一句話 : "好的架構(gòu)不是設(shè)計出來的,而是演進出來的,沒有完美的架構(gòu),只有不斷演變、不斷完善的架構(gòu)。" 今天我們來看一下1 號店 App 服務(wù)端架構(gòu)改造的例子,來具體說明架構(gòu)的演變過程,讓你能更深入地理解架構(gòu)演變背后的原因。
隨著智能設(shè)備的普及和移動互聯(lián)網(wǎng)的發(fā)展,移動端逐漸成為用戶的新入口,各個電商平臺都開始聚焦移動端 App。這個時候,1 號店也開始試水移動端購物,從那時起,1 號店 App 的服務(wù)端架構(gòu)一共經(jīng)歷了三個版本的變化。接下來,我就為你具體介紹 App 服務(wù)端架構(gòu)變化的過程以及原因。
一、V1.0 架構(gòu)
我先說說最開始的 1.0 版本。當(dāng)時的情況是,App 前端的 iOS 和 Android 開發(fā)團隊是外包出去的,而 App 的服務(wù)端是由 1 號店內(nèi)部一個小型的移動團隊負責(zé)的,這個團隊主要負責(zé)提供 App 前端需要的各個接口,接口使用的通信協(xié)議是 HTTP+JSON。具體的架構(gòu)如下圖所示:
這個架構(gòu)比較簡單,App 的服務(wù)端整體上就一個應(yīng)用,由移動團隊來維護所有對外接口,服務(wù)端內(nèi)部有很多 Jar 包,比如商品搜索、商品詳情、購物車等等,這些 Jar 包包含了各個業(yè)務(wù)線的業(yè)務(wù)邏輯及數(shù)據(jù)庫訪問,它們由各個業(yè)務(wù)線的開發(fā)者負責(zé)提供。
你可以看到,這個 1.0 版本的服務(wù)端,實際上就是一個單體應(yīng)用,只是對外的接口和內(nèi)部 Jar 包分別由不同的團隊來提供,這個架構(gòu)的優(yōu)點和缺點同樣都非常明顯。
它的優(yōu)點是簡單方便。 App 前端的外包團隊只需要對接后端的一個移動團隊就可以了,然后移動團隊通過現(xiàn)成的 Jar 包,封裝各個業(yè)務(wù)線的功能。至于這些 Jar 包,業(yè)務(wù)線團隊也無需額外去開發(fā)。
為什么呢?我們知道,早期的電商平臺都是先有 PC 端應(yīng)用,再推 App,App 最開始的功能,大多是從已有的 PC 端平移過來的。因此,這些 Jar 包直接從 PC 端應(yīng)用里拿過來就可以了,如果 Jar 包版本有更新,由業(yè)務(wù)線團隊直接同步給移動團隊即可。
那這個架構(gòu)設(shè)計是不是很完美?。慨?dāng)然不是,不知道你發(fā)現(xiàn)了沒有,其實這里也存在了很多問題。
第一個問題:移動服務(wù)端對 Jar 包的緊密依賴
移動團隊負責(zé)對外接口,但他們非常依賴業(yè)務(wù)團隊提供的 Jar 包來實現(xiàn)業(yè)務(wù)邏輯,這是一種物理上的緊耦合依賴關(guān)系。如果業(yè)務(wù)團隊根據(jù) PC 端的需求,修改了應(yīng)用代碼后,Jar 包也會隨之修改。那么在實踐中,經(jīng)常會出現(xiàn)這樣的情況:業(yè)務(wù)團隊很多時候,要么忘了同步新的 Jar 包給移動團隊,要么是新的 Jar 包調(diào)整了類的接口,導(dǎo)致了 App 服務(wù)端的功能有問題,或者直接不可用。
第二個問題:移動團隊的職責(zé)過分復(fù)雜
服務(wù)端為 App 提供的是粗粒度接口,而業(yè)務(wù)團隊的 Jar 包提供的是細粒度的接口。
因此,移動團隊在 Jar 包的基礎(chǔ)上,還需要做很多的業(yè)務(wù)邏輯聚合,很多時候,這些邏輯還跨多個業(yè)務(wù)線,導(dǎo)致移動團隊對所有業(yè)務(wù)邏輯都要深入了解。相信你也知道,這是很難做到的。
第三個問題:團隊并行開發(fā)困難
由于移動團隊和業(yè)務(wù)團隊是通過物理 Jar 包進行集成的,移動團隊直接受業(yè)務(wù)團隊的代碼影響,就導(dǎo)致了團隊之間并行開發(fā)困難,一次大的 App 升級經(jīng)常需要 2~3 個月的時間。
而當(dāng)時的 1 號店,需要能盡快地推出 App 端,我們所有的做法都是圍繞這個目的來的,包括把前端團隊外包出去,后端采用單體架構(gòu),移動端功能從 PC 端直接移植過來。所以,從當(dāng)時的情況來說,這種簡單的服務(wù)端架構(gòu)和團隊合作模式是非常合適的。
而過了一段時間,當(dāng)移動端的功能已經(jīng)初步具備,我們就需要針對移動自身的特點去組織功能,并能夠快速上線這些新功能。那么,這種單體架構(gòu)加物理 Jar 包耦合的方式,就成為 App 進一步發(fā)展的瓶頸。接下來,我們就看下系統(tǒng)是如何通過架構(gòu)升級,來解決這個問題的。
二、V2.0 架構(gòu)
到了 2013 年,1 號店 App 服務(wù)端架構(gòu)升級到了 V2.0。在這個時候,1 號店自己接手了 App 前端的開發(fā)工作,同時,服務(wù)端接口也由各個業(yè)務(wù)線團隊直接負責(zé),這樣,App 前端直接對接多個后端應(yīng)用提供的 HTTP 接口。
整體架構(gòu)如下圖所示:
對于各個業(yè)務(wù)團隊來說,他們現(xiàn)在走向了前臺,每個團隊負責(zé)各個業(yè)務(wù)線的 App 接口。他們一般采取這樣的做法,一方面,他們以 Web 應(yīng)用的方式,為 PC 端瀏覽器提供訪問;另一方面,針對移動端的訪問需求,他們在 Web 應(yīng)用里面,增加了一些 REST 接口,直接供 App 訪問。在這里,移動接口和 Web 應(yīng)用在同一個工程里開發(fā),作為同一個應(yīng)用進行部署和運行。
這里你可以看到,這實際上就是一種分布式的系統(tǒng)架構(gòu),每塊業(yè)務(wù)由不同的團隊負責(zé),可以很好地支持團隊之間的并行開發(fā);同時,移動接口和 PC 端共享底層業(yè)務(wù)邏輯,有助于快速把 PC 端的功能完整地復(fù)制到 App 端。
這樣,通過 V2.0 架構(gòu)的升級,業(yè)務(wù)線團隊的生產(chǎn)力就被完全釋放了,App 的功能也就快速豐富起來了。
但這種方式也帶來了一系列的問題,我們具體說下。
1.首先是移動端和 PC 端互相干擾的問題。
你可以看到,在同一個業(yè)務(wù)線內(nèi)部,移動接口和 Web 應(yīng)用,物理上是綁定在一起的。很多時候,PC 端的代碼修改會影響到移動接口,而 Web 應(yīng)用的發(fā)布,也會導(dǎo)致移動接口被動地被發(fā)布,如果 PC 端出現(xiàn)功能問題,也會影響到移動接口的可用性。反過來也是一樣的,移動接口的需求變化,會影響到 PC 端的功能。
我們知道,當(dāng)移動端發(fā)展到了一定程度,它需要和 PC 端有不同的功能和用戶體驗,但這種緊耦合的方式,導(dǎo)致了相互之間產(chǎn)生很多不必要的干擾,對系統(tǒng)的功能和穩(wěn)定性都帶來了負面影響。
2.其次是重復(fù)開發(fā)的問題。
移動接口除了要給 App 端提供業(yè)務(wù)數(shù)據(jù),還需要考慮一系列系統(tǒng)級的功能,比如說,安全驗證、日志記錄、性能監(jiān)控等等,每個移動接口都需要這些通用功能。
那現(xiàn)在,由于 App 前端是和后端直連的,這就意味著,每個后端系統(tǒng)都需要獨自去支持這些系統(tǒng)級的功能,導(dǎo)致了各個后端系統(tǒng)重復(fù)開發(fā)。一旦這些通用需求發(fā)生了變化,比如說,我們要對傳輸數(shù)據(jù)進行壓縮,那么,所有的后端系統(tǒng)都需要同步調(diào)整,這樣不但工作量很大,而且也給項目管理也帶來了很大的挑戰(zhàn)。
3.最后是穩(wěn)定性的問題。
在這里,基于這種直連方式,只要一個后端系統(tǒng)出問題,就會直接影響到 App 的可用性,使得 App 整體上非常的脆弱。
之所以會出現(xiàn)以上這些問題,它的根本原因在于,我們在 App 端,直接照搬了 PC 端的做法,沒有針對移動端自身的特點,去做架構(gòu)設(shè)計。
我們知道,當(dāng) App 發(fā)展到一個成熟階段時,無論是業(yè)務(wù)功能,還是非業(yè)務(wù)性功能,和 PC 端都是不同的。所以,在架構(gòu)設(shè)計上,我們必須能夠支持它們各自不同的特點,根據(jù)這個思路,我們的 App 服務(wù)端架構(gòu)也演變到了 V3.0 版本。
三、V3.0 架構(gòu)
在 V3.0 版本中,服務(wù)端架構(gòu)包含了兩個大的升級。
首先,我們對每個業(yè)務(wù)線的服務(wù)端進行拆分,讓 App 接口和 PC 端接口各自在物理上獨立,但它們共享核心的業(yè)務(wù)邏輯。
拆分后的架構(gòu)如下圖所示:
這樣拆分的結(jié)果是,原來大的服務(wù)端變成了 3 個應(yīng)用,包括一個 App 端接口應(yīng)用,一個 PC 端 Web 應(yīng)用,還有一個核心業(yè)務(wù)邏輯服務(wù),3 個部分都是獨立維護和部署的。
除此之外,架構(gòu)改造還考慮了移動端自身的特點。
一方面,每個移動端接口需要調(diào)用對應(yīng)的后臺服務(wù),進行業(yè)務(wù)邏輯處理,這個是個性化的,每個接口的處理邏輯都不一樣;另一方面,每個移動端接口都需要進行系統(tǒng)級的功能處理,比如前面所說的安全驗證、接口監(jiān)控等,這個是共性的,每個接口的處理方式都是一樣的。
那么,在架構(gòu)上,我們就需要把共性的系統(tǒng)級功能進行集中處理,把個性化的業(yè)務(wù)功能進行分散處理。
最后,我們結(jié)合服務(wù)端的應(yīng)用拆分,以及對移動接口本身的改造,落地了服務(wù)端 V3.0 架構(gòu)。
如下圖所示:
在這里,App 前端會通過移動網(wǎng)關(guān)來訪問服務(wù)端接口。這里的網(wǎng)關(guān)主要就是負責(zé)處理通用的系統(tǒng)級功能,包括通信協(xié)議適配、安全、監(jiān)控、日志等等;網(wǎng)關(guān)處理完之后,會通過接口路由模塊,轉(zhuǎn)發(fā)請求到內(nèi)部的各個業(yè)務(wù)服務(wù),比如搜索服務(wù)、詳情頁服務(wù)、購物車服務(wù)等等。
對于 PC 端瀏覽器來說,它直接訪問對應(yīng)的 Web 應(yīng)用,如搜索應(yīng)用、詳情頁應(yīng)用等,然后這些應(yīng)用也是訪問同樣的內(nèi)部服務(wù)。
這里說明下,當(dāng)時還沒有流行前后端分離,所以 PC 端有對應(yīng)的 Web 應(yīng)用,同時負責(zé)業(yè)務(wù)邏輯和 UI 展現(xiàn)?,F(xiàn)在,你已經(jīng)了解了 V3.0 版本的整體架構(gòu)設(shè)計,接下來,我們就深入移動網(wǎng)關(guān),去具體了解下它的內(nèi)部實現(xiàn)機制。
四、移動網(wǎng)關(guān)的內(nèi)部實現(xiàn)
在圖中,你可以看到,整個移動網(wǎng)關(guān)分為三層,自上而下分別是通用層、接口路由層、適配層,接下來我們逐一分析。
1.通用層
首先是通用層,它負責(zé)所有系統(tǒng)級功能的處理,比如通訊協(xié)議適配、安全、監(jiān)控、日志等等,這些功能統(tǒng)一由網(wǎng)關(guān)的通用層進行預(yù)處理,避免了各個業(yè)務(wù)線的重復(fù)開發(fā)。
在具體實現(xiàn)時,每個通用功能的處理邏輯都會封裝成一個攔截器,這些攔截器遵循統(tǒng)一的接口定義,并且攔截器都是可配置的。當(dāng)有外部請求過來,網(wǎng)關(guān)會依次調(diào)用這些攔截器,完成各個系統(tǒng)級功能的處理。
這個攔截器接口的定義如下:
Object filter(Object input)throws Exception
2.接口路由層
接下來是接口路由層。移動端請求經(jīng)過通用層的預(yù)處理之后,將會進一步分發(fā)給后端的業(yè)務(wù)適配器進行處理。
我們在配置文件里,對接口請求的 URL 和業(yè)務(wù)適配器進行映射,接口路由層的分發(fā)邏輯就是根據(jù)請求中的 URL,在配置文件里找到對應(yīng)的適配器,然后把請求交給適配器進行后續(xù)的處理。
配置文件的具體內(nèi)容如下所示:
www.website.com/search SearchAdapter
www.website.com/detail DetailAdapter
3.服務(wù)適配層
最后是服務(wù)適配層。我們知道,外部接口的請求格式,往往和內(nèi)部服務(wù)接口的格式是不一樣的。具體到 1 號店當(dāng)時的情況,外部接口是 HTTP+JSON 格式,內(nèi)部服務(wù)是 Hessian+ 二進制格式。
適配器首先用來解決內(nèi)外部接口的適配,除此之外,適配器還可以根據(jù)需要,對多個內(nèi)部服務(wù)做業(yè)務(wù)聚合,這樣可以對 App 前端提供粗粒度的接口服務(wù),減少遠程網(wǎng)絡(luò)的調(diào)用次數(shù)。
這些適配器遵循統(tǒng)一的接口定義:
Object adapter(Object input)throws Exception
這些適配器物理上是 Jar 包的形式,由各個業(yè)務(wù)線研發(fā)團隊提供,所有的適配器會集中部署在網(wǎng)關(guān),而網(wǎng)關(guān)本身可以支持多實例的部署,通過水平擴展的方式提升服務(wù)端的處理能力。
現(xiàn)在,你已經(jīng)很清楚了 V3.0 架構(gòu)的實現(xiàn)細節(jié),接下來,我們就深入看下,這次架構(gòu)升級達到了什么樣的實際效果。
五、架構(gòu)的實際效果
首先,App 端和 PC 端徹底獨立了。在上面的圖中,我們可以看到,App 前端和 PC 端瀏覽器是完全對等的,PC 端瀏覽器有自己的服務(wù)端,App 前端也有自己的服務(wù)端,在這里,移動網(wǎng)關(guān)就充當(dāng) App 服務(wù)端的角色。
在這個架構(gòu)下,兩個服務(wù)端都可以針對自身的特點,獨立開發(fā),獨立部署,無論在邏輯層面還是物理層面都實現(xiàn)了徹底解耦。我們知道,一開始,App 是依附于 PC 端,而現(xiàn)在,它終于可以獨立地發(fā)展了。
其次,通過架構(gòu)改造,實現(xiàn)了核心業(yè)務(wù)的復(fù)用。 這里,我們把核心的業(yè)務(wù)邏輯從 Web 應(yīng)用中剝離出來,變成了共享的服務(wù)。在服務(wù)設(shè)計時,我們不再區(qū)分 PC 端還是移動端,而是從業(yè)務(wù)本身出發(fā),提供一套通用的接口,同時供 PC 端和移動端調(diào)用,從而實現(xiàn)了底層業(yè)務(wù)邏輯的復(fù)用。
還有,這個架構(gòu)強化了系統(tǒng)級功能。 原來通用的系統(tǒng)級功能,由各個團隊各自去提供,很多團隊要么不提供,要么實現(xiàn)的方式不一樣;現(xiàn)在的系統(tǒng)級功能,是由集中式的移動網(wǎng)關(guān)統(tǒng)一來提供,我們就可以很方便地強化這些系統(tǒng)級功能。
舉個例子,我們可以把通信協(xié)議由 HTTP 升級為更安全的 HTTPS,當(dāng)后端服務(wù)有問題時,也可以通過網(wǎng)關(guān)進行事先的數(shù)據(jù)緩存,直接返回給 App 前端。比如說商品的詳情數(shù)據(jù),就很適合這樣的處理。
所以,有了移動網(wǎng)關(guān),整個 App 的可用性、穩(wěn)定性和安全性都得到了大幅度的提升。
最后,團隊分工也更明確了。 在這里,移動團隊主要負責(zé)移動網(wǎng)關(guān),包括網(wǎng)關(guān)本身和各種過濾器的維護,他們可以針對移動端的特點,做各種系統(tǒng)級功能的優(yōu)化;而業(yè)務(wù)團隊,主要負責(zé)各自的業(yè)務(wù)邏輯,包括適配器和底層服務(wù)。移動團隊和業(yè)務(wù)團隊通過明確的適配接口進行協(xié)作,相互不影響。
我們可以看到,V3.0 在 V2.0 分布式架構(gòu)的基礎(chǔ)上,通過服務(wù)化改造,實現(xiàn)了基礎(chǔ)業(yè)務(wù)的復(fù)用;同時,通過移動網(wǎng)關(guān)落地系統(tǒng)級功能,實現(xiàn)了系統(tǒng)的平臺化改造。
總的改造結(jié)果就是,解放了業(yè)務(wù)線,提升了系統(tǒng)的穩(wěn)定性,使得移動端可以做大做強。
六、總結(jié)
今天,我與你分享了 1 號店 App 服務(wù)端架構(gòu)改造的實際例子。在這個例子中,架構(gòu)經(jīng)歷了單體架構(gòu)到分布式架構(gòu),再到 SOA 架構(gòu)的變化過程,并且通過移動網(wǎng)關(guān)的方式,一定程度上實現(xiàn)了平臺化。在這里,你可以清晰地看到,公司每個階段的業(yè)務(wù),都有它不同的特點,我們選擇的架構(gòu)必須能夠適配它,過度設(shè)計和設(shè)計不足,同樣都是有害的。
通過今天的分享,相信你對各種架構(gòu)的優(yōu)缺點,以及業(yè)務(wù)上的適用性有了更進一步的了解。他山之石,可以攻玉。架構(gòu)的策略和原則是通用的,希望你能夠通過實戰(zhàn)不斷去領(lǐng)會和運用。