做了3年的后端開(kāi)發(fā), 經(jīng)歷一款SaaS產(chǎn)品從0到10(還沒(méi)有到100, 哈哈哈)的過(guò)程, 3年間后端的架構(gòu)逐步演變, 在微服務(wù)的實(shí)踐過(guò)程中遇到的問(wèn)題也越來(lái)越多, 在這里總結(jié)下.
產(chǎn)品是一款服務(wù)于人力資源的SaaS在線(xiàn)服務(wù), 面向HR有Web Android/IOS 小程序多個(gè)客戶(hù)端, 后端采用RESTful風(fēng)格API來(lái)提供服務(wù). 主要使用Python語(yǔ)言, 方便快速迭代.
架構(gòu)的演進(jìn)經(jīng)歷了4個(gè)大的階段: 1. MVC 2. 服務(wù)拆分 3. 微服務(wù)架構(gòu) 4. 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì).
1. MVC
項(xiàng)目剛開(kāi)始的時(shí)候, 后端同事不超過(guò)5個(gè), 這個(gè)階段主要的工作是實(shí)現(xiàn)產(chǎn)品的原型, 沒(méi)有太多的考慮架構(gòu), 使用Django來(lái)快速實(shí)現(xiàn)功能, DB的表結(jié)構(gòu)設(shè)計(jì)好之后, 抽象出功能View, 由于產(chǎn)品設(shè)計(jì)也很不完善, 后端需要很多的預(yù)留設(shè)計(jì), 避免產(chǎn)品邏輯的變更帶來(lái)整個(gè)表結(jié)構(gòu)的變動(dòng), 在這個(gè)階段代碼上最重要的是確定適合團(tuán)隊(duì)的代碼規(guī)范, 代碼檢查規(guī)則.
整體上架構(gòu)如上圖, Nginx負(fù)責(zé)負(fù)載均衡, 分發(fā)流量到多個(gè)Django服務(wù), Django處理邏輯, 需要異步任務(wù)就交給Celery, 然后數(shù)據(jù)量比較大的地方使用redis做緩存. 同時(shí)還有實(shí)時(shí)消息通知的需要使用了Nginx Push Module.
問(wèn)題與優(yōu)化方式:
Django并發(fā)性能差 使用uWSGI Master+Worker 配合 gevent 攜程支持高并發(fā)
Redis連接數(shù)過(guò)多 使用redis-py自帶的連接池來(lái)實(shí)現(xiàn)連接復(fù)用
MySQL連接數(shù)過(guò)多 使用djorm-ext-pool連接池復(fù)用連接
Celery配置gevent支持并發(fā)任務(wù)
隨著開(kāi)發(fā)的功能越來(lái)越多, Django下的App也越來(lái)越多, 這就帶了發(fā)布上的不方便, 每次發(fā)布版本都需要重啟所有的Django服務(wù), 如果發(fā)布遇到問(wèn)題, 只能加班解決了. 而且單個(gè)Django工程下的代碼量也越來(lái)越多, 不好維護(hù).
2. 服務(wù)拆分
隨著后端團(tuán)隊(duì)的壯大, 分給每個(gè)同事的需求也越來(lái)越細(xì), 如果繼續(xù)在一個(gè)工程里面開(kāi)發(fā)所有的代碼, 維護(hù)起來(lái)的代價(jià)太高, 而我們的上一個(gè)架構(gòu)中在Django里面已經(jīng)按模塊劃分了一個(gè)個(gè)app, app內(nèi)高類(lèi)聚, app之間低耦合, 這就為服務(wù)的拆分帶來(lái)了便利. 拆分的過(guò)程沒(méi)有遇到太大的問(wèn)題, 初期的拆分只是代碼的分離, 把公用的代碼抽離出來(lái)實(shí)現(xiàn)一個(gè)公用的Python庫(kù), 數(shù)據(jù)庫(kù), Redis還是共用, 隨著負(fù)載的增加, 數(shù)據(jù)庫(kù)也做了多實(shí)例.
如上圖, 服務(wù)之間盡量避免相互調(diào)用, 需要交互的地方采用http請(qǐng)求的方式, 內(nèi)網(wǎng)的調(diào)用使用hosts指向內(nèi)網(wǎng)地址.
問(wèn)題與優(yōu)化方式:
Nginx Push Module由于長(zhǎng)時(shí)間沒(méi)有維護(hù), 長(zhǎng)連接最大數(shù)量不夠, 使用Tornado + ZeroMQ實(shí)現(xiàn)了tormq服務(wù)來(lái)支撐消息通知
服務(wù)之間的調(diào)用采用http的方式, 并且要求有依賴(lài)的服務(wù)主機(jī)配置hosts指向被調(diào)用的地址, 這樣帶來(lái)的維護(hù)上的不方便. 以及在調(diào)用鏈的過(guò)程中沒(méi)有重試, 錯(cuò)誤處理, 限流等等的策略, 導(dǎo)致服務(wù)可用性差. 隨著業(yè)務(wù)拆分, 繼續(xù)使用Nginx維護(hù)配置非常麻煩, 經(jīng)常因?yàn)樾薷腘ginx的配置引發(fā)調(diào)用錯(cuò)誤. 每一個(gè)服務(wù)都有一個(gè)完整的認(rèn)證過(guò)程, 認(rèn)證又依賴(lài)于用戶(hù)中心的數(shù)據(jù)庫(kù), 修改認(rèn)證時(shí)需要重新發(fā)布多個(gè)服務(wù).
3. 微服務(wù)架構(gòu)
首先是在接入層引入了基于OpenResty的Kong API Gateway, 定制實(shí)現(xiàn)了認(rèn)證, 限流等插件. 在接入層承接并剝離了應(yīng)用層公共的認(rèn)證, 限流等功能. 在發(fā)布新的服務(wù)時(shí), 發(fā)布腳本中調(diào)用Kong admin api注冊(cè)服務(wù)地址到Kong, 并加載api需要使用插件.
為了解決相互調(diào)用的問(wèn)題, 維護(hù)了一個(gè)基于gevent+msgpack的RPC服務(wù)框架doge, 借助于etcd做服務(wù)治理, 并在rpc客戶(hù)端實(shí)現(xiàn)了限流, 高可用, 負(fù)載均衡這些功能.
在這個(gè)階段最難的技術(shù)選型, 開(kāi)源的API網(wǎng)關(guān)大多用Golang與OpenResty(lua)實(shí)現(xiàn), 為了應(yīng)對(duì)我們業(yè)務(wù)的需要還要做定制. 前期花了1個(gè)月時(shí)間學(xué)習(xí)OpenResty與Golang, 并使用OpenResty實(shí)現(xiàn)了一個(gè)短網(wǎng)址服務(wù)shorturl用在業(yè)務(wù)中. 最終選擇Kong是基于Lua發(fā)布的便利性, Kong的開(kāi)箱即用以及插件開(kāi)發(fā)比較容易. 性能的考量倒不是最重要的, 為了支撐更多的并發(fā), 還使用了云平臺(tái)提供的LB服務(wù)分發(fā)流量到2臺(tái)Kong服務(wù)器組成的集群. 集群之間自動(dòng)同步配置.
餓了么維護(hù)一個(gè)純Python實(shí)現(xiàn)的thrift協(xié)議框架thriftpy, 并提供很多配套的工具, 如果團(tuán)隊(duì)足夠大, 這一套R(shí)PC方案其實(shí)是合適的, 但是我們的團(tuán)隊(duì)人手不足, 水平參差不齊, 很難推廣這一整套學(xué)習(xí)成本高昂的方案. 最終我們開(kāi)發(fā)了類(lèi)Duboo的RPC框架doge, 代碼主要參考了weibo開(kāi)源的motan.
4. 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)
在這一架構(gòu)中我們嘗試從應(yīng)用服務(wù)中抽離出數(shù)據(jù)服務(wù)層, 每一個(gè)數(shù)據(jù)服務(wù)包含一個(gè)或多個(gè)界限上下文, 界限上下文類(lèi)只有一個(gè)聚合根來(lái)暴露出RPC調(diào)用的方法. 數(shù)據(jù)服務(wù)不依賴(lài)于應(yīng)用服務(wù), 應(yīng)用服務(wù)可以依賴(lài)多個(gè)數(shù)據(jù)服務(wù). 有了數(shù)據(jù)服務(wù)層, 應(yīng)用就解耦了相互之間的依賴(lài), 高層服務(wù)只依賴(lài)于底層服務(wù).
在我離職時(shí)領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)還在學(xué)習(xí)設(shè)計(jì)階段, 還沒(méi)有落地, 但是我相信前公司的后端架構(gòu)一定會(huì)往這個(gè)方向繼續(xù)演進(jìn).
總結(jié)
架構(gòu)的設(shè)計(jì), 技術(shù)的選型, 不能完全按照流行的技術(shù)走, 最終還是服務(wù)于產(chǎn)品, 服務(wù)于客戶(hù)的需求. 設(shè)計(jì)過(guò)程中由于團(tuán)隊(duì), 人員的結(jié)構(gòu)問(wèn)題, 有很多的妥協(xié)之處, 如何在妥協(xié)中找到最優(yōu)解才是最大的挑戰(zhàn).
最后,小編想說(shuō):我是一名python開(kāi)發(fā)工程師,整理了一套最新的python系統(tǒng)學(xué)習(xí)教程,想要這些資料的可以關(guān)注私信小編“01”即可,希望能對(duì)你有所幫助