CTO之瞳-後端系統1-代碼框架+微服務初探

有了數據庫,接下來聊一聊直接操控數據的系統——後端系統。這其實是一個非常大的話題,可以用來寫後端系統的開發語言有java/php/nodejs/python/C#/go等,常用的體系有.NET/springboot,加上各類的概念規範,比如MVC/ Servlet,還有層出不窮的框架/庫/工具,每當想起這些,總會冒出一個念頭:wtf,還是《資治通鑑》容易點。

範圍這麼廣,很多領域都沒接觸過,那寫什麼好呢?其實挺簡單,打開獵聘找下CTO/架構師/研發總監之類的職位,看看職位要求裡都寫了些什麼,答案就躍然紙上了。

Java,spring boot,基於spring cloud的後端微服務體系,就是此部分的主角了。每一種技術都有自身的優劣勢,每一個技術人選擇什麼技術棧都有自己的判斷。重要的不是選了什麼,而是選擇以後堅持走下去。在技術的領域裡,需要廣度,同時更需要深度。

注:這裡的廣度,指的是可以端到端的完成一個業務體系搭建,也就是常說的全棧技術。並不是指什麼都摸兩下,比如做前端的,今天用vue,明天用react,後天又換成angular;做後端的今天java,明天php,後天nodejs,這不能稱之為廣度。當然,大神除外。

其實選什麼開發語言,用什麼技術框架與體系,這些也並不重要;重要的是:按照市場以及用戶的需求,在合理的成本與時間範圍下,搭建出可持續發展的技術系統。

正式開始吧。

1 代碼框架

前面扯了一大段頂層的東西,為什麼要從代碼框架開始說起?因為良好的代碼框架對應的是良好的邏輯分層,而技術裡的分層實際上是一種形式的社會分工。亞當斯密在《國富論》裡曾經寫到過:“勞動生產力最大的改進,以及勞動在任何地方運作或應用中所體現的技能、熟練和判斷的大部分,似乎都是勞動分工的結果。”

先上代碼(沒有代碼、系統框圖、流程圖的技術類文章都是耍流氓,相反的,大段複製粘貼代碼的技術類文章也是耍流氓 ^^)。

https://github.com/ctoeyes/basicweb-ui

https://github.com/ctoeyes/basicweb-service

搭建了一個簡單的web註冊與登錄demo,後端基於Springboot,前端基於React+MaterialUI,數據庫MySql。可以訪問www.ctoeyes.cn查看效果,香港的服務器,速度較慢。主域名www.ctoeyes.com還在備案審核中,一旦審核通過,會把demo遷移過去。

Demo非常簡單,沒用https,數據庫連接的密碼是明文存儲,API也沒防暴力攻擊,主要用來說明代碼框架。在下一篇後端系統2裡,從單體demo改為微服務demo時,這些點都會一一改造。

這個後端service的代碼框架長成這樣:

CTO之瞳-後端系統1-代碼框架+微服務初探

主要由幾個部分組成

1. controller:接收前端發來的request並組裝相應的response返回

2. service:處理具體的業務邏輯

3. repository:數據倉庫,處理DB的CRUD操控

4. pojo:簡單對象的定義

  • entity:與數據庫表一一對應
  • dto:與業務數據模型一一對應,例如view object,form object
  • enums
  • response:http response的封裝

5. common

  • config:配置類
  • utils:工具類
  • const:常量

以上幾個部分的具體名稱依據個人習慣或者公司規範會有所不同,但大體上都分這幾個層級。其中,有兩個點較難把握。

1.1 controller和service怎麼分?

如果看demo裡的代碼,controller似乎是多餘的,只是簡單的把前端的login和register請求做了一次轉發。是的,在簡單的場景下,完全沒有必要照本宣科,把controller與service拆分開來。

但是,如果系統功能進一步迭代,這種分層就有必要了。我們先認識一下MVC(Model/View/Controller)模型(如下圖,圖片來源於網絡),MVC是一種設計規範或者理念,任何複雜的業務都可以拆分成三類模塊:Model(Service)、View與Controller。


