本文首發於InfoQ垂直公眾號:聊聊架構,5年時間伺服器從0到200,一個創業公司的架構野蠻生長史,轉發於InfoQ公眾號:一家創業公司的5年架構變遷史。
貝聊成立於2013年,是中國幼兒園家長工作平臺,致力於通過網際網路產品及定製化解決方案,幫助幼兒園解決展示、通知、溝通等家長工作中的痛點,促進家園關係和諧。貝聊是威創股份(A股幼教第一股)、清華啟迪、網易聯手投資的唯一品牌。在短短几年內,使用者規模迅速達到千萬級別,每年DAU均呈倍數級增長。面對如此快速的發展,原有的技術架構很難支撐越來越複雜的業務場景,在系統可用性以及穩定性方面,都給貝聊技術團隊帶來了很大的壓力。因此,如何針對當前需求,選擇合適的技術架構,保證架構平滑演進,值得我們深入思考。
貝聊架構演進的三個重要歷史階段
貝聊架構總體經歷了三次重大曆程,由幾臺伺服器搭建的單體架構到目前的幾百臺分散式部署架構,在整個變化過程中,我們踩過了很多坑,遇到過很多重大技術挑戰。
誕生期—技術架構選型V1.0
創業初期,我們的初始創業團隊在進行架構選型時,主要基於以下幾點進行考慮:
1.在創業初期,研發資源有限,研發人力有限,技術儲備有限,需要選擇一個易維護、簡單的技術架構;
2.產品需要快速研發上線,並能夠滿足快速迭代要求,現實情況決定了一開始沒有時間和精力來選擇一個過於複雜的分散式架構系統,研發速度必須要快;
3.創業初期,業務複雜度比較低,業務量也比較小,如果選擇過於複雜的架構,反而會增加研發難度以及運維難度;
4.遵從選擇合適的技術而不是最好的技術原則,並權衡研發效率和產品目標,同時創業初期貝聊只有一個PHP研發人員,過於複雜的技術架構必然會帶來比較高昂的學習成本。
正是基於以上幾點考慮,最終選擇了經典的LNMP技術架構,貝聊V1.0架構就這樣誕生了,為了加快產品研發速度,儘快上線產品,首期通過外包公司實現了研發以及部署,後續由我們的PHP研發人員接手,並進行後續的迭代開發。
初期部署時,部署了三臺ECS伺服器,其中接入層nginx與系統部署在同一臺機器,RDS資料庫一臺機器,Memcached快取一臺機器,V1.0架構具有以下特點:
- 單體架構,架構簡單,清晰的分層結構;
- 可以快速研發,滿足產品快速迭代要求;
- 沒有複雜的技術,技術學習成本低,同時運維成本低,無需專業的運維,節省開支。
LNMP架構支撐貝聊業務發展了將近一年半左右的時間,簡單、易維護的架構為貝聊的快速發展做出了很大的貢獻,期間業務發展迅速,使用者體量也越來越大,原有架構逐漸暴露出越來越多的問題。
成長期—技術架構重構V2.0
我是在2015年初加入了貝聊,初始研發團隊只有三人,有幸在這一時期
主導了貝聊技術架構重構,並經歷了貝聊後續的幾次架構演進路程,將原有PHP單體架構重構為JAVA分散式架構。
首先談一談我們做技術架構重構的契機,重構並非難在怎麼做,而是難在何時開始做,所以我們做架構重構的契機主要基於以下幾點:
1.原有LNMP架構經歷了兩個團隊研發和維護,外包團隊和公司PHP研發人員,由於業務變化比較快,原有的資料庫設計逐漸暴露出來很多問題,很多表設計不合理, 很多欄位定義不清,比較混亂;
2.2015年,由於業務發展,貝聊app需要拆分為兩個客戶端:貝聊家長端和貝聊老師端,通過不同的客戶端來服務不同的使用者群體,達到精準運營的目的,如果在原有架構上繼續進行開發,則會導致新舊介面邏輯混在一起,並且早期的很多介面定義不是很規範,維護起來越來越麻煩、越來越吃力;
3.原有API介面系統是單體架構,裡面包含了各種介面,混合了各組業務邏輯處理,所有功能都集中在API介面系統中,程式碼非常臃腫,業務非常繁雜,迭代開發的速度也逐漸減慢,各種效能問題經常爆出,BUG也比較多,並且部署困難,任何修改每次都需整體部署。由於業務邏輯混雜在一起,新入職研發人員需要很長時間才能夠完全熟悉系統,很難快速通過以點及面的方式切入系統;
4.所有資料儲存在一個RDS資料庫中,只使用了一個主庫,沒有從庫,同時很多系統共用一個資料庫,一方面資料庫沒有做到物理上的隔離,另一方面很多表都放在了同一個資料庫中,經常會碰到一個慢查詢或者其他效能問題,導致整個RDS各項指標飆升,造成雪崩效應,所有系統連鎖出現故障,最終都不能訪問;
5.公共服務耦合比較嚴重,很多第三方服務都散落在各個系統裡面,不便於統一維護,當需要修改公共服務引數或者做其他調整時,需要深入到每個系統裡進行修改或者排查,非常麻煩,還非常容易出現遺漏,最終產生BUG,急需獨立拆分出公共服務,將公共服務從業務系統中解耦出來,由專人進行獨立維護、獨立部署;
6.我們新的研發團隊都擁有豐富的JAVA分散式架構設計經驗,擁有高併發、高可用經驗,因此將原有的單體架構重構為JAVA分散式架構也是順勢而為。
由於公司業務高速發展,如果停下來專門做技術架構重構是不可能的,我們選擇了在維護現有系統的基礎上,同時進行新的技術架構重構工作。重構期間,在原有PHP研發團隊的大力支援下,我們的重構工作還算非常順利,既保障了業務的快速迭代需求,又成功完成了新的技術架構重構,新的V2.0架構如下:
在V2.0架構時期,初步實現了分散式部署架構,根據不同的功能以及業務邏輯,完成系統級別的拆分,同時對第三方服務進行解耦,拆分出了獨立的服務模組,針對DB,我們實現了系統級拆分以及物理獨立部署,並實現了資料庫主從分離,同時引入了MQ訊息佇列,並使用SLB實現了負載均衡和頻寬流量入口統一。
V2.0時期的架構具有以下特點:
- 分散式部署架構,系統易擴充套件;
- 系統級拆分,拆分出業務功能邏輯獨立的子系統,並獨立拆分出DB;
- 初步實現了服務化,系統間呼叫使用Hessian實現RPC;
- DB實現了物理隔離,避免以前單DB出故障,引發業務連鎖故障,同時實現了資料庫主從分離;
- 引入MQ訊息佇列實現訊息和任務非同步化,加快介面響應速度,提升使用者體驗,同時針對一些訊息推送任務也實現非同步化,避免早期的輪詢MySQL機制,減少訊息推送延時,提升訊息推送速度;
- 使用SLB實現了Nginx負載均衡,在V1.0架構時期,我們的Nginx是單點部署,若一臺Nginx伺服器掛掉,則會影響很多業務系統,有單點故障風險,通過SLB實現多臺Nginx負載均衡,達到高可用的目的,避免單點故障。
系統拆分和DB拆分
針對系統拆分以及DB拆分,我們通過兩個階段來完成該項工作。
第一階段
首先在系統層面進行拆分,將原有的大系統拆分出多個業務邏輯獨立的子系統,而DB暫時不進行拆分,多套系統還繼續共用一個DB,只是根據業務邏輯劃分各個系統所依賴的表,不同業務邏輯系統之間不能互相訪問表,這樣新系統只訪問自己所歸屬的表,通過此種方案,可以保證原有系統業務不受影響,同時新拆分的業務系統研發工作也可以順利進行,此階段大概花費了我們幾個月的時間,最終順利完成系統層面的拆分。
第二階段
在完成系統層面拆分之後,我們緊接著實施DB層面的拆分,將各個子系統所依賴的表獨立拆分出來,分別放置到不同的RDS資料庫,實現物理的隔離,同時實現了資料庫主從分離。最終實現效果如下圖:
初步服務化
本階段,我們採用了比較簡單易用的Hessian實現初期的RPC服務化。針對第三方公共服務,從原有系統中解耦出來,獨立拆分出服務化元件,並做獨立部署,供其餘業務系統統一呼叫。而系統間呼叫也通過Hessian來實現RPC遠端呼叫。
SLB負載均衡
在V1.0架構期間,我們的Nginx都是單點部署,一旦一臺Nginx伺服器出現故障,則會波及到大量業務系統,風險非常大,如下圖:
在V2.0架構期間,我們引入了SLB實現負載均衡,SLB配置了多臺Nginx,同時在業務系統層面也實現了負載均衡,避免了單點故障,達到高可用的目的。
爆發期—微服務架構V3.0
進入2016年以來,貝聊業務高速發展,使用者規模在短時間內增長數百萬,同時各個業務線逐漸鋪開,業務場景更加複雜,程式碼規模膨脹得也非常快,研發團隊迅速達到了幾十人規模,一個系統多人開發,研發人員層次不一,規範難以統一,同時業務邏輯耦合嚴重,每次上線都需要將整個大系統整體打包上線,風險非常大,並且新人入職之後學習成本非常高。因此我們引入了微服務架構,將業務邏輯拆分為獨立的微服務元件,每個微服務都圍繞著具體業務進行構建,由專人研發和維護,並由專人做效能優化和架構優化,各個微服務元件的研發與上線互不影響。
結合V2.0架構,在實施微服務架構時,基於多方面考慮,我們選擇了Dubbo作為分散式微服務框架。
- 成熟的高效能分散式框架,目前很多公司都在使用,已經經受住各方面效能考驗,比較穩定;
- 可以和Spring框架無縫整合,我們的架構正是基於Spring搭建,並且接入Dubbo時可以做到程式碼無侵入,接入也非常方便;
- 具備服務註冊、發現、路由、負載均衡、服務降級、權重調節等能力;
- 程式碼開源,可以根據需求進行個性化定製,擴充套件功能,進行自研發;
在做微服務時,我們考慮了以下幾個關鍵點:
- 以服務為中心,一切都是服務,每個服務都針對單一業務進行封裝,保證功能完整性和職責單一性;
- 鬆耦合性,服務之間功能獨立,能夠獨立部署,服務之間相互依賴;
- 高擴充套件性,分散資源,團隊協同工作,可無限擴充套件,更高的程式碼重用率。
在實施微服務架構時,主要考慮從以下幾個方面進行實施:
- 獨立功能邏輯拆分為微服務,獨立部署,獨立維護;
- 系統功能全部通過呼叫微服務實現,系統不能直接訪問DB;
- 小資料量高併發呼叫使用Dubbo長連線協議進行通訊,大資料量服務比如檔案、圖片、視訊等使用Hessian協議進行通訊;
- 每個微服務都維護獨立的DB。
微服務拆分案例
1 班級動態微服務
貝聊的班級動態是一個高頻率使用功能,園長、老師、家長都可以在班級進行釋出動態,通過點贊、回覆進行互動。隨著貝聊業務飛速發展,使用者規模爆發,每天都產生數十萬的班級動態量,同時日回覆量和點贊量均達到了數百萬級別。面對如此大規模的資料量,我們一方面要應對高併發的效能壓力,另一方面又要應對資料壓力,原有的班級動態功能散落在API介面系統以及後臺管理系統中,相關的表也與原有系統共享一個DB,迫切需要我們拆分出獨立的班級動態微服務元件,同時還需要做分庫分表減少單資料庫壓力。因此我們專門抽調精幹研發人力,拆分出了班級動態微服務元件。
舊班級動態呼叫方式如下
班級動態微服務元件呼叫方式如下:
拆分出班級動態微服務之後,我們解決了以下問題:
- 班級動態微服務對業務呼叫方透明,業務呼叫方只需呼叫介面即可,無需關注技術實現細節;
- 程式碼複用性,班級動態業務邏輯單獨抽出來做成獨立微服務元件,業務系統不再散落班級動態業務邏輯程式碼、無需再進行程式碼拷貝;
- 採用DRDS實施了分庫分表,解決了單資料庫資料量大、資料處理能力有限的瓶頸問題,在單資料庫情況下,由於資料量比較大,高併發時期,經常遇到效能問題,介面響應速度非常慢,在實施分庫分表之後,班級動態介面的整體效能提升了幾倍,使用者體驗非常好,高併發時期也沒有了效能問題。
2 使用者通行證微服務
很多創業公司,在一開始發展時,為了追求速度,同時由於人力不足,都是將使用者資料表與業務資料表暫時放在了一個DB裡面,貝聊早期也是這樣,這就造成了各個業務系統都是自己分別寫DAO來獲取使用者資料,產生了大量重複的使用者邏輯拷貝程式碼。隨著業務發展的越來越快,越來越多的業務系統都需要訪問使用者資料,使用者邏輯程式碼散落在各個業務系統,使用者資料越來越難維護,複雜度越來越高,同時使用者量越來越大,經常會遇到高併發效能問題,不容易做獨立效能優化,因此拆分出獨立的使用者通行證微服務迫在眉睫。
舊使用者資料獲取方式
使用者通行證微服務
拆分出使用者通行證微服務之後,我們解決了以下問題:
- 程式碼複用性,原先幾乎每個業務系統都散落有使用者邏輯程式碼,到處都是拷貝程式碼,拆分出使用者通行證微服務之後,業務系統只需呼叫使用者通行證微服務介面即可;
- 使用者資料一致性,以前由於獲取以及修改使用者資料程式碼散落在各個業務系統,經常會產生一些使用者髒資料,並且很難查詢在哪個系統修改了使用者資料,同時由於不同的研發人員開發維護不同的業務系統,也給維護使用者資料一致性帶來了很大的挑戰,拆分出使用者通行證微服務之後,所有跟使用者邏輯相關的功能,都由使用者通行證微服務提供,保證了修改資料以及獲取資料的介面一致性;
- 使用者資料解耦,原有業務系統中經常會join使用者表獲取使用者資料,難以拆分,拆分出微服務之後,使用者資料庫獨立設計部署,方便進行擴容以及效能優化。
微服務治理
微服務架構開發、測試、部署複雜度遠遠大於單體架構,因此需要構建能夠支撐微服務架構的交付和運維能力。
1 版本釋出系統
微服務架構的應用開發、部署的複雜度都是遠大於單體架構應用的,大量的微服務元件如果依然靠運維人員手工的配置管理顯然是難於應付了,因此我們研發了自動化部署和釋出的版本釋出系統,我們的版本釋出系統具有以下特性:
- 專案配置包括專案名稱、管理員、專案成員、SVN/Git地址、帳號、服務啟動的Shell、自定義指令碼、不同環境的JVM配置、Web容器配置等等;
- 按照專案配置好之後,可以發起上線申請單,通過審批之後,一鍵即可部署;
- 支援灰度釋出,可以灰度選擇伺服器進行版本釋出,確保版本釋出安全穩定;
- 可以實時收集部署過程產生的日誌,視覺化實時監控部署過程產生的問題;
- 針對釋出異常,我們有釋出異常處理機制,針對有多臺伺服器的情況,可以選擇只要有失敗就停止釋出,即一臺釋出出錯,後續其餘伺服器停止釋出,也可以選擇不管是否有失敗都繼續釋出;
- 快速回滾,針對版本釋出出現異常的情況,我們支援快速回滾,可以快速回滾到上一個穩定的版本。
通過版本釋出系統,實現程式碼版本管理、一鍵部署上線、一鍵快速回滾、上線單申請、上線稽核以及上線日誌等。
2 開發測試釋出部署
針對微服務複雜的架構,為了保證每個微服務交付的質量,我們部署了四個環境:
- 開發環境,供研發人員在開發、除錯階段使用;
- 測試環境,研發人員在開發環境完成所有功能開發、測試之後,部署給測試人員進行驗收的環境;
- 預釋出環境,在完成測試環境的功能驗收之後,功能釋出至生產環境前的一個預演環境,與生產環境共用相同的資料庫、快取、MQ訊息佇列等,用來在微服務上線生產環境前,確認是否還存在BUG等問題,不會影響生產環境的使用者,最終用來確保上線生產環境成功;
- 生產環境,即線上環境,是面向使用者的線上環境。
通過以上四個環境,確保微服務元件的研發、測試、釋出的質量。
3 分散式配置中心以及分散式任務排程平臺
隨著微服務架構的實施,我們拆分出了很多的微服務以及子系統,各種配置資訊都以明文形式配置在配置檔案中,同時各種定時任務也散落在各個微服務以及子系統中,非常難管理。因此我們選擇了合適的分散式配置中心以及分散式任務排程平臺
- Disconf分散式配置管理平臺,實現了配置釋出統一化,所有配置都儲存在雲端系統,使用者統一在平臺上進行釋出、更新配置,更改配置時,無需重新打包或重啟微服務,通過管理平臺直接修改即可,同時我們進行了個性化定製研發,所有配置資訊都實現了加密方式,避免賬號、密碼等敏感資訊洩露;
- Elastic-Job分散式任務排程平臺,實現了定時任務註冊中心、任務分片、任務執行伺服器彈性擴容縮容、失效轉移、任務停止恢復和禁用等特性,更方便的管理分散式架構環境中的定時任務。
4 全鏈路跟蹤
微服務架構拆分了大量的子系統以及微服務元件,面對如此複雜大規模分散式叢集,一次鏈路呼叫可能會發生在多個微服務元件之間,如何進行鏈路呼叫追蹤,如何快速發現一次介面呼叫過程中哪些地方需要優化、哪個微服務介面導致了整體呼叫比較慢。針對上述問題,我們引入了美團點評的APM工具Cat實時監控系統,與Dubbo服務化框架進行整合,通過全域性鏈路ID,實現鏈路追蹤功能。
5 微服務授權
預設Dubbo沒有實現服務授權功能,系統呼叫微服務、微服務之間呼叫均沒有實現授權驗證,都是直接訪問微服務元件介面,因此我們針對Dubbo進行了個性化定製研發,研發了微服務授權認證中心,通過授權認證保證核心微服務介面的呼叫安全性。
6 微服務監控
拆分出大量的微服務元件,我們面對的是如何監控這麼多的微服務執行狀態,我們採用了Dubbo自帶的簡易監控中心,監控微服務元件的成功率、失敗率、平均耗時、最大耗時、併發量等指標,通過這些指標發現微服務效能瓶頸,進而優化微服務效能。同時我們進行個性化定製擴充套件與研發,針對Dubbo介面呼叫,統計介面耗時排行、介面失敗排行、介面訪問異動排行等等,通過定製化研發的統計報表,更直觀的監控Dubbo介面效能。
7 微服務管理
我們使用Dubbo的管理控制檯,實現對微服務的路由規則配置、訪問控制、權重調節、服務降級、服務禁用、容錯等功能,可以非常方便的管理執行中的微服務元件。
經過一年多的微服務化歷程,V3.0架構如下:
V3.0微服務架構具有以下特點:
- 完全實現了分散式部署架構,系統與微服務元件都非常容易擴充套件;
- 以服務為中心,全面構建了微服務元件;
- 系統、微服務元件、快取、MQ訊息佇列、DB等均無單點風險,全部實現了HA高可用;
未來—貝聊架構演進V4.0
V3.0架構雖然實現了微服務架構,但該架構還存在以下可以繼續演進的點:
- Docker容器部署,Docker具備輕量級、快速部署、隔離應用、跨平臺的優勢,微服務非常適合與Docker結合進行快速部署,目前雖然我們實現了微服務架構,但還未做到快速彈性擴充套件,如果將微服務與Docker容器進行結合,可以實現快速彈性擴充套件,業務高峰期可以快速自動擴充套件伺服器,業務低峰期可以自動回收伺服器,接下來我們即將實施微服務元件的Docker容器化部署;
- 統一API閘道器,目前我們的核心API還只是一個統一的代理層,尚不具備閘道器的身份認證、防報文重放與防資料篡改、業務鑑權、流量與併發控制等等功能,實施API閘道器後,可以實現前後端分離,提供便捷的監控、報警、分析,還可以提供嚴格的許可權管理以及流量限制,保障API的安全穩定。接下來我們將實施統一的API閘道器控制;
- 跨IDC機房部署,目前我們的系統還是單機房部署,單機房不具備冗餘以及容災機制,首先我們將逐漸實施同地多機房部署,先具備多機房部署能力,避免單機房故障,最後我們再實施異地跨IDC機房部署,達到異地冗餘高可用以及使用者就近訪問的目的。
總結
架構演進一直在路上,架構要圍繞業務進行,不能脫離於業務,不同的業務時期需要不同的架構。
單體應用架構,更適合創業初期,公司需要快速試錯以及驗證市場反應,需要更快的研發速度,同時研發人員比較少,而單體應用架構比較簡單,可以快速切入,對研發人員的技術棧要求不是特別高,可以快速上手快速研發,但在設計單體應用架構時最好可以提前規劃好未來的擴充套件性,可以在業務層面先規劃好,便於日後業務發展到一定規模,可以快速進行解耦,實施微服務化架構。
當企業發展到一定規模,業務線變的越來越多、越來越複雜,同時研發人員的數目也快速增長,單體應用架構就會慢慢暴露出來弊端,大量的研發人員在一個系統上進行開發,缺少並行研發能力,大量的業務程式碼耦合在一起,同時研發效率非常低。微服務架構可以更好的進行業務解耦,具備更好的擴充套件性以及獨立性,可以提高研發團隊間的並行化研發速度,提升效率、提高模組複用性,具備高可用、高併發特性。但微服務架構對服務治理的能力要求比較高,維護成本也會比單體應用高,需要強大的服務治理支援,對研發人員的技術能力要求也比較更高。
目前我們依然在架構演進的路上,經歷了以上幾次架構歷程,雖然取得了一定的進步,但依然有很多挑戰等待我們去迎戰。規劃技術架構需要綜合考慮業務的規模、業務的時效性、研發團隊的規模、研發的技術能力、基礎環境配置等。架構來源於業務,架構演進的生命週期只有完美匹配好業務的生命週期,才能最終發揮出最好的效果。