今天閱讀了兩篇關(guān)于微服務(wù)的文章,總結一些筆記,簡(jiǎn)單翻譯了一篇文章。說(shuō)明:并沒(méi)有嚴格按照原文一字語(yǔ)句翻譯,有部分自己的理解,還有部分是意譯。
微服務(wù)(micro services)這個(gè)概念不是新概念,很多公司已經(jīng)在實(shí)踐了,例如亞馬遜、Google、FaceBook、Alibaba。微服務(wù)架構模式(Microservices Architecture Pattern)的目的是將大型的、復雜的、長(cháng)期運行的應用程序構建為一組相互配合的服務(wù),每個(gè)服務(wù)都可以很容易得局部改良。 Micro這個(gè)詞意味著(zhù)每個(gè)服務(wù)都應該足夠小,但是,這里的小不能用代碼量來(lái)比較,而應該是從業(yè)務(wù)邏輯上比較——符合SRP原則的才叫微服務(wù)。
暫且不討論大小問(wèn)題,讀者朋友你首先要考慮的是如何解決目前技術(shù)團隊遇到的開(kāi)發(fā)問(wèn)題、部署問(wèn)題。正是在解決這些問(wèn)題的過(guò)程中,才漸漸總結提煉出了微服務(wù)架構模式的概念。
微服務(wù)跟SOA有什么區別呢,可以把微服務(wù)當做去除了ESB的SOA。ESB是SOA架構中的中心總線(xiàn),設計圖形應該是星形的,而微服務(wù)是去中心化的分布式軟件架構。
接下來(lái)會(huì )討論以下幾個(gè)話(huà)題:
應用微服務(wù)的動(dòng)機,跟傳統巨石應用的比較
微服務(wù)的優(yōu)點(diǎn)與缺點(diǎn)
應用微服務(wù)架構設計時(shí)可能遇到的關(guān)鍵問(wèn)題(內部服務(wù)通信、分布式數據管理)
web應用程序發(fā)展的早期,大部分web工程是將所有的功能模塊(service side)打包到一起并放在一個(gè)web容器中運行,很多企業(yè)的Java應用程序打包為war包。其他語(yǔ)言(Ruby、Python或者C++)寫(xiě)的程序也有類(lèi)似的問(wèn)題。
假設你正在構建一個(gè)在線(xiàn)商店系統:客戶(hù)下訂單、核對清單和信用卡額度,并將貨物運輸給客戶(hù)。很快,你們團隊一定能構造出如下圖所示的系統。
這種將所有功能都部署在一個(gè)web容器中運行的系統就叫做巨石型應用。巨石型應用有很多好處:IDE都是為開(kāi)發(fā)單個(gè)應用設計的、容易測試——在本地就可以啟動(dòng)完整的系統、容易部署——直接打包為一個(gè)完整的包,拷貝到web容器的某個(gè)目錄下即可運行。
但是,上述的好處是有條件的:應用不那么復雜。對于大規模的復雜應用,巨石型應用會(huì )顯得特別笨重:要修改一個(gè)地方就要將整個(gè)應用全部部署(PS:在不同的場(chǎng)景下優(yōu)勢也變成了劣勢);編譯時(shí)間過(guò)長(cháng);回歸測試周期過(guò)長(cháng);開(kāi)發(fā)效率降低等。另外,巨石應用不利于更新技術(shù)框架,除非你愿意將系統全部重寫(xiě)(代價(jià)太高你愿意老板也不愿意)。
詳細一個(gè)網(wǎng)站在業(yè)務(wù)大規模爬升時(shí)會(huì )發(fā)生什么事情?并發(fā)度不夠?OK,加web服務(wù)器。數據庫壓力過(guò)大?OK,買(mǎi)更大更貴的數據庫。數據庫太貴了?將一個(gè)表的數據分開(kāi)存儲,俗稱(chēng)“分庫分表”。這些都沒(méi)有問(wèn)題,good job。不過(guò),老外的抽象能力比我們強,看下圖Fig2。
這張圖從三個(gè)維度概括了一個(gè)系統的擴展過(guò)程:(1)x軸,水平復制,即在負載均衡服務(wù)器后增加多個(gè)web服務(wù)器;(2)z軸擴展,是對數據庫的擴展,即分庫分表(分庫是將關(guān)系緊密的表放在一臺數據庫服務(wù)器上,分表是因為一張表的數據太多,需要將一張表的數據通過(guò)hash放在不同的數據庫服務(wù)器上);(3)y軸擴展,是功能分解,將不同職能的模塊分成不同的服務(wù)。從y軸這個(gè)方向擴展,才能將巨型應用分解為一組不同的服務(wù),例如訂單管理中心、客戶(hù)信息管理中心、商品管理中心等等。
將系統劃分為不同的服務(wù)有很多方法:(1)按照用例劃分,例如在線(xiàn)商店系統中會(huì )劃分出一個(gè)checkout UI服務(wù),這個(gè)服務(wù)實(shí)現了checkout這個(gè)用例;(2)按照資源劃分,例如可以劃分出一個(gè)catlog服務(wù)來(lái)存儲產(chǎn)品目錄。
服務(wù)劃分有兩個(gè)原則要遵循:(1)每個(gè)服務(wù)應該盡可能符合單一職責原則——Single Responsible Principle,即每個(gè)服務(wù)只做一件事,并把這件事做好;(2)參考Unix命令行工具的設計,Unix提供了大量的簡(jiǎn)單易用的工具,例如grep、cat和find。每個(gè)工具都小而美。
最后還要強調:系統分解的目標并不僅僅是搞出一堆很小的服務(wù),這不是目標;真正的目標是解決巨石型應用在業(yè)務(wù)急劇增長(cháng)時(shí)遇到的問(wèn)題。
對于上面的例子,按照功能和資源劃分后,就形成下面圖3的架構圖。分解后的微服務(wù)架構包含多個(gè)前端服務(wù)和后端服務(wù)。前端服務(wù)包括Catalog UI(用于商品搜索和瀏覽)、Checkout UI(用于實(shí)現購物車(chē)和下單操作);后端服務(wù)包括一些業(yè)務(wù)邏輯模塊,我們將在巨石應用中的每個(gè)服務(wù)模塊重構為一個(gè)單獨的服務(wù)。這么做有什么問(wèn)題呢?
每個(gè)服務(wù)足夠內聚,足夠小,代碼容易理解、開(kāi)發(fā)效率提高;
服務(wù)之間可以獨立部署,微服務(wù)架構讓持續部署成為可能;
每個(gè)服務(wù)可以各自進(jìn)行x擴展和z擴展,而且,每個(gè)服務(wù)可以根據自己的需要部署到合適的硬件服務(wù)器上;
容易擴大開(kāi)發(fā)團隊,可以針對每個(gè)服務(wù)(service)組件開(kāi)發(fā)團隊;
提高容錯性(fault isolation),一個(gè)服務(wù)的內存泄露并不會(huì )讓整個(gè)系統癱瘓;
系統不會(huì )被長(cháng)期限制在某個(gè)技術(shù)棧上。
《人月神話(huà)》中講到:沒(méi)有銀彈,意思是只靠一把錘子是蓋不起摩天大樓的,要根據業(yè)務(wù)場(chǎng)景選擇設計思路和實(shí)現工具。我們看下為了換回上面提到的好處,我們付出(trade)了什么?
開(kāi)發(fā)人員要處理分布式系統的復雜性;開(kāi)發(fā)人員要設計服務(wù)之間的通信機制,對于需要多個(gè)后端服務(wù)的user case,要在沒(méi)有分布式事務(wù)的情況下實(shí)現代碼非常困難;涉及多個(gè)服務(wù)直接的自動(dòng)化測試也具備相當的挑戰性;
服務(wù)管理的復雜性,在生產(chǎn)環(huán)境中要管理多個(gè)不同的服務(wù)的實(shí)例,這意味著(zhù)開(kāi)發(fā)團隊需要全局統籌(PS:現在docker的出現適合解決這個(gè)問(wèn)題);
應用微服務(wù)架構的時(shí)機如何把握?對于業(yè)務(wù)還沒(méi)有理清楚、業(yè)務(wù)數據和處理能力還沒(méi)有開(kāi)始爆發(fā)式增長(cháng)之前的創(chuàng )業(yè)公司,不需要考慮微服務(wù)架構模式,這時(shí)候最重要的是快速開(kāi)發(fā)、快速部署、快速試錯。
在巨石型架構下,客戶(hù)端應用程序(web或者app)通過(guò)向服務(wù)端發(fā)送HTTP請求;但是,在微服務(wù)架構下,原來(lái)的巨石型服務(wù)器被一組微服務(wù)替代,這種情況下客戶(hù)端如何發(fā)起請求呢?
如圖4中所示,客戶(hù)端可以向micro service發(fā)起RESTful HTTP請求,但是會(huì )有這種情況發(fā)生:客戶(hù)端為了完成一個(gè)業(yè)務(wù)邏輯,需要發(fā)起多個(gè)HTTP請求,從而造成系統的吞吐率下降,再加上無(wú)線(xiàn)網(wǎng)絡(luò )的延遲高,會(huì )嚴重影響客戶(hù)端的用戶(hù)體驗。
為了解決這個(gè)問(wèn)題,一般會(huì )在服務(wù)器集群前面再加一個(gè)角色:API gateway,由它負責與客戶(hù)度對接,并將客戶(hù)端的請求轉化成對內部服務(wù)的一系列調用。這樣做還有個(gè)好處是,服務(wù)升級不會(huì )影響到客戶(hù)端,只需要修改API gateway即可。加了API gateway之后的系統架構圖如圖5所示。

內部服務(wù)之間的通信方式有兩種:基于HTTP協(xié)議的同步機制(REST、RPC);基于消息隊列的異步消息處理機制(AMQP-based message broker)。
Dubbo是阿里巴巴開(kāi)源的分布式服務(wù)框架,屬于同步調用,當一個(gè)系統的服務(wù)太多時(shí),需要一個(gè)注冊中心來(lái)處理服務(wù)發(fā)現問(wèn)題,例如使用ZooKeeper這類(lèi)配置服務(wù)器進(jìn)行服務(wù)的地址管理:服務(wù)的發(fā)布者要向ZooKeeper發(fā)送請求,將自己的服務(wù)地址和函數名稱(chēng)等信息記錄在案;服務(wù)的調用者要知道服務(wù)的相關(guān)信息,具體的機器地址在ZooKeeper查詢(xún)得到。這種同步的調用機制足夠直觀(guān)簡(jiǎn)單,只是沒(méi)有“訂閱——推送”機制。
AMQP-based的代表系統是Kafka、RabbitMQ等。這類(lèi)分布式消息處理系統將訂閱者和消費者解耦合,消息的生產(chǎn)者不需要消費者一直在線(xiàn);消息的生產(chǎn)者只需要把消息發(fā)送給消息代理,因此也不需要服務(wù)發(fā)現機制。
兩種通信機制都有各自的優(yōu)點(diǎn)和缺點(diǎn),實(shí)際中的系統經(jīng)常包含兩種通信機制。例如,在分布式數據管理中,就需要同時(shí)用到同步HTTP機制和異步消息處理機制。
在線(xiàn)商店的客戶(hù)賬戶(hù)有限額,當客戶(hù)試圖下單時(shí),系統必須判斷總的訂單金額是否超過(guò)他的信用卡額度。信用卡額度由CustomerService管理、下訂單的操作由OrderService負責,因此Order Service要通過(guò)RPC調用向Customer Service請求數據;這種方法能夠保證每次Order Service都獲取到準確的額度,單缺點(diǎn)是多一次RPC調用、而且Customer Service必須保持在線(xiàn)。
還有一種處理方式是,在OrderService這邊存放一份信用卡額度的副本,這樣就不需要實(shí)時(shí)發(fā)起RPC請求,但是還需要一種機制保證——當Customer Service擁有的信用卡額度發(fā)生變化時(shí),要及時(shí)更新存放在Order Service這邊的副本。
當一份數據位于多個(gè)服務(wù)上時(shí),必須保證數據的一致性。
分布式事務(wù)(Distributed transactions) 使用分布式事務(wù)非常直觀(guān),即要更新Customer Service上的信用卡額度,就必須同時(shí)更新其他服務(wù)上的副本,這些操作要么全做要么全不做。使用分布式事務(wù)能夠保證數據的強一致,但是會(huì )降低系統的可用性——所有相關(guān)的服務(wù)必須始終在線(xiàn);而且,很多現代的技術(shù)棧并不支持事務(wù),例如REST、NoSQL數據庫等。
基于事件的異步更新(Event-driven asynchronous updates) Customer Service中的信用卡額度改變時(shí),它對外發(fā)布一個(gè)事件到“message broker(消息代理人)”;其他訂閱了這個(gè)事件的服務(wù)受到提示后就更新數據。事件流如圖6所示。
在實(shí)際工作中,很少有機會(huì )參與一個(gè)全新的項目,需要處理的差不多都是存在這樣那樣問(wèn)題的復雜、大型應用。這時(shí)候如何在維護老服務(wù)的同時(shí),將系統漸漸重構為微服務(wù)架構呢?
不要讓事情更壞,有新的需求過(guò)來(lái)時(shí),如果可以獨立開(kāi)發(fā)為一個(gè)服務(wù),就單獨開(kāi)發(fā),然后為老服務(wù)和新服務(wù)直接編寫(xiě)膠水代碼(Glue Code)——這個(gè)過(guò)程不容易,但這是分解巨型服務(wù)的第一步,如圖7所示;
識別巨石型應用中的可以分離出來(lái)當做單獨服務(wù)的模塊,一般適合分離的模塊具有如下特點(diǎn):兩個(gè)模塊對資源的需求是沖突的(一個(gè)是CPU密集型、一個(gè)是IO密集型);授權鑒定層也適合單獨分離出一個(gè)服務(wù)。每分離出一個(gè)服務(wù),就需要編寫(xiě)對應的膠水代碼來(lái)與剩下的服務(wù)通信,這樣,在逐漸演進(jìn)過(guò)程中,就完成了整個(gè)系統的架構更新。
關(guān)于重構,有篇文章推薦大家閱讀——推倒重來(lái)的講究,關(guān)于重構有很多可以寫(xiě)的,希望我能快速進(jìn)步,多寫(xiě)點(diǎn)總結與大家分享。
聯(lián)系客服