·
- 原創 | Java 2020 超神之路,很肝~
- 中文詳細注釋的開源項目
- RPC 架構 Dubbo 源碼解析
- 網絡應用架構 Netty 源碼解析
- 消息中間件 RocketMQ 源碼解析
- 資料庫中間件 Sharding-JDBC 和 MyCAT 源碼解析
- 作業排程中間件 Elastic-Job 源碼解析
- 分布式事務中間件 TCC-Transaction 源碼解析
- Eureka 和 Hystrix 源碼解析
- Java 并發源碼·
- 1. MVC
- 2. 服務拆分
- 3. 微服務架構
- 4. 領域驅動設計
- 總結
來騰訊之前在前公司做了3年的後端開發, 經曆一款SaaS産品從0到10(還沒有到100, 哈哈哈)的過程, 3年間後端的架構逐漸演變, 在微服務的實踐過程中遇到的問題也越來越多, 在這裡總結下.
産品是一款服務于人力資源的SaaS線上服務, 面向HR有Web Android/iOS 小程式多個用戶端, 後端采用RESTful風格API來提供服務. 主要使用Python語言, 友善快速疊代.
架構的演進經曆了4個大的階段: 1. MVC 2. 服務拆分 3. 微服務架構 4. 領域驅動設計.
項目剛開始的時候, 後端同僚不超過5個, 這個階段主要的工作是實作産品的原型, 沒有太多的考慮架構, 使用Django來快速實作功能, DB的表結構設計好之後, 抽象出功能View, 由于産品設計也很不完善, 後端需要很多的預留設計, 避免産品邏輯的變更帶來整個表結構的變動, 在這個階段代碼上最重要的是确定适合團隊的代碼規範, 代碼檢查規則.
整體上架構如上圖, Nginx負責負載均衡, 分發流量到多個Django服務, Django處理邏輯, 需要異步任務就交給Celery, 然後資料量比較大的地方使用Redis做緩存. 同時還有實時消息通知的需要使用了Nginx Push Module.
問題與優化方式:
- Django并發性能差 使用uWSGI Master+Worker 配合 gevent 攜程支援高并發
- Redis連接配接數過多 使用redis-py自帶的連接配接池來實作連接配接複用
- MySQL連接配接數過多 使用djorm-ext-pool連接配接池複用連接配接
- Celery配置gevent支援并發任務
随着開發的功能越來越多, Django下的app也越來越多, 這就帶了釋出上的不友善, 每次釋出版本都需要重新開機所有的Django服務, 如果釋出遇到問題, 隻能加班解決了. 而且單個Django工程下的代碼量也越來越多, 不好維護.
随着後端團隊的壯大, 分給每個同僚的需求也越來越細, 如果繼續在一個工程裡面開發所有的代碼, 維護起來的代價太高, 而我們的上一個架構中在Django裡面已經按子產品劃分了一個個app, app内高類聚, app之間低耦合, 這就為服務的拆分帶來了便利. 拆分的過程沒有遇到太大的問題, 初期的拆分隻是代碼的分離, 把公用的代碼抽離出來實作一個公用的Python庫, 資料庫, Redis還是共用, 随着負載的增加, 資料庫也做了多執行個體.
如上圖, 服務之間盡量避免互相調用, 需要互動的地方采用http請求的方式, 内網的調用使用hosts指向内網位址.
- Nginx Push Module由于長時間沒有維護, 長連接配接最大數量不夠, 使用Tornado + ZeroMQ實作了tormq服務來支撐消息通知
服務之間的調用采用http的方式, 并且要求有依賴的服務主機配置hosts指向被調用的位址, 這樣帶來的維護上的不友善. 以及在調用鍊的過程中沒有重試, 錯誤處理, 限流等等的政策, 導緻服務可用性差. 随着業務拆分, 繼續使用Nginx維護配置非常麻煩, 經常因為修改Nginx的配置引發調用錯誤. 每一個服務都有一個完整的認證過程, 認證又依賴于使用者中心的資料庫, 修改認證時需要重新釋出多個服務.
首先是在接入層引入了基于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.
在這一架構中我們嘗試從應用服務中抽離出資料服務層, 每一個資料服務包含一個或多個界限上下文, 界限上下文類隻有一個聚合根來暴露出RPC調用的方法. 資料服務不依賴于應用服務, 應用服務可以依賴多個資料服務. 有了資料服務層, 應用就解耦了互相之間的依賴, 高層服務隻依賴于底層服務.
在我離職時領域驅動設計還在學習設計階段, 還沒有落地, 但是我相信前公司的後端架構一定會往這個方向繼續演進.
架構的設計, 技術的選型, 不能完全按照流行的技術走, 最終還是服務于産品, 服務于客戶的需求. 設計過程中由于團隊, 人員的結構問題, 有很多的妥協之處, 如何在妥協中找到最優解才是最大的挑戰.