06.11 微服務:從設計到部署「筆記」

一、微服務簡介

A.單體地獄

1.成功的應用有一個趨勢,隨著時間推移而變得越來越臃腫

2.複雜的單體應用本身就是持續部署的障礙

3.單體應用使得采用新框架和語言變得非常困難

B.微服務 — 解決複雜問題

1.思路是將應用程序分解成一套較小的互連服務。一個服務通常實現了一組不同的特性或功能。每個微服務都是一個迷你應用,包括了業務邏輯以及多個適配器

2.一些微服務會暴露一個供其他微服務或應用客戶端消費的API,其他微服務可能實現了一個WebUI,在運行時,每個實例通常是一個雲虛擬機(virtual machine,VM)或者一個Docker容器

3.他們之間的通信是由一個被稱為API網關(API Gateway)的中介負責,API網關負責負載均衡、緩存、訪問控制、API計量和監控

4.如果您想從微服務中受益,每一個服務都應該有自己的數據庫模式,因為它能實現松耦合

C.微服務的優點

1.解決了複雜問題,把可能會變得龐大的單體應用程序分解成一套服務

2.這種架構使得每個服務都可以由一個團隊獨立專注開發

3.微服務架構模式可以實現每一個微服務獨立部署

4.微服務架構模式使得每個服務能夠獨立擴展

D.微服務的缺點

1.微服務這個術語的重點過多偏向於服務的規模,有些開發者主張構建極細粒度的10至100LOC(代碼行)服務,但小型服務只是一種手段,目標在於充分分解應用程序以方便應用敏捷開發和部署

2.微服務是一個分佈式系統,使得整體變得複雜,開發者需要選擇和實現基於消息或者RPC的進程間通信機制,模塊間通過語言級方法/過程調用相互調用,這比單體應用要複雜得多

3.分區數據庫架構,需要更新不同服務所用的數據庫,通常不會選擇分佈式事務,不僅僅是因為CAP定理

4.測試微服務應用程序也很複雜,需要啟動該服務及其所依賴的所有服務,或者至少為這些服務配置存根

5.實現了跨越多服務變更,在微服務中需要仔細規劃和協調出現的變更至每個服務

6.部署基於微服務的應用程序也是非常複雜的

7.每個服務都有多個運行時實例,還有更多的移動部件需要配置、部署、擴展和監控,還需要實現服務發現機制,使得服務能夠發現需要與之通信的任何其他服務的位置(主機和端口),需要開發人員能高度控制部署方式和高度自動化

二、使用API網關

A.客戶端與微服務直接通信

1.問題:客戶端的需求與每個微服務暴露的細粒度的API不匹配,公網下效率低下

2.問題:有可能使用了非web友好協議,一個服務可能使用了Thrift二進制rpc,而另一個可能使用AMQP消息協議,這些對瀏覽器還是防火牆都是不友好的,最好是在內部使用

3.缺點:難以重構微服務

B.使用API網關

1.API網關是一個服務器,是系統的單入口點,類似於面向對象設計模式中的門面(Facade)模式,封裝了內部系統架構,並針對每個客戶端提供一個定製API,還可用於認證、監控、負載均衡、緩存和靜態響應處理

2.API網關負責請求路由、組合和協議轉換,通常會調用多個微服務和聚合結果來處理一個請求,可以在Web協議(如HTTP和WebSocket)和用於內部的非Web友好協議之間進行轉換

3.API還可以為每個客戶端提供一個定製API,通常為客戶端暴露一個粗粒度的API

C.API網關的優點與缺點

1.主要好處是它封裝了應用程序的內部結構,客戶端只與網關通信,而不必調用特定的服務

2.缺點是它是另一個高度可用的組件,需要開發、部署和管理,API網關可能會成為開發瓶頸

3.重要的是更新API網關的過程應儘可能地放緩一些,否則,開發人員將被迫排除等待網關更新

D.實施API網關

1.在一個支持異步、非阻塞I/O平臺上構建API網關是很有必要的。Node.js、Nginx Plus

2.API網關通過簡單地把他們(請求)路由到適當的後端服務來處理一些請求。它通過調用多個後端服務並聚合結果來處理其他請求,API網關應該併發執行獨立請求

3.使用傳統的異步回調方式來編寫API組合代碼會很快使您陷入回調地獄,好的方式是使用響應式方法以聲明式編寫API網關代碼

