最近在做一個(gè)在線平臺,架構(gòu)思路如下
架構(gòu)演進(jìn):1蹋盆、MVC? 2费薄、服務(wù)拆分 3、微服務(wù)架構(gòu) 4栖雾、領(lǐng)域驅(qū)動設(shè)計(jì)
1楞抡、MVC
這個(gè)階段主要是快速實(shí)現(xiàn)產(chǎn)品,沒考慮其他的析藕,設(shè)計(jì)之初劃分多個(gè)app召廷,app內(nèi)高類聚,app之間低耦合账胧,DB表設(shè)計(jì)好了之后竞慢,實(shí)現(xiàn)view層功能需求,利用Django來快速實(shí)現(xiàn)功能治泥,后端有許多預(yù)留設(shè)計(jì)筹煮,避免產(chǎn)品邏輯的變更帶來整個(gè)表結(jié)構(gòu)的變動,架構(gòu)如下圖居夹;
nginx是負(fù)載均衡败潦,通過權(quán)重法本冲,把請求發(fā)送到多個(gè)Django服務(wù)(其實(shí)中間還有一個(gè)uwsgi),如果是靜態(tài)請求变屁,nginx直接返回給客戶端眼俊,如果是其他請求,通過uwsgi傳給Django粟关,Django拿到請求疮胖,處理響應(yīng)請求。耗時(shí)大的需要異步的闷板,我們用celery處理澎灸,使用mysql作為數(shù)據(jù)庫,redis作為緩存遮晚,加快請求的響應(yīng)性昭,減輕mysql負(fù)擔(dān),同時(shí)還有實(shí)時(shí)消息通知的需要使用了Nginx Push Module县遣。
問題以及處理:
1糜颠、Django并不像tornado一樣,對并發(fā)很支持萧求,Django并發(fā)性能差其兴,采用uwsgi+nginx+gevent實(shí)現(xiàn)高并發(fā)。
2夸政、redis連接數(shù)過多,導(dǎo)致服務(wù)掛掉元旬,使用redis-py自帶的連接池來實(shí)現(xiàn)連接復(fù)用
3、mysql連接數(shù)過多守问,使用使用djorm-ext-pool
4匀归、Celery配置gevent支持并發(fā)任務(wù)
5、celery配合rabbitmq任務(wù)隊(duì)列實(shí)現(xiàn)任務(wù)的異步調(diào)度執(zhí)行
Celery是一個(gè)分布式的任務(wù)隊(duì)列耗帕。它的基本工作就是管理分配任務(wù)到不同的服務(wù)器穆端,并且取得結(jié)果。至于說服務(wù)器之間是如何進(jìn)行通信的仿便?這個(gè)Celery本身不能解決徙赢。所以,RabbitMQ作為一個(gè)消息隊(duì)列管理工具被引入到和Celery集成探越,負(fù)責(zé)處理服務(wù)器之間的通信任務(wù)。
隨著開發(fā)的功能需求越來越多窑业,Django下的app也越來越多钦幔,這就帶了發(fā)布上的不方便,每次發(fā)布版本都需要重啟所有的Django服務(wù)常柄,如果發(fā)布遇到問題鲤氢,只能加班解決了搀擂。而且單個(gè)Django工程下的代碼量也越來越多,不好維護(hù)卷玉。
2哨颂、服務(wù)拆分
前面設(shè)計(jì)的app內(nèi)高類聚,app之間低耦合是為服務(wù)拆分做鋪墊的相种,首先先把公用的代碼抽離出來威恼,實(shí)現(xiàn)一個(gè)公用的庫,其他的還是公用寝并。估計(jì)當(dāng)數(shù)據(jù)量增加后箫措,要對redis以及mysql進(jìn)行優(yōu)化,可以分庫分表衬潦,后續(xù)還需要拆分業(yè)務(wù)斤蔓,這個(gè)要看原來的代碼整潔度和互相依賴程度。
Nginx Push Module镀岛,長連接最大數(shù)量不夠弦牡,使用Tornado + ZeroMQ實(shí)現(xiàn)了tormq服務(wù)來支撐消息通知。
問題:
隨著業(yè)務(wù)拆分漂羊,繼續(xù)使用Nginx維護(hù)配置非常麻煩驾锰,經(jīng)常因?yàn)樾薷腘ginx的配置引發(fā)調(diào)用錯誤。每一個(gè)服務(wù)都有一個(gè)完整的認(rèn)證過程拨与,認(rèn)證又依賴于用戶中心的數(shù)據(jù)庫稻据,修改認(rèn)證時(shí)需要重新發(fā)布多個(gè)服務(wù)。
前面二層的架構(gòu)均已實(shí)現(xiàn)买喧,后續(xù)的微服務(wù)以及領(lǐng)域驅(qū)動設(shè)計(jì)由于我還未涉及到(我之前工作是使用Java做的微服務(wù))捻悯,所以在此貼出一位python開發(fā)工程師的解決辦法。
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注冊服務(wù)地址到Kong蔗蹋,并加載api需要使用插件何荚。
為了解決相互調(diào)用的問題,維護(hù)了一個(gè)基于gevent+msgpack的RPC服務(wù)框架doge猪杭,借助于etcd做服務(wù)治理餐塘,并在rpc客戶端實(shí)現(xiàn)了限流,高可用皂吮,負(fù)載均衡這些功能戒傻。
在這個(gè)階段最難的技術(shù)選型税手,開源的API網(wǎng)關(guān)大多用Golang與OpenResty(lua)實(shí)現(xiàn),為了應(yīng)對我們業(yè)務(wù)的需要還要做定制需纳。前期花了1個(gè)月時(shí)間學(xué)習(xí)OpenResty與Golang芦倒,并使用OpenResty實(shí)現(xiàn)了一個(gè)短網(wǎng)址服務(wù)shorturl用在業(yè)務(wù)中。最終選擇Kong是基于Lua發(fā)布的便利性不翩,Kong的開箱即用以及插件開發(fā)比較容易兵扬。性能的考量倒不是最重要的,為了支撐更多的并發(fā)慌盯,還使用了云平臺提供的LB服務(wù)分發(fā)流量到2臺Kong服務(wù)器組成的集群周霉。集群之間自動同步配置。
餓了么維護(hù)一個(gè)純Python實(shí)現(xiàn)的thrift協(xié)議框架thriftpy亚皂,并提供很多配套的工具, 如果團(tuán)隊(duì)足夠大俱箱,這一套RPC方案其實(shí)是合適的,但是我們的團(tuán)隊(duì)人手不足灭必,水平參差不齊狞谱,很難推廣這一整套學(xué)習(xí)成本高昂的方案。最終我們開發(fā)了類Duboo的RPC框架doge禁漓,代碼主要參考了weibo開源的motan跟衅。
4. 領(lǐng)域驅(qū)動設(shè)計(jì)
在這一架構(gòu)中我們嘗試從應(yīng)用服務(wù)中抽離出數(shù)據(jù)服務(wù)層,每一個(gè)數(shù)據(jù)服務(wù)包含一個(gè)或多個(gè)界限上下文播歼,界限上下文類只有一個(gè)聚合根來暴露出RPC調(diào)用的方法伶跷。數(shù)據(jù)服務(wù)不依賴于應(yīng)用服務(wù),應(yīng)用服務(wù)可以依賴多個(gè)數(shù)據(jù)服務(wù)秘狞。有了數(shù)據(jù)服務(wù)層叭莫,應(yīng)用就解耦了相互之間的依賴,高層服務(wù)只依賴于底層服務(wù)烁试。
出處:https://zhu327.github.io/2018/07/19/python/后端架構(gòu)演進(jìn)/