貝聊成立於2013年,是中國幼兒園家長工做平臺,致力於經過互聯網產品及定製化解決方案,幫助幼兒園解決展現、通知、溝通等家長工做中的痛點,促進家園關係和諧。貝聊是威創股份(A股幼教第一股)、清華啓迪、網易聯手投資的惟一品牌。在短短几年內,用戶規模迅速達到千萬級別,每一年DAU均呈倍數級增加。面對如此快速的發展,原有的技術架構很難支撐愈來愈複雜的業務場景,在系統可用性以及穩定性方面,都給貝聊技術團隊帶來了很大的壓力。所以,如何針對當前需求,選擇合適的技術架構,保證架構平滑演進,值得咱們深刻思考。nginx
貝聊架構演進的三個重要歷史階段
貝聊架構整體經歷了三次重大曆程,由幾臺服務器搭建的單體架構到目前的幾百臺分佈式部署架構,在整個變化過程當中,咱們踩過了不少坑,遇到過不少重大技術挑戰。
誕生期—技術架構選型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實現了負載均衡和帶寬流量入口統一。
- 系統級拆分,拆分出業務功能邏輯獨立的子系統,並獨立拆分出DB;
- 初步實現了服務化,系統間調用使用Hessian實現RPC;
- DB實現了物理隔離,避免之前單DB出故障,引起業務連鎖故障,同時實現了數據庫主從分離;
- 引入MQ消息隊列實現消息和任務異步化,加快接口響應速度,提高用戶體驗,同時針對一些消息推送任務也實現異步化,避免早期的輪詢MySQL機制,減小消息推送延時,提高消息推送速度;
- 使用SLB實現了Nginx負載均衡,在V1.0架構時期,咱們的Nginx是單點部署,若一臺Nginx服務器掛掉,則會影響不少業務系統,有單點故障風險,經過SLB實現多臺Nginx負載均衡,達到高可用的目的,避免單點故障。
針對系統拆分以及DB拆分,咱們經過兩個階段來完成該項工做。
首先在系統層面進行拆分,將原有的大系統拆分出多個業務邏輯獨立的子系統,而DB暫時不進行拆分,多套系統還繼續共用一個DB,只是根據業務邏輯劃分各個系統所依賴的表,不一樣業務邏輯系統之間不能互相訪問表,這樣新系統只訪問本身所歸屬的表,經過此種方案,能夠保證原有系統業務不受影響,同時新拆分的業務系統研發工做也能夠順利進行,此階段大概花費了咱們幾個月的時間,最終順利完成系統層面的拆分。
在完成系統層面拆分以後,咱們緊接着實施DB層面的拆分,將各個子系統所依賴的表獨立拆分出來,分別放置到不一樣的RDS數據庫,實現物理的隔離,同時實現了數據庫主從分離。最終實現效果以下圖:
本階段,咱們採用了比較簡單易用的Hessian實現初期的RPC服務化。針對第三方公共服務,從原有系統中解耦出來,獨立拆分出服務化組件,並作獨立部署,供其他業務系通通一調用。而系統間調用也經過Hessian來實現RPC遠程調用。
在V1.0架構期間,咱們的Nginx都是單點部署,一旦一臺Nginx服務器出現故障,則會波及到大量業務系統,風險很是大,以下圖:
在V2.0架構期間,咱們引入了SLB實現負載均衡,SLB配置了多臺Nginx,同時在業務系統層面也實現了負載均衡,避免了單點故障,達到高可用的目的。
爆發期—微服務架構V3.0
進入2016年以來,貝聊業務高速發展,用戶規模在短期內增加數百萬,同時各個業務線逐漸鋪開,業務場景更加複雜,代碼規模膨脹得也很是快,研發團隊迅速達到了幾十人規模,一個系統多人開發,研發人員層次不一,規範難以統一,同時業務邏輯耦合嚴重,每次上線都須要將整個大系統總體打包上線,風險很是大,而且新人入職以後學習成本很是高。所以咱們引入了微服務架構,將業務邏輯拆分爲獨立的微服務組件,每一個微服務都圍繞着具體業務進行構建,由專人研發和維護,並由專人作性能優化和架構優化,各個微服務組件的研發與上線互不影響。
結合V2.0架構,在實施微服務架構時,基於多方面考慮,咱們選擇了Dubbo做爲分佈式微服務框架。
- 能夠和Spring框架無縫集成,咱們的架構正是基於Spring搭建,而且接入Dubbo時能夠作到代碼無侵入,接入也很是方便;
- 具有服務註冊、發現、路由、負載均衡、服務降級、權重調節等能力;
- 代碼開源,能夠根據需求進行個性化定製,擴展功能,進行自研發;
- 鬆耦合性,服務之間功能獨立,可以獨立部署,服務之間相互依賴;
- 高擴展性,分散資源,團隊協同工做,可無限擴展,更高的代碼重用率。
在實施微服務架構時,主要考慮從如下幾個方面進行實施:
- 系統功能所有經過調用微服務實現,系統不能直接訪問DB;
- 小數據量高併發調用使用Dubbo長鏈接協議進行通信,大數據量服務好比文件、圖片、視頻等使用Hessian協議進行通信;
貝聊的班級動態是一個高頻率使用功能,園長、老師、家長均可以在班級進行發佈動態,經過點贊、回覆進行互動。隨着貝聊業務飛速發展,用戶規模爆發,天天都產生數十萬的班級動態量,同時日回覆量和點贊量均達到了數百萬級別。面對如此大規模的數據量,咱們一方面要應對高併發的性能壓力,另外一方面又要應對數據壓力,原有的班級動態功能散落在API接口系統以及後臺管理系統中,相關的表也與原有系統共享一個DB,迫切須要咱們拆分出獨立的班級動態微服務組件,同時還須要作分庫分表減小單數據庫壓力。所以咱們專門抽調精幹研發人力,拆分出了班級動態微服務組件。
- 代碼複用性,班級動態業務邏輯單獨抽出來作成獨立微服務組件,業務系統再也不散落班級動態業務邏輯代碼、無需再進行代碼拷貝;
- 採用DRDS實施了分庫分表,解決了單數據庫數據量大、數據處理能力有限的瓶頸問題,在單數據庫狀況下,因爲數據量比較大,高併發時期,常常遇到性能問題,接口響應速度很是慢,在實施分庫分表以後,班級動態接口的總體性能提高了幾倍,用戶體驗很是好,高併發時期也沒有了性能問題。
不少創業公司,在一開始發展時,爲了追求速度,同時因爲人力不足,都是將用戶數據表與業務數據表暫時放在了一個DB裏面,貝聊早期也是這樣,這就形成了各個業務系統都是本身分別寫DAO來獲取用戶數據,產生了大量重複的用戶邏輯拷貝代碼。隨着業務發展的愈來愈快,愈來愈多的業務系統都須要訪問用戶數據,用戶邏輯代碼散落在各個業務系統,用戶數據愈來愈難維護,複雜度愈來愈高,同時用戶量愈來愈大,常常會遇到高併發性能問題,不容易作獨立性能優化,所以拆分出獨立的用戶通行證微服務迫在眉睫。
- 用戶數據一致性,之前因爲獲取以及修改用戶數據代碼散落在各個業務系統,常常會產生一些用戶髒數據,而且很難查詢在哪一個系統修改了用戶數據,同時因爲不一樣的研發人員開發維護不一樣的業務系統,也給維護用戶數據一致性帶來了很大的挑戰,拆分出用戶通行證微服務以後,全部跟用戶邏輯相關的功能,都由用戶通行證微服務提供,保證了修改數據以及獲取數據的接口一致性;
- 用戶數據解耦,原有業務系統中常常會join用戶表獲取用戶數據,難以拆分,拆分出微服務以後,用戶數據庫獨立設計部署,方便進行擴容以及性能優化。
微服務架構開發、測試、部署複雜度遠遠大於單體架構,所以須要構建可以支撐微服務架構的交付和運維能力。
微服務架構的應用開發、部署的複雜度都是遠大於單體架構應用的,大量的微服務組件若是依然靠運維人員手工的配置管理顯然是難於應付了,所以咱們研發了自動化部署和發佈的版本發佈系統,咱們的版本發佈系統具備如下特性:
- 項目配置包括項目名稱、管理員、項目成員、SVN/Git地址、賬號、服務啓動的Shell、自定義腳本、不一樣環境的JVM配置、Web容器配置等等;
- 按照項目配置好以後,能夠發起上線申請單,經過審批以後,一鍵便可部署;
- 支持灰度發佈,能夠灰度選擇服務器進行版本發佈,確保版本發佈安全穩定;
- 能夠實時收集部署過程產生的日誌,可視化實時監控部署過程產生的問題;
- 針對發佈異常,咱們有發佈異常處理機制,針對有多臺服務器的狀況,能夠選擇只要有失敗就中止發佈,即一臺發佈出錯,後續其他服務器中止發佈,也能夠選擇無論是否有失敗都繼續發佈;
- 快速回滾,針對版本發佈出現異常的狀況,咱們支持快速回滾,能夠快速回滾到上一個穩定的版本。
經過版本發佈系統,實現代碼版本管理、一鍵部署上線、一鍵快速回滾、上線單申請、上線審覈以及上線日誌等。
針對微服務複雜的架構,爲了保證每一個微服務交付的質量,咱們部署了四個環境:
- 測試環境,研發人員在開發環境完成全部功能開發、測試以後,部署給測試人員進行驗收的環境;
- 預發佈環境,在完成測試環境的功能驗收以後,功能發佈至生產環境前的一個預演環境,與生產環境共用相同的數據庫、緩存、MQ消息隊列等,用來在微服務上線生產環境前,確認是否還存在BUG等問題,不會影響生產環境的用戶,最終用來確保上線生產環境成功;
經過以上四個環境,確保微服務組件的研發、測試、發佈的質量。
隨着微服務架構的實施,咱們拆分出了不少的微服務以及子系統,各類配置信息都以明文形式配置在配置文件中,同時各類定時任務也散落在各個微服務以及子系統中,很是難管理。所以咱們選擇了合適的分佈式配置中心以及分佈式任務調度平臺
微服務架構拆分了大量的子系統以及微服務組件,面對如此複雜大規模分佈式集羣,一次鏈路調用可能會發生在多個微服務組件之間,如何進行鏈路調用追蹤,如何快速發現一次接口調用過程當中哪些地方須要優化、哪一個微服務接口致使了總體調用比較慢。針對上述問題,咱們引入了美團點評的APM工具Cat實時監控系統,與Dubbo服務化框架進行整合,經過全局鏈路ID,實現鏈路追蹤功能。
5 微服務受權
默認Dubbo沒有實現服務受權功能,系統調用微服務、微服務之間調用均沒有實現受權驗證,都是直接訪問微服務組件接口,所以咱們針對Dubbo進行了個性化定製研發,研發了微服務受權認證中心,經過受權認證保證核心微服務接口的調用安全性。
拆分出大量的微服務組件,咱們面對的是如何監控這麼多的微服務運行狀態,咱們採用了Dubbo自帶的簡易監控中心,監控微服務組件的成功率、失敗率、平均耗時、最大耗時、併發量等指標,經過這些指標發現微服務性能瓶頸,進而優化微服務性能。同時咱們進行個性化定製擴展與研發,針對Dubbo接口調用,統計接口耗時排行、接口失敗排行、接口訪問異動排行等等,經過定製化研發的統計報表,更直觀的監控Dubbo接口性能。
咱們使用Dubbo的管理控制檯,實現對微服務的路由規則配置、訪問控制、權重調節、服務降級、服務禁用、容錯等功能,能夠很是方便的管理運行中的微服務組件。
- 系統、微服務組件、緩存、MQ消息隊列、DB等均無單點風險,所有實現了HA高可用;
將來—貝聊架構演進V4.0
V3.0架構雖然實現了微服務架構,但該架構還存在如下能夠繼續演進的點:
- Docker容器部署,Docker具有輕量級、快速部署、隔離應用、跨平臺的優點,微服務很是適合與Docker結合進行快速部署,目前雖然咱們實現了微服務架構,但還未作到快速彈性擴展,若是將微服務與Docker容器進行結合,能夠實現快速彈性擴展,業務高峯期能夠快速自動擴展服務器,業務低峯期能夠自動回收服務器,接下來咱們即將實施微服務組件的Docker容器化部署;
- 統一API網關,目前咱們的核心API還只是一個統一的代理層,尚不具有網關的身份認證、防報文重放與防數據篡改、業務鑑權、流量與併發控制等等功能,實施API網關後,能夠實現先後端分離,提供便捷的監控、報警、分析,還能夠提供嚴格的權限管理以及流量限制,保障API的安全穩定。接下來咱們將實施統一的API網關控制;
- 跨IDC機房部署,目前咱們的系統仍是單機房部署,單機房不具有冗餘以及容災機制,首先咱們將逐漸實施同地多機房部署,先具有多機房部署能力,避免單機房故障,最後咱們再實施異地跨IDC機房部署,達到異地冗餘高可用以及用戶就近訪問的目的。
總結
架構演進一直在路上,架構要圍繞業務進行,不能脫離於業務,不一樣的業務時期須要不一樣的架構。
單體應用架構,更適合創業初期,公司須要快速試錯以及驗證市場反應,須要更快的研發速度,同時研發人員比較少,而單體應用架構比較簡單,能夠快速切入,對研發人員的技術棧要求不是特別高,能夠快速上手快速研發,但在設計單體應用架構時最好能夠提早規劃好將來的擴展性,能夠在業務層面先規劃好,便於往後業務發展到必定規模,能夠快速進行解耦,實施微服務化架構。
當企業發展到必定規模,業務線變的愈來愈多、愈來愈複雜,同時研發人員的數目也快速增加,單體應用架構就會慢慢暴露出來弊端,大量的研發人員在一個系統上進行開發,缺乏並行研發能力,大量的業務代碼耦合在一塊兒,同時研發效率很是低。微服務架構能夠更好的進行業務解耦,具有更好的擴展性以及獨立性,能夠提升研發團隊間的並行化研發速度,提高效率、提升模塊複用性,具有高可用、高併發特性。但微服務架構對服務治理的能力要求比較高,維護成本也會比單體應用高,須要強大的服務治理支持,對研發人員的技術能力要求也比較更高。
目前咱們依然在架構演進的路上,經歷了以上幾回架構歷程,雖然取得了必定的進步,但依然有不少挑戰等待咱們去迎戰。規劃技術架構須要綜合考慮業務的規模、業務的時效性、研發團隊的規模、研發的技術能力、基礎環境配置等。架構來源於業務,架構演進的生命週期只有完美匹配好業務的生命週期,才能最終發揮出最好的效果。