CTO之瞳-後端系統1-代碼框架+微服務初探


View負責頁面交互;Controller作為控制層,決定調用哪些服務處理View傳來的請求並返回相應的數據;Model,也即Service負責處理具體的業務邏輯,並調用數據庫做持久化處理。

以最簡單的login登錄場景來舉例:

1. View負責搭建登錄頁面,組裝頁面上用戶輸入的數據傳給Controller,並負責把Controller回傳的數據顯示在頁面上。


2. Controller接收View傳來的login請求,在簡單的場景下只需把login請求轉發給Model(service)層做處理,並接收Model的處理結果回傳給View顯示,就像demo裡那樣。

但假設,用戶交互的不僅僅有網頁瀏覽器,我們還設計了一個移動端APP,同樣也有登錄請求,這時Controller就派上用場了。因為屏幕大小的限制,APP登錄後頁面所展示的用戶信息一般與網頁端有所不同,偷懶的做法是把數據庫裡所有用戶有關的數據全部回傳給前端,由前端選擇哪些數據要顯示,這顯然不符合常規的設計思路。

通常的做法,由後端組裝每一個View所需的數據,組成一個VO(View Object)回傳給前端。那麼既然網頁與APP顯示的數據不同,很顯然我們需要兩個不同的VO,這個工作的調度應該在Controller層面完成。

網頁端來的登錄請求,Controller首先調用AccountService裡的login接口,完成登錄的驗證並拿到登錄後獲取的uid以及token;接著調用UserInfoService,獲取用戶的一些基礎信息,比如暱稱、頭像等;再調用HistoryService獲取用戶最近瀏覽的幾篇文章信息。然後把這些數據打包成一個MainPagePcObject回傳給網頁端,這樣用戶在登錄後的主頁上可以看到個人信息以及最近的瀏覽記錄。

APP來的登錄請求,,Controller首先調用AccountService裡的login接口,完成登錄的驗證並拿到登錄後獲取的uid以及token;接著調用UserInfoService,獲取用戶的一些基礎信息,比如暱稱、頭像等。然後把這些數據打包成一個MainPageMobileObject回傳給APP,這樣用戶在登錄後的主頁上可以看到個人信息(由於屏幕大小的限制,APP主頁上放不下近期瀏覽記錄,所以Controller並沒有調用HistoryService)。

Controller根據不同View的請求,調度不同的Service,組成適當的DTO回傳給View顯示(以上的代碼實現,將在後續講述前端系統那一篇時加入)。

3. Model(Service)負責處理具體的業務邏輯,比如AcountService負責處理賬號的註冊/登錄/登出等操作。UserInfoService負責新增/修改/刪除用戶頭像、暱稱、愛好等個人信息。


1.2 Pojo裡的對象有哪些,如何區分?

Pojo = Plain Ordinary Java Object,也就是簡單java對象,意味著只有數據不含任何邏輯操作的對象。我們可以把response,enums放在裡面,還有與庫表一一對應的entity放在裡面,接著還有各種眼花繚亂的DTO、VO、FO、DO、PO也可以放在裡面。

這些xxO讓人不禁聯想起公司裡的各種CxO,怎麼區分呢?按照個人的理解,畫了張圖,請看。

CTO之瞳-後端系統1-代碼框架+微服務初探

層層封裝的好處是底層的改動,例如數據庫表的變動並不會影響到上層(比如View)的代碼。正所謂髒活累活後端都做了,前端只要負責美和優雅。

但是分層太多,也會增加代碼的複雜度,所以一般的項目裡只會做兩層數據封裝,一層與庫表對應的Entity,另一層與業務對應的DTO。


2 微服務初探

很久很久很久以前(其實也就6-7年前吧,但在技術領域可以算很久遠了),用java寫的後端系統很多都是一套代碼集成在一起,有的甚至與前端頁面打成一個war包直接部署。

這種方式,運維起來很簡單,但是

- 如果業務代碼當中一個service出錯,可能會導致整個服務不可用

- 各個service的迭代節奏,相互影響

隨著互聯網業務體量越來越大,以及涉及的業務種類越來越多,這樣的技術架構不能跟上業務的發展需求。回顧文初提過的觀點:重要的是按照市場以及用戶的需求,在合理的成本與時間範圍下,搭建出可持續發展的技術系統。很顯然,單體架構無法持續發展下去了,因此微服務架構應運而生。一種技術,必須有旺盛的業務與用戶需求,才可能進入快速發展的通道。

微服務的核心概念就是松耦合,把一個業務中相對獨立的部分拆出來做成一個獨立的服務系統,可以獨立開發、獨立測試、獨立部署。而一套完整的業務系統又由很多個微服務組成。

2.1 Spring & Spring Boot & SpringCloud

Java微服務的框架,必然提到基於Spring體系的Spring Cloud,在此簡單闡述一下spring,spring boot,spring cloud的關係。

2.1.1 Spring

更準確的說應該是spring framework,一套基於依賴注入的java開發框架,spring承擔的角色是各個java類之間的依賴關係管理者。

2.1.2 Spring boot

基於spring,提供了面向REST(REpresentational State Transfer)設計風格的框架,通過簡單的註解,可以快速搭建起一套web服務。

關於REST風格,推薦讀一下2000年被首次提出時的論文,傳送門如下

英文版

https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf

中文版

https://www.docin.com/p-525700155.html


2000年我在幹嘛?2000年北京的夏天,雖然20年過去了,依然難忘。

2.1.3 Spring Cloud

基於spring boot(依賴注入+面向REST),納入了許多經過生產驗證的開源微服務組件,方便使用者快速的搭建一套微服務框架。例如netflix的微服務三大件:服務註冊與發現Eureka,網關服務zuul,熔斷器Hystrix。

2.2 常用微服務組件與架構

技術上很多設計都來源於生活,例如設計模式裡的工廠模式、委派模式等。微服務的組件同樣也是來源於我們的日常生活,所以就從生活開始吧。想象一下我們去一間商場買東西的場景,順著這個場景來介紹Spring Cloud中各個關鍵組件。

服務網關 – Zuul

走進一間商場或者一個公共設施,第一道關其實是門衛;而離開一間商場,最後一道關也是門衛。平時我們的出入彷彿感受不到門衛的存在,但當你拿著一把刀試圖走進商場的時候,門衛必然很快出現並把你摁倒在地;或者當你離開商場時,試圖帶著並未結賬的商品一起離開,門衛也會出現把你摁倒在地。同理,在微服務的體系裡也有一個門衛起到網關的作用,Spring Cloud裡用的是Zuul(不過已經可以看到spring在推Gateway組件來代替Zuul)。

不論你去商場買什麼東西、辦什麼事,都需要通過門衛這一關,同理所有的服務請求都要通過Zuul。Zuul可以攔截非法的請求,並對請求做統一的鑑權認證。

Zuul還具備負載均衡的作用。Miss1,請問休息間往哪走?往前右轉50米。Miss 2,請問休息間怎麼走?往前右轉50米。Miss 100,請問休息間怎麼走?往前右轉50米。

休息間保潔員:你md能不能把要休息的引到別處去,我這裡排隊都繞兩圈了。Zuul:抱歉,忘記開啟負載均衡了,重新來。

Miss 1,請問休息間往哪走?往前右轉50米。Miss 2,請問休息間怎麼走?上2樓左轉50米。Miss 3,請問休息間往哪走?往前右轉50米。Miss 4,請問休息間怎麼走?上2樓左轉50米。這就是負載均衡。當一個微服務有多臺實例時,同一類請求會被均勻分配至不同的實例。

以上負載均衡實現的前提,需要導購妹子Eureka知曉商場裡有兩個休息間,並且知道它們的具體位置。

服務發現與註冊 – Eureka

順利進入商場後,由於龐大的空間,我們並不知道該去哪裡買心儀的東西。於是通常我們都會走到導購臺,詢問導購妹子:“請問,xx成人用品在哪裡有售?”導購妹子白你一眼,但依然會盡職的告知:“請往左走100米進電梯,直達地下18層。”

在微服務的世界裡,同樣有一個導購臺,spring cloud中用的是Eureka,每一個微服務啟動時都要向Eureka報備一下:“報告,我是xx成人用品服務,我的位置在地下18層”。這樣當Zuul收到訪問請求後,Eureka可以告訴Zuul把請求發往哪裡,可能是地下18層,也可能是去天台。

斷路器 – Hystrix

昨天商場打了個廣告,N95口罩有售,5個只要199,於是今天湧入了大量的人群購買口罩。絡繹不絕的人流被導購臺引導到了口罩專櫃,人群把口罩專櫃的售賣妹子圍在了中間,在七嘴八舌的詢問聲中,售賣妹子崩潰了,無法再回答任何顧客的提問或是售出口罩。

Hystrix登場,它監控到了以上情況,於是在到口罩專櫃的路上,豎了一面告示牌:“N95口罩已售光!明日請早!”於是人群紛紛掉頭。

售賣妹子逐漸恢復了常態,處理完了一個個圍在櫃檯前的用戶需求。Hystrix於是撤掉了告示牌,有口罩購買需求的客戶又繼續前往口罩專櫃。

當某一個微服務響應異常時,Hystrix對於新入的請求按照事先設定的策略給予立即的回覆(一般是拒絕),而不是讓請求繼續積壓形成堰塞湖,使得服務難以迴歸正常。

消息隊列 – Kafka或RabbitMQ

今天商場洗衣機搞活動,顧客買的很多。

第1臺:顧客-我要下單;售賣員-秒接單;倉庫-庫存確認;物流-我查下這個送貨時間有沒有車,還有送貨地址是不是OK,1分鐘過去了,OK可以送;顧客-成交。

第2臺:顧客-我要下單;售賣員-秒接單;倉庫-庫存確認;物流-我查下這個送貨時間有沒有車,還有送貨地址是不是OK,1分鐘過去了,OK可以送;顧客-成交。

第3臺:顧客-我要下單;售賣員-秒接單;倉庫-庫存確認;物流-我查下先,送貨師傅聯繫不上呀,太累了,先抽根菸,5分鐘過去了;顧客-靠,等不了了,不買了。

第4臺:顧客-我要下單;售賣員-秒接單;倉庫-庫存確認;物流-我查下先,啊呀,誰吃香蕉亂扔皮,去醫院先,50分鐘過去了;顧客-靠,不等了。

加入消息緩存機制之後

第1-n臺:顧客-我要下單;售賣員-秒接單;倉庫-庫存確認;物流(採用消息緩存)-接到送貨請求,後續電話聯繫反饋;顧客-成交。

一個不需要實時反饋的業務點,可以利用消息隊列先接受請求,按照先進先出的原則一個個有序的處理,處理完畢後再更新結果(比如各類在線商城裡的物流派單)。

業務監控 – Spring Boot Admin

今天顧客特別多,商場巡查溜了一圈。李姐呀,這一樓的休息室衛生沒跟上呀,再努努力,不然客戶投訴了;導購臺貌似一切正常,乾的不錯;口罩專櫃運轉正常,不錯不錯;倉庫,咦,倉庫裡怎麼沒人?怎麼還有煙出來?靠,趕緊打電話報警。

業務系統是否運轉正常,需要一些基礎的監控點進行監控,一旦有異常立即告警,提醒相關人員介入處理。

以上列舉了常用的幾個微服務組件或者框架,個人概念中的一個完整中小型業務微服務體系示意圖如下(未包含數據分析平臺)。

CTO之瞳-後端系統1-代碼框架+微服務初探

補充說明,微服務有它的好處,但也一定要根據實際業務來判斷是否有必要上微服務體系,否則可能事倍功半。比如,一個十人左右的貿易公司,建一個IT系統跟蹤貿易單的狀態,此外再無其他IT需求,這種情況直接單體結構服務就好了。

下一篇,寫一個簡單的微服務系統來闡述以上提到的各個組件與框架。


分享到:


相關文章: