來源:zhu327.github.io/2018/07/19/python後端架構演進php
來騰訊以前在前公司作了3年的後端開發,經歷一款SaaS產品從0到10(尚未到100, 哈哈哈)的過程,3年間後端的架構逐步演變,在微服務的實踐過程當中遇到的問題也愈來愈多,在這裏總結下。python
產品是一款服務於人力資源的SaaS在線服務,面向HR有Web Android/iOS 小程序多個客戶端,後端採用RESTful風格API來提供服務。主要使用Python語言,方便快速迭代。git
架構的演進經歷了4個大的階段:1. MVC 2. 服務拆分 3. 微服務架構 4. 領域驅動設計程序員
1. MVC
項目剛開始的時候,後端同事不超過5個,這個階段主要的工做是實現產品的原型,沒有太多的考慮架構,使用Django來快速實現功能,DB的表結構設計好以後,抽象出功能View,因爲產品設計也很不完善,後端須要不少的預留設計,避免產品邏輯的變動帶來整個表結構的變更,在這個階段代碼上最重要的是肯定適合團隊的代碼規範,代碼檢查規則。github
![](http://static.javashuo.com/static/loading.gif)
總體上架構如上圖,Nginx負責負載均衡,分發流量到多個Django服務,Django處理邏輯,須要異步任務就交給Celery,而後數據量比較大的地方使用Redis作緩存。同時還有實時消息通知的須要使用了Nginx Push Module。面試
問題與優化方式:redis
一、Django併發性能差 使用uWSGI Master+Worker 配合 gevent 攜程支持高併發數據庫
二、Redis鏈接數過多 使用redis-py自帶的鏈接池來實現鏈接複用django
三、MySQL鏈接數過多 使用djorm-ext- pool(https://github.com/djangonauts/djorm-ext-pool)鏈接池複用鏈接小程序
四、Celery配置gevent支持併發任務
隨着開發的功能愈來愈多,Django下的app也愈來愈多,這就帶了發佈上的不方便,每次發佈版本都須要重啓全部的Django服務,若是發佈遇到問題,只能加班解決了。並且單個Django工程下的代碼量也愈來愈多,很差維護。
2. 服務拆分
隨着後端團隊的壯大,分給每一個同事的需求也愈來愈細,若是繼續在一個工程裏面開發全部的代碼,維護起來的代價過高,而咱們的上一個架構中在Django裏面已經按模塊劃分了一個個app,app內高類聚,app之間低耦合,這就爲服務的拆分帶來了便利。
拆分的過程沒有遇到太大的問題,初期的拆分只是代碼的分離,把公用的代碼抽離出來實現一個公用的Python庫,數據庫,Redis仍是共用,隨着負載的增長,數據庫也作了多實例。
![](http://static.javashuo.com/static/loading.gif)
如上圖,服務之間儘可能避免相互調用,須要交互的地方採用http請求的方式,內網的調用使用hosts指向內網地址。
問題與優化方式:
Nginx Push Module因爲長時間沒有維護,長鏈接最大數量不夠,使用Tornado + ZeroMQ實現了tormq(https://github.com/zhu327/tormq)服務來支撐消息通知
服務之間的調用採用http的方式,而且要求有依賴的服務主機配置hosts指向被調用的地址,這樣帶來的維護上的不方便。以及在調用鏈的過程當中沒有重試,錯誤處理,限流等等的策略,致使服務可用性差。
隨着業務拆分,繼續使用Nginx維護配置很是麻煩,常常由於修改Nginx的配置引起調用錯誤。每個服務都有一個完整的認證過程,認證又依賴於用戶中心的數據庫,修改認證時須要從新發布多個服務。
3. 微服務架構
![](http://static.javashuo.com/static/loading.gif)
首先是在接入層引入了基於OpenResty的Kong API Gateway,定製實現了認證,限流等插件。在接入層承接並剝離了應用層公共的認證,限流等功能。在發佈新的服務時,發佈腳本中調用Kong admin api註冊服務地址到Kong,並加載api須要使用插件。
爲了解決相互調用的問題,維護了一個基於gevent+msgpack的RPC服務框架doge,藉助於etcd作服務治理,並在rpc客戶端實現了限流,高可用,負載均衡這些功能。
在這個階段最難的技術選型,開源的API網關大多用Golang與OpenResty(lua)實現,爲了應對咱們業務的須要還要作定製。前期花了1個月時間學習OpenResty與Golang,並使用OpenResty實現了一個短網址服務shorturl用在業務中。
最終選擇Kong是基於Lua發佈的便利性,Kong的開箱即用以及插件開發比較容易。性能的考量倒不是最重要的,爲了支撐更多的併發,還使用了雲平臺提供的LB服務分發流量到2臺Kong服務器組成的集羣。集羣之間自動同步配置。
餓了麼維護一個純Python實現的thrift協議框架thriftpy,並提供不少配套的工具, 若是團隊足夠大,這一套RPC方案實際上是合適的,可是咱們的團隊人手不足,水平良莠不齊,很難推廣這一整套學習成本高昂的方案。最終咱們開發了類Duboo的RPC框架doge,代碼主要參考了weibo開源的motan。
4. 領域驅動設計
![](http://static.javashuo.com/static/loading.gif)
在這一架構中咱們嘗試從應用服務中抽離出數據服務層,每個數據服務包含一個或多個界限上下文,界限上下文類只有一個聚合根來暴露出RPC調用的方法。數據服務不依賴於應用服務,應用服務能夠依賴多個數據服務。有了數據服務層,應用就解耦了相互之間的依賴,高層服務只依賴於底層服務。
在我離職時領域驅動設計還在學習設計階段,尚未落地,可是我相信前公司的後端架構必定會往這個方向繼續演進。
# 總結
架構的設計,技術的選型,不能徹底按照流行的技術走,最終仍是服務於產品,服務於客戶的需求。設計過程當中因爲團隊,人員的結構問題,有不少的妥協之處,如何在妥協中找到最優解纔是最大的挑戰。
Service Mesh這種新一代的微服務架構正在成爲主流,雖然如今的工做與微服務無關了,可是也還會繼續關注學習。
END
推薦閱讀 把 Spring Cloud 給拆了!詳解每一個組件的做用 Java中5種List的去重方法及它們的效率對比,你用對了嗎? 是時候扔掉 Postman 了,試試 IntelliJ IDEA 自帶的高能神器! 2020年11月程序員工資統計,平均14327元 這應該是全網最全的Git分支開發規範手冊~ 最後,推薦給你們一個有趣有料的公衆號:寫代碼的渣渣鵬,7年老程序員教你寫bug,回覆 面試或資源 送一你整套開發筆記 有驚喜哦