4.一個基於微服務的應用程序是一個分佈式系統,必須使用一個進程間(inter-process)通信機制,有兩種方案:一是使用基於消息的異步機制,如JMS、AMQP、ZeroMQ等;另一種採用了同步機制,如HTTP和Thrift;API網關需要支持各種通信機制

5.API網關需要知道與其通論的每個微服務的位置(IP地址和端口),需要使得系統的服務發現機制:服務端發現或客戶端發現,API網關必須能夠查詢服務註冊中心,該註冊中心是所有微服務實例及其位置的數據庫

6.當一個服務調用另一個響應緩慢或不可用的服務時,API網關不應該無期限地等待下游服務,如何處理故障問題取決於決定的方案和哪些服務發生故障

7.如果可以,API網關還可以返回緩存數據,通過返回默認數據或緩存數據,確保系統發生故障時最小程度上影響到用戶體驗

8.Netflix Hystrix是一個非常有用的用於編寫調用遠程服務代碼的庫

三、進程間通信

A.簡介

1.服務必須通過進程間通信(IPC)機制進行交互

B.交互方式

1.第一類:

* 一對一,每個客戶端請求都由一個服務實例處理

* 一對多,每個請求由多個服務實例處理

2.第二類:

* 同步,客戶端要求服務及時響應,在等待過程中可能會發生阻塞

* 異步,客戶端在等待響應時不會發生阻塞,但響應(如果有)不一事實上立即返回

微服務:從設計到部署「筆記」

同步異步

3.一對一交互,包括同步(請求/響應)與異步(通知與請求/異步響應):

* 請求/響應,客戶端向服務發出請求並等待響應。客戶端要求響應及時到達。在基於純種的應用程序中,發出請求的線程可能在等待時發生阻塞

* 通知(又稱為單向請求),客戶端向服務發送請求,但不要求響應

* 請求/異步響應,客戶端向服務發送請求,服務異步響應。客戶端在等待時不發生阻止,適用於假設響應可能不會立即到達的場景

4.一對多交互,異步類型:

* 發佈/訂閱客戶端,發佈通知消息,由零個或多個感興趣的服務消費

* 發佈/異步響應,客戶端發佈請求消息,之後等待一定時間來接收消費者的響應

C.定義API

1.無論使用何種IPC機制,使用接口定義語言(interface definition language,IDL)來嚴格定義服務API都是非常有必要的

2.在對需要實現的服務API定義進行迭代之後,可以通過編寫接口定義並與客戶端開發人員進行審閱來開始開發服務

D.演化API

1.需要逐步部署服務的新版本,以便新舊版本的服務同時運行

2.如果使用了基於HTTP的機制(如REST),一種方法是將版本號嵌入到URL中

E.處理局部故障

1.處理局部故障的策略:

* 網絡超時,不要無限期地阻塞,始終使用超時方案

* 限制未完成的請求數量,對客戶端擁有特定服務的未完成請求的數量設置上限

* 斷路器模式,追蹤成功和失敗請求的數量。如果錯誤率超過配置閾值,則斷開斷路器

* 提供回退,請求失敗時執行回退邏輯

F.IPC技術

1.基於HTTP的REST或Thrift

2.異步、基於消息的通信機制,如AMQP或STOMP

3.基於文本的格式,如JSON或XML

4.使用如Avro或Protocol Buffers等二進制格式(更加高效)

G.異步、基於消息的通信

1.客戶端通過發送消息向服務發出請求,如果服務需要回復,則通過向客戶端發送一條單獨的消息來實現,由於通信是異步的,因此客戶端不會阻塞等待回覆,客戶端被假定不會立即收到回覆

2.兩種通道

* 點對點通道,發送一條消息給一個確切的、正在從通道讀取消息的消費者

* 發佈訂閱通道,將每條消息傳遞給所有已訂閱的消費者

3.大量的開源消息系統:RabbitMQ、Apache Kafka、Apache ActiveMQ、NSQ

4.使用消息傳遞的優點:

* 將客戶端與服務分離,服務實例對客戶端而言是透明的,客戶端不需要使用發現機制來確定服務實例的位置

* 消息緩衝,將消息寫入通道隊列,直到消費者處理它們

* 靈活的客戶端—服務交互

* 毫無隱瞞的進程間通信

5.缺點:

* 額外的複雜操作,消息傳遞系統是一個需要安裝、配置和操作的系統組件。消息代理必須高度可用

* 實施基於請求/響應式交互的複雜性

H.同步的請求/響應IPC

1.客戶端向服務器發送請求,該服務處理該請求並返回響應

2.兩種流行協議分別是REST和Thrift

