微服務架構沉思錄
1. 微服務架構沉思錄
關於學習微服務架構中的一些筆記和思考
1.1 微服務架構中的進程間通信
當一個單體架構應用程序升級爲微服務架構之後,發生一個最爲明顯的變化是各個模塊之間的調用不再是本地方法的調用,而是基於進程間的通信。
進程間通信的本質其實就是交換消息,微服務架構本質也是一種分佈式架構。
1.1.1 進程間通信技術
進程間通信的技術一般有兩種選擇:基於同步請求響應的通信機制 和基於異步的消息通信機制。
1.1.1.1 基於同步請求響應的通信機制
- 比如SOAP協議(HTTP XML),REST(HTTP JSON),gRPC(二進制序列化,Socket通信,NIO動態代理,反射)
- 使用RPC協議的有阿里的Dubbo ,
Google gRPC
,Facebook 的Thrift
,Twitter 的 Fingle.- 通信框架有Netty , MINA
- gPRC使用ProtocolBffers作爲消息格式
- REST協議有資源,動詞(GET/PUT/DELTE/POST)的概念
RPC 到底是什麼?
用一張圖解釋:
正如上圖所示,
- 所謂的RPC的核心無非是微服務A 將請求的方法和參數 序列化成二進制數據
- 然後通過Netty類網絡通訊框架,將二進制數據通過Scoket 發送到另外一個B微服務
- 另外一個B微服務將收到的請求反序列化解析出來,然後將執行結果再次序列化成二進制後,通過Socket 返回。
一個RPC框架大致需要動態代理、序列化、網絡請求、網絡請求接受(netty實現)、動態加載、反射這些知識點。
1.1.1.1.1 REST 的好處
REST好處在於:
- 非常簡單,大家都很熟悉
- 方便測試
- 防火牆支持友好
- 不需要中間代理,簡化系統架構
1.1.1.1.2 REST 的弊端
REST弊端在於:
- 支持請求/響應方式通信
- 在調用期間必須保持都在線
- 客戶端必須知道服務端的地址(URL),必須使用服務發現機制
- 多個更新映射到HTTP動詞
1.1.1.1.3 gRPC好處
gRPC好處在於:
- 設計複雜更新操作的API非常簡單
- 具有高效,緊湊進程間通信機制,尤其在交換大量消息時
- 支持在遠程過程調用和消息傳遞過程中使用雙向流式消息方式
- 實現了客戶端和用各種語言編寫的服務端之間的互操作性
- 可以使用Erlang 更好利用多核資源
1.1.1.1.4 gRPC弊端
gPRC弊端在於:
- 比REST/JSON 的API機制相比需要更多的工作
- 舊式防火牆可能不支持HTTP/2
1.1.1.2 基於異步的機遇消息通信機制
- AMQP,比如RabbitMQ,IBM MQ,
- STOMP
但總之不管哪種方式都需要使用服務發現。
1.2 微服務架構中的分佈式事務
在單體架構中也許我們可以使用 Spring 採取註解方式@Transaction
實現本地事務。
但是微服務架構中卻不再適用,分佈式事務的本質是同步進程間通信。
這是因爲在微服務架構中,各個微服務處於不同的進程中,並沒有辦法像單體架構中那樣操作本地事務。
而且一些消息中間件並不支持分佈式事務,比如 Rabbit MQ, Apache Kafka,除非你打算放棄不使用這些。
因此在微服務架構中有些人提出使用分佈式事務的解決方案:兩階段提交(JTA) 或三階段提交(JTA)。
那麼什麼是兩階段提交和三階段提交呢?
1.2.1 兩階段提交
所謂的兩階段提交原理圖如下所示:
所謂兩階段提交的設計思路總結如下:
- 引入一個協調者通知每個微服務參與者各自執行自己的本地事務但是不要提交,執行結果都要反饋給協調者。
- 協調者如果收到任意一個微服務參與者事務執行失敗,那麼就通知其他所有微服務,進行回滾。
- 協調者如果收到所有微服務參與者都執行成功,那麼通知所有微服務可以開始提交自己執行的本地事務。
1.2.2 三階段提交
所謂的三階段提交則是如下圖所示:
- 引入一個協調者詢問每個微服務參與者能否參加事務執行?
- 協調者如果收到任意一個微服務參與者不能執行,那麼就通知其他所有微服務,進行回滾。
- 協調者如果收到所有微服務參與者都說可以執行,就通知所有微服務執行自己的本地事務但是不提交。
- 協調者詢問所有微服務的本地事務執行結果,如果都執行了,沒問題,那麼下發通知,全部執行事務提交。
- 協調者接受微服務所有的事務執行結果。
上面無論是兩階段提交還是三階段提交,其實都有一個問題:
那就是事務執行期間,所有微服務必須都有空執行本地事務,其中一個微服務不在,就無法開始。
因此這種並不是最好的解決方案。
1.2.3 更好的解決方案
另外一種解決思路是使用數據庫做消息臨時緩存表。
設計思路如下:
- 協調者讓每個微服務執行提交自己的本地事務
- 然後將每個微服務的執行結果存放到數據庫或消息中間件的臨時緩存表中。
- 只要一旦其中一個出錯,啓動補償事務回滾,所有微服務撤銷之前的事務。
1.3 微服務架構中的 API Gateway
在微服務架構中,如果沒有API Gateway 會怎麼樣?
我們通過上圖可以看出,這種架構存在嚴重的問題。
- 客戶端請求次數過多導致用戶體驗不佳
- 每個客戶端需要多次網絡請求,那麼如果出現一旦網絡卡頓,很可能影響用戶體驗
- 調用很多微服務,需要在本地進行數據組合
- 手機端如果網絡請求次數過多,也會導致手機電量下降過快
- 客戶端到服務端的請求次數很多,如果想提供較好的服務,必須長期保持較高的帶寬。
1.3.2 API Gateway 模式
那麼如果我們嘗試引入 API Gateway 呢?
API Gateway在這裏充當了反向代理的角色,屏蔽了其他微服務之間的調用。
任何客戶端調用微服務都統一通過API Gateway 才能調用微服務。
由於API Gateway和其他微服務可能處於局域網內,因此就算請求很多微服務,速度也會比之前快很多。
但是這種架構設計仍然存在一些問題。
什麼問題呢?
- 移動客戶端,和網頁客戶端可能因爲平臺不同,功能不同,需要不同的API 接口。
- API Gateway 開發團隊工作量過於巨大,需要整合所有微服務以及客戶端的API 設計需求。
存在問題如下所示:
1.3.2 後端前置模式
解決上述問題的一種比較好的處理方式是使用後端前置模式。
那麼什麼是後端前置模式呢?
這種模式的設計思路是這樣的:
- 我們只提供公共層的方法調用,相當於把各個微服務整合到公共層。
- 其他每一種客戶端都由自己的客戶端去維護自己的API 層。
1.3.1 API Gateway 的選擇?
關於如何首先API Gateway 有多種選擇,可以選擇使用一些成熟的產品框架,也可以自己開發。
比如Netflix 的Zuul 經過Spring 官方團隊封裝後,推出的Spring Cloud Zuul。
當然,還有更好的選擇是使用Spring Cloud Gateway
之所以更推薦使用SpringCloud Gateway是因爲它是一個基於Spring Framework 5,Spring Boot 2 和Spring Webflux
構建的API Gateway 框架,構建在Project Reactor 之上,而Project Reactor 是一個基於NIO 的JVM 響應式框架。
1.4 關於微服務的理解
每個微服務都是自治,是可以獨立部署的,不依賴其他微服務。
每個微服務部署一般部署在不同服務器上的, 從部署方面來看是分佈式部署。(如果沒那麼多服務器也可能單個機器多個端口方式部署,也可以容器K8S 對這些微服務服務編排)
單體架構舉個例子就像車原來是一體的,如果壞了,整個車都得換,現在微服務一個車拆分成了多個零件,壞了只需要換其中一個零件即可,甚至可以添加新的部件,可擴展性強。
微服務本質是各個微服務之間跨進程通信,可以通過SOAP 協議XML 方式發送,也可以通過HTTP REST JSON方式發送,也可以通過RPC 序列化類Socket 通信。
每個微服務的請求統一入口都是通過API Gateway
權限驗證和攔截也應該在API Gateway 處進行驗證,不知道你們有沒有聽過跨站拿站的故事。
試想下如果你有多個微服務,其中很多微服務密不透風,其中一個微服務一旦被攻破由於這臺機器位於內網,那麼將可能也會被攻破,尤其如果其他微服務也部署在這臺服務器上的話。
每個微服務可以用多種語言麼?理論上是可以的,但是建議統一使用一種語言,這是因爲雖然每個微服務都是獨立的可以用不同的語言實現,但是服務註冊中心並不見得支持所有的語言,不是麼?
爲什麼需要服務註冊?
第一:微服務與微服務之間的請求是跨進程的,無論選擇基於SOAP 協議的XML 傳輸還是基於 REST JSON 傳輸,或選擇RPC 框架如Dubbo實現服務治理,都需要知道請求的地址。
第二:微服務的部署的IP 一般都是動態IP分配不可能那麼多靜態IP 給你用。
第三:彈性擴容,需要動態擴容機器部署,這些後期的一些部署IP 等信息是無法提前預知的。
因此必須有一個高可用的服務治理解決方案。
更多的思考
如果你打開官網,應該能找到這麼一張圖,對於java 後端開發者的我們,會有怎麼樣的啓示呢?
本篇完~