3.REST

* 資源是REST的關鍵概念,通常表示業務對象或業務對象的集合

* 使用HTTP動詞(謂詞)來操縱資源,通過URL引用

* REST成熟度模型:

* 級別0的API客戶端通過向其唯一的URL端點發送HTTP POST請求來調用該服務,每個請求都被指定要執行的操作、操作的目標(如業務對象)以及參數

* 級別1的API支持資源概念

* 級別2的API使用HTTP動詞(謂詞)執行操作:GET檢索、POST創建、PUT更新

* 級別3的API基於非常規命名原則設計,基本思想是GET請求返回的資源的表述,包含用於執行該資源上允許的操作的鏈接

* 使用基於HTTP的協議的好處:

* 簡單易懂

* 可以使用瀏覽器來測試 HTTP API

* 直接支持請求/響應式通信

* 屬於防火牆友好

* 不需要中間代碼,簡化系統架構

* 缺點:

* 僅直接支持請求/響應的交互方式

* 因為直接通信,所以必須在交換期間都運行著

* 必須知道每個服務實例的位置(即URL)

4.Thrift

* 是一個用於編寫跨語言RPC客戶端和服務器skeleton,由一個或多個服務組成,定義類假於一個Java接口,是強類型方法的集合

* 支持多種消息格式:JSON、二進制和壓縮二進制

I.消息格式

1.兩種主要的消息格式

* 文本:JSON、XML,人類可讀,自描述

* 二進制:二進制Thrift,Protocol Buffers和Apache Avro

四、服務發現

A.為何使用服務發現

1.服務實例具有動態分配的網絡位置。此外,由於自動擴縮、故障與升級,整級服務實例會動態變更。因此,您的客戶端代碼需要使用更精確的服務發現機制

2.兩種主要的服務發現模式:客戶端發現(client-side discovery)與服務端發現(server-side discovery)

B.客戶端發現模式

1.客戶端負責確定可用服務實例的網絡位置和請求負載均衡。客戶端查詢服務註冊中心(service registry),它是可用服務實例的數據庫,之後,客戶端利用負載均衡算法選擇一個可用的服務實例併發出請求

2.服務實例的網絡位置在服務註冊中心啟動時被註冊,當實例終止時,它將從服務註冊中心移除,通常使用心跳機制週期性地刷新服務實例的註冊信息

微服務:從設計到部署「筆記」

客戶端發現

3.優點:相對簡單,可以實現智能的,特定於應用程序的負載均衡決策

4.缺點:將客戶端與服務註冊中心耦合在一起,必須為服務客戶端使用的每種編程語言和框架實現客戶端服務發現邏輯

5.Netflix OSS提供了一個很好的客戶端發現模式示例,Netflix Eureka是一個服務註冊中心,Netflix Ribbon是一個IPC客戶端,用於在可用服務實例之間請求負載均衡

C.服務端發現模式

1.客戶端通過負載均衡器向服務發出請求,負載均衡器查詢服務註冊中心並將每個請求路由到可用的服務實例,服務實例由服務註冊中心註冊與銷燬

微服務:從設計到部署「筆記」

服務端發現

2.AWS Elastic Load Balancer(ELB)是一個服務端發現路由示例

3.HTTP服務器和負載均衡器(如Nginx Plus和Nginx)也可以作為服務端發現負載均衡器

4.優點:把發現的細節從客戶端抽象出來,消除了為服務客戶端使用的每種編程語言和框架都實現發現邏輯的必要性

5.缺點:除非負載均衡器由部署環境提供,否則您需要引入這個高可用系統組件,並進行設置和管理

D.服務註冊中心

1.服務註冊中心(service registry)是服務發現的一個關鍵部分,是一個包含了服務實例網絡位置的數據庫。服務註冊中心由使用了複製協議(replication protocol)來維護一致性的服務器集群組成

2.Netflix Eureka、etcd、Consul、Apache ZooKeeper

E.服務註冊方式

1.服務實例自我註冊,即自注冊模式

2.使用其他系統組件來管理服務實例的註冊,即第三方註冊模式

F.自注冊模式

1.服務實例負責在服務註冊中心註冊和註銷自己。如果有必要,服務實例通過發送心跳請求來防止其註冊信息過期

2.好處是相對簡單,不需要任何其他系統組件,缺點是它將服務實例與服務註冊中心耦合

G.第三方註冊模式

1.服務實例不負責再向服務註冊中心註冊自己,該工作將由被稱為服務註冊器(service register)的另一系統組件負責

2.服務註冊器通過輪詢部署環境或訂閱事件來跟蹤運行實例集的變更情況

3.好處是服務與服務註冊中心之間解耦,缺點是除非部署環境內置,否則同樣需要引入這樣一個高可用系統組件,並進行設置和管理

五、事件驅動數據管理

A.微服務和分佈式數據管理問題

1.使用關係型數據庫的一個主要優點是應用程序可以使用ACID事務,另一大好處是它提供了SQL語言

2.每個微服務所擁有的數據對當前微服務來說是私有的,只能通過其提供的API進行訪問

3.一個分區的數據存儲混合持久化架構具有許多優點,包括了松耦合的服務以及更好的性能與可擴展性

B.事件驅動架構

1.微服務在發生某些重要事件時發佈一個事件,其他微服務訂閱了這些事件,當微服務接收到一個事件時,它可以更新自己的業務實體

2.可以使用事件實現跨多服務的業務事務

3.優點:能夠實現跨越多服務並提供最終一致性事務,使得應用程序能夠維護物化視圖

4.缺點:其編程模型比使用ACID事務更復雜,訂閱者必須要檢測和忽略重複的事件

C.實現原子性

1.標準方法是使用涉及到數據庫和Message Broker的分佈式事務

D.使用本地事務發佈事件

1.應用程序使用僅涉及本地事務的多步驟過程來發布事件,訣竅在於存儲業務實體狀態的數據庫中有一個用作消息隊列的EVENT表

微服務:從設計到部署「筆記」

本地事務發現

2.好處是保證了被髮布的事件每次更新都不依賴於2PC,事件可以消除推斷的需要

3.缺點是很容易出錯

E.挖掘數據庫事務日誌

1.使用線程或進程發佈事件,該進程或線程對數據庫的事務或者提交日誌進行挖掘,當更新數據庫時,更改信息被記錄到數據庫的事務日誌中,Transaction Log Miner線程或進程讀取事務日誌並向Message Broker發佈事件

微服務:從設計到部署「筆記」

挖掘數據庫事務日誌

2.好處是它能保證被髮布的事件每次更新都不依賴於2PC,可以通過將事件發佈與應用程序的業務邏輯分離來簡化應用程序

3.缺點是事務日誌的格式對於每個數據庫來說都是專有的,記錄於事務日誌中的低級別更新可能難以對高級業務事件進行逆向工程

F.使用事件溯源

1.事件溯源通過使用完全不同的、不間斷的方式來持久化業務實體,實現無2PC原子性。應用程序不存儲實體的當前狀態,而是存儲一系列狀態改變事件。通過無回放事件來重建實體當前狀態。由於保存事件是一個單一操作,因此具有原子性

2.事件被持久化在事件存儲中,事件存儲是一個事件數據庫,該存儲有一個用於添加和檢索實體事件的API

3.好處:可以在狀態發生變化時可靠地發佈事件,解決了數據一致性;持久化的是事件而不是領域對象,避免了對象關係阻抗失配問題;提供對業務實體所做更改的100%可靠的審計日誌;業務邏輯包括松耦合的交換事件業務實體,從單體應用程序遷移到微服務架構更加容易

4.缺點:是一種不同而陌生的編程風格,存在學習曲線;事件存儲僅支持通過主鍵查找業務實體;必須使用命令查詢責任分離(CQRS)來實現查詢,應用程序必須處理最終一致的數據

六、選擇部署策略

A.動機

1.微服務應用程序由數十甚至上百個服務組成,服務以不同的語言和框架編寫,每個都是一個迷你的應用程序,需要根據該服務的需求運行每個服務的一定數量的實例,必須為每個服務實例提供相應的CPU、內存和I/O資源

B.單主機多服務實例模式

1.單主機多服務實例(Multiple Service Instances per Host)模式,可以提供一個或多個物理主機或虛擬主機,並在每個上運行多個服務實例,每個服務實例在一個或多個主機的標準端口上運行

2.一種變體是每個服務實例都是一個進程或進程組

3.另一個變體是在同一進程或進程組中運行多個服務實例

4.優點:資源使用率相對較高,多個服務實例共享服務器及其操作系統。如果進程或進程組運行了多個服務實例,則效率更高;部署服務實例相對較快;由於缺乏開銷,通常啟動一個服務是非常快的

5.缺點:服務實例很少或者沒有隔離,除非每個服務實例是一個單獨的進程,一個行為不當的服務實例可能會佔用掉主機的所有內存或CPU;部署服務的運維團隊必須瞭解執行此操作的具體細節

C.每個主機一個服務實例模式

1.每個主機一個服務實例(Service Instance per Host)模式,在主機上單獨運行每個服務實例,每個虛擬機一個服務實例模式和每個容器一個服務實例模式

2.每個虛擬機一個服務實例模式,將每個服務打包為一個虛擬機(VM)鏡像,每個服務實例都是一個使用該VM鏡像啟動的VM

微服務:從設計到部署「筆記」

虛擬機

3.每個虛擬機一個服務實例模式的優點:每個服務實例運行是完全隔離的;可以利用成熟的雲基礎架構;封裝了服務的實現技術;

4.每個虛擬機一個服務實例模式的缺點:資源利用率較低;公共IaaS中的VM通常是收費的,無論他們是處於繁忙還是空閒;部署新版本的服務時通常很慢;要對很多未劃分的重擔負責;

5.每個容器一個服務實例(Service Instance per Container)模式,每個服務實例都在其自己的容器中運行(Docker)

微服務:從設計到部署「筆記」

容器服務實例

6.容器模式的優點:將服務實例彼此隔離,輕鬆監控每個容器所消耗的資源,封裝了服務實現技術,容器管理API作為管理服務的API;容器是輕量級技術,可以快速構建,也可以很快地啟動;

7.容器模式的缺點:不成熟,不像VM那樣安全,因為共享了主機的OS內核;需要負責劃分容器鏡像管理重擔;通常部署在一個按單個VM收費的基礎設施上,可能會產生超額配置VM的額外成本,以處理負載峰值;

D.Serverless部署

1.AWS Lambda

七、重構單體為微服務

A.微服務重構概述

1.一個不要使用的策略是“大爆炸”重寫,就是您將所有的開發工作都集中在從頭開始構建新的基於微服務的應用程序

2.應該逐步重構單體應用程序,逐漸添加新功能,並以微服務的形式創建現有功能的擴展——以互補的形式修改單體應用,並且一同運行微服務和修改後的單體

B.策略一:停止挖掘

1.洞穴定律:當你身處在一個洞穴中,你應該停止挖掘

2.當你的單體應用變得難以管理時,應該停止擴張,避免使單體變得更大

3.三種策略來訪問單體數據:

* 調用由單體提供的遠程API

* 直接訪問單體數據庫

* 維護自己的數據副本,與單體數據庫同步

4.粘合代碼,將服務與單體集成,位於單體、服務或兩者中的粘合代碼負責數據集成,有時被稱為防護層(anti-corruption layer),因為粘合代碼阻止了服務被遺留的單體領域模型的概念汙染,這些服務具有自己的原始領域模式

C.策略二:前後端分離

1.縮小單體應用的一個策略是從業務邏輯層和數據訪問層拆分出表現層:

* 表現層(Presentation Layer,PL)處理HTTP請求並實現(REST) API或基於HTML的WebUI組件,通常存在大量代碼

* 業務邏輯層(Business Logic Layer,BLL)作為應用程序核心,實現業務規則的組件

* 數據訪問層(Data Access Layer,DAL)訪問基礎架構組件的組件,如數據庫和消息代理

2.業務層具有由一個或多個門面組成的粗粒度API,其封裝了業務邏輯組件。一個應用程序包含表現層,另一個應用程序包含業務和數據訪問邏輯

3.優點:使您能夠獨立於彼此開發、部署和擴展這兩個應用,允許表現層開發人員在用戶界面上快速迭代,可以輕鬆執行A/B測試;暴露了可以被您開發的微服務調用的遠程API

4.只是一個局部解決方案,兩個應該程序中的一個或兩個很可能是一個無法管理的單體,需要使用第三種策略來消除剩餘的整體或單體

D.策略三:提取服務

1.將龐大的現有模塊轉變為獨立的微服務,每次提取一個模塊並將其轉換成服務時,單體就會縮小

2.從容易提取的幾個模塊開始,將得到微服務的相關經驗,之後提取能給你最大利益的模塊;提取頻繁更改的模塊通常是有益的;提取與單體的其他模塊有顯著不同的模塊也是有益的;

3.提取模塊第一步是在模塊和單體之間定義一個粗粒度接口;一旦實現了粗粒度接口,就可以將模塊變成獨立的服務,必須編寫代碼以使單體和服務通過使用進程間通信(IPC)機制的API進行通信;第二個重構步驟是將模塊轉換為一個獨立服務;


分享到:


相關文章: