徐琛杰 周 翔 彭 鑫 趙文耘
(復旦大學軟件學院 上海 201203)(上海市數據科學重點實驗室(復旦大學) 上海 201203)
近年來,越來越多的公司如谷歌、亞馬遜[1]等開始采用微服務架構。文獻[2]將微服務定義為可獨立開發、部署、測試和伸縮的應用程序。基于微服務架構的應用系統(下文簡稱“微服務系統”)相較于單體應用有易于開發、部署、維護和伸縮等優點[3]。
微服務系統在線運行時,不同微服務擁有的實例數量不一,而微服務占有的資源量與實例數量呈正相關關系。微服務實例越多,該微服務占有的資源就越多,反之亦然[4]。進而當請求數量較多時,若某個微服務實例數量過少,其占有的資源也會較少。這將導致該微服務出現平均響應時間急劇變長的現象,嚴重影響用戶的使用。同時,微服務系統線上運行時,其負載一直動態變化,導致特定的部署方案無法有效降低系統的整體響應時間。特別是在集群資源固定且有限的場景下,需要根據負載的變化動態地調節各個微服務的實例數量,從而降低微服務系統的整體響應時間。
Kubernetes[5]、Marathon[6]等當下流行的微服務管理工具提供了微服務的自動伸縮[7]功能。這些工具根據預先設定的條件為所有資源過少的微服務添加實例、為所有資源過多的微服務減少實例,從而調節微服務占有的資源量。但這些工具均假定在云計算環境下運行,當集群資源不足時,可以動態地向集群中添加新服務器。在集群資源固定且有限的場景下,當集群資源不足時,因無法添加新服務器,這些工具將無法如期調節微服務實例數量。此外,這些工具需要人工為每個微服務配置伸縮方案,配置工作相對繁重。
針對以上問題,本文提出了一個基于MAPE(Monitor、Analyze、Plan、Execute)環路[8]的自適應部署優化方法。在集群資源固定且有限的場景下,每輪MAPE循環時,通過分析微服務調用鏈及各個微服務實例的資源使用情況,根據調用鏈上缺少資源的微服務數量,挑選出數量最多的一條調用鏈。當集群資源充足時,為挑選出的調用鏈上缺少資源的微服務各添加一個實例。當集群資源不足時,先為所有存在空閑實例的微服務各減少一個實例,再根據減少實例后集群資源空閑情況,為挑選出的調用鏈上部分或全部缺少資源的微服務各添加一個實例。最終達到降低微服務系統整體響應時間的效果。在開源系統Spring Cloud Sleuth和Zipkin的基礎上,本文實現了一個面向微服務系統的運行時可視化監控工具,基于該工具實現了一個面向微服務系統的運行時部署優化工具,并通過實驗驗證了部署優化方法的有效性。Train_ticket[9]是一個開源的、基于微服務架構的火車票訂票系統,擁有超過40個微服務,本文基于該系統舉例說明相關方法和技術,同時進行實驗驗證。
谷歌開發的Dapper[10]是一個面向分布式系統的追蹤工具,通過向通信模塊、流程控制模塊和線程庫等被絕大多數應用所使用的模塊中插入探針,使Dapper能在收集追蹤信息時保持對應用層透明。Spring Cloud Sleuth是Spring Cloud提供的一個分布式追蹤解決方案。Zipkin是一個基于Dapper開發的分布式追蹤系統,可以用來收集Spring Cloud Sleuth指導分布式系統產生的運行時數據并可視化。
文獻[11]實現了一組監控和管理微服務系統的靜態頁面,對單個微服務的平均響應時間、吞吐量等進行了可視化。本文不僅實現了單個微服務的平均響應時間、吞吐量等的可視化,還實現了微服務調用鏈及線上部署情況的可視化,并結合收集到的數據進行了可視化展示。
Kubernetes可以將單個微服務部署為一個Pod,通過自動伸縮Pod的方式,實現微服務的自動伸縮。但Kubernetes的自動伸縮功能要求微服務系統運行在云計算環境下,從而可以動態地向集群中添加新服務器。Kubernetes會為所有資源利用率超過預先設定值的微服務啟動新實例,但如果運行的實例總數量過大,集群中的資源將不能啟動全部新實例,Kubernetes就會添加新服務器到集群中,但是這種做法不適合集群資源固定且有限的場景。此外,Kubernetes還需人工為每個微服務單獨配置伸縮方案,如微服務實例數量的上下限、期望的CPU平均利用率等限制條件,配置過程相對繁瑣。文獻[12]將微服務調用鏈定義為請求到達微服務系統后,微服務之間的相互調用所形成的有向無環圖。本文提出的基于MAPE環路的自適應部署優化方法是在集群資源固定且有限的場景下,根據調用鏈來調節各個微服務的實例數量從而縮短微服務系統整體的平均響應時間,不需要人工為每個微服務配置伸縮方案,降低了配置的復雜度。
文獻[13]提出了一個基于MAPE環路的自動伸縮方法,但該方法僅關注了CPU資源利用率,且每輪調節只調節一個節點,這導致在請求數量快速變化的場景下其調節速度較慢。本文提出的部署優化方法除關注CPU資源利用率外,還關注內存的使用情況,且每輪調節會根據調用鏈選擇出一組微服務進行調節。當請求數量快速變化時,本文提出的部署優化方法能快速調節微服務實例數量。
微服務系統屬于分布式系統,因此可將基于Dapper開發的面向分布式系統的開源工具Spring Cloud Sleuth和Zipkin,用于研究微服務系統的行為。其中Spring Cloud Sleuth負責指導微服務產生trace信息,Zipkin負責收集trace信息并展示。
用戶對微服務系統的一次請求通常需要大量的微服務調用進行處理。Dapper將針對一個特定請求的全部后臺調用定義為一個trace,將微服務實例間的一次遠程過程調用定義為一個span。Dapper為每個trace分配一個唯一的ID,稱為trace id,從而區分開了來自前臺的不同請求;Dapper為每個span分配一個唯一的ID,稱為span id,從而區分開了不同微服務實例之間的相互調用。除最外層span,每個span均有一個span作為自己的父親,并將父親span的span id作為parent id記錄在span中。圖1展示了一個簡單的微服務系統請求處理流程(圖中,六邊形節點代表了微服務系統中的不同微服務實例) ,rpc1對應的span1,是rpc3對應的span3的父親,也是rpc4對應的span4的父親;而微服務實例C沒有再調用其他微服務實例,因此rpc2對應的span2沒有孩子。通過span之間的父子關系,同一trace下的眾多span構成了一顆樹,記錄了微服務系統不同實例之間的調用關系。

圖1 微服務系統請求處理路徑圖
Span除記錄trace id、span id和parent id外,還記錄了span的name(通常為被調用的接口)、遠程過程調用的時間節點、被調用方的接口、IP等眾多信息,為研究微服務系統的行為奠定了基礎。
為實現本文提出的基于MAPE環路的自適應部署優化方法,本文在開源系統Spring Cloud Sleuth和Zipkin的基礎上,實現了一個面向微服務系統的運行時可視化監控工具。該工具從微服務可用性、微服務調用鏈及部署情況三個維度監控微服務系統的運行狀態,并將結果可視化。
通過分析trace信息可以得到微服務可用性信息,微服務可用性信息包括每秒鐘請求數量QPS(Query Per Second)、平均響應時間、最短響應時間、最長響應時間和錯誤率(請求錯誤數量與請求總數量的比值)等信息。微服務系統的大量微服務在短時間內可產生龐大的trace數據。例如,將Train_ticket部署在1臺服務器上,每個微服務擁有1個實例,以每秒50個請求的速度發送查票請求,持續一小時可以收集32.6 GB的trace數據,數據量巨大。當用戶查詢微服務可用性信息時,如果直接從數據庫中取得數據進行統計,由于數據量巨大,往往需要很長的時間分析數據。為了提升系統性能,本文采用MapReduce編程模型[14]對trace數據進行預處理。
MapReduce是一種用于處理大規模數據集的編程模型,該模型首先對數據進行映射(Map),然后對映射后的數據進行歸約(Reduce),最終獲得需要的結果。本文通過定時任務,每分鐘統計一次微服務可用性信息。統計微服務可用性信息時先將span映射為統計微服務可用性的avaCount,再將具有相同微服務名稱、微服務實例IP地址和接口名稱的avaCount歸約,最終將歸約后得到的avaCount轉換為微服務可用性信息(即中間結果)。avaCount中包含如下信息:微服務名稱、微服務實例IP地址、接口名稱、函數名稱、查詢數量、總響應時間、錯誤數量、最短響應時間和最長響應時間。將預處理獲得的中間結果存儲在數據庫中。當用戶查詢微服務可用性信息時,只需分析數據庫中的中間結果即可獲得微服務可用性信息。
映射過程如圖2所示。將span與avaCount中邊框類型相同的項目進行映射。將span的名稱映射為avaCount中的接口名稱。將span的持續時間分別映射為avaCount的最短響應時間、最長響應時間、總響應時間。Span的類型有CLIENT、SERVER、PRODUCER、CONSUMER四種。其中CLIENT對應同步或異步調用中的調用方,SERVER對應同步或者異步調用中的被調用方;PRODUCER對應消息隊列中的生產方,CONSUMER對應消息隊列中的消費方。當span的類型為SERVER或者CONSUMER時,span的localEndpoint中記錄了被調用微服務的信息,serviceName為微服務名稱,Ipv4為微服務實例IP地址,將以上兩項分別映射為avaCount中的微服務名稱和微服務實例IP地址。Span中的tags記錄了一些額外信息。如圖2中mvc.controller.method為微服務被調用的函數名稱,將其映射為avaCount中的函數名稱。當被調用的微服務響應出現錯誤時,tags中將包含error,此時avaCount中的錯誤數量應置為1。因1個span對應1個請求,avaCount中的查詢數量應置為1。

圖2 span映射為avaCount示意圖
歸約過程如圖3所示。avaCount1和avaCount2的微服務名稱均為ts-route-service、微服務實例IP地址均為10.0.0.12、函數名稱均為querybyid,因此可以將avaCount1和avaCount2歸約為reduced avaCount。接口名稱中可能包含參數,因此取httpPath1和httpPath2的相同部分作為歸約后的httpPath。查詢數量、總響應時間和錯誤數量均采用相加的方式歸約。最小響應時間取minResponseTime1和minResponseTime2中較小者。最大響應時間取maxResponseTime1和maxResposneTime2中較大者。

圖3 avaCount歸約示意圖
圖4給出了一個基于Train_ticket收集的微服務可用性信息表格,表格共八列,從左到右依次為:微服務名稱、微服務實例ID(通常為IP地址)、調用接口名稱、查詢時間內的QPS、查詢時間內的平均響應時間、查詢時間內最短響應時間、查詢時間內最長響應時間、查詢時間內的錯誤率。表格中第1~6行分別為微服務實例10.0.0.39的6個不同接口的可用性信息,該實例屬于微服務ts-order-service。

圖4 微服務可用性
圖5給出了一個Train_ticket中查票微服務的QPS隨時間動態變化圖。橫軸為時間,縱軸為對應時間一分鐘內的平均QPS。圖6給出了一個Train_ticket中查票微服務的平均響應時間隨時間變化圖。橫軸為時間,縱軸為對應時間一分鐘內的平均響應時間。

圖5 QPS隨時間動態變化圖

圖6 平均響應時間隨時間動態變化圖
本文基于Zipkin實現了微服務調用鏈的可視化。微服務調用鏈可視化展示了微服務間的調用關系,線條粗細表示了微服務間調用次數的多少,線條上標注的數字表示了各微服務調用其余微服務的可能性。圖7給出了一個基于Train_ticket收集的微服務調用鏈的可視化界面,每個節點代表一個微服務,節點上的字母代表微服務的名稱。例如:節點ts-travel-service代表微服務ts-travel-service,節點ts-ticketinfo-service代表微服務ts-ticketinfo-service。節點間的有向線段表示微服務間的遠程調用關系,位于有向線段起點的微服務調用位于有向線段終點的微服務。有向線段的粗細表示調用次數的多少,越粗調用次數越多。線段上標注了“數字1:數字2”,其中“數字1”代表查詢時間內遠程調用的次數,“數字2”代表有向線段起點的微服務調用線段終點的微服務的概率。如ts-travel-service調用ts-ticketinfo-service共12次,ts-travel-service有0.400 0的概率調用ts-ticketinfo-service,且通過線段的粗細可以看出:ts-travel-service調用ts-ticketinfo-service和ts-route-service的次數較多、調用ts-seat-service和ts-train-service的次數較少。

圖7 微服務調用鏈可視化
本文實現的可視化監控工具通過調用外部系統接口獲取微服務系統線上部署情況。Kubernetes和Docker Swarm等微服務管理工具通常提供了獲取微服務實例部署情況的接口。為使開發和運維人員能更直觀地了解微服務系統的線上部署情況,可視化監控工具通過3D圖形來表示微服務系統的在線部署情況。圖8給出了一個基于Train_ticket收集的部署情況的可視化界面,圖中每個小球代表一個微服務實例,顏色相同的小球屬于同一個微服務。單擊小球可看到對應微服務的名稱,如圖中的ts-food-map-service。可以明顯看到小球聚集成兩堆,聚集在一起的小球表示對應微服務實例部署在同一臺服務器上,橫坐標server2和server1則代表著對應服務器的名稱。

圖8 微服務系統線上部署可視化
本文提出的基于MAPE環路的自適應部署優化方法,由監控模塊、分析模塊、規劃模塊和執行模塊四部分組成。監控模塊負責監控微服務系統中各個微服務的運行情況及服務器的資源使用情況;分析模塊根據監控模塊收集到的信息,分析各個微服務實例是否過載或空閑;規劃模塊根據微服務調用鏈、微服務實例資源使用情況、集群資源使用情況等信息,從分析模塊找出的過載和空閑的微服務實例中挑選出需要進行動態伸縮的一組微服務,并生成最終的自動伸縮方案;執行模塊根據規劃模塊生成的自動伸縮方案,通過調用外部系統接口實現動態伸縮微服務。
基于所實現的面向微服務系統的運行時可視化監控工具,本文實現了一個運行時部署優化工具。本文將微服務部署在Docker容器中,并由Docker Swarm管理微服務集群。Docker Swarm提供了spread、binpack、random三種容器放置策略[15]。Spread策略將容器盡可能平均地置于集群中的服務器上;binpack策略將容器盡可能地置于已放置了容器的服務器上,以便將空余服務器用于放置未來較大的容器;random策略隨機選擇服務器放置容器上。本文采用spread策略,使容器盡可能平均地分布在服務器上。
監控模塊主要監控以下三項信息:trace信息、服務器上CPU的平均利用率和內存的平均使用情況、各個微服務實例使用服務器CPU百分比。
本文實現的部署優化工具中,監控模塊通過可視化監控工具收集對應時間內的trace信息;通過定時采樣的方式,收集對應時間內服務器上CPU平均利用率和內存平均使用情況;通過Docker Swarm提供的Docker Stats指令[16],以定時采樣的方式收集對應時間內各個微服務實例使用服務器CPU百分比。
分析模塊通過分析監控模塊收集到的各個微服務實例使用服務器CPU百分比,判斷各個微服務實例的運行狀態,進而判斷各個微服務是否獲得了足夠的資源。
將各個微服務實例使用服務器CPU百分比超過上限的次數稱為upper、低于下限的次數稱為lower。若某個微服務實例的upper高于閾值,則認為該微服務實例過載;若某個微服務實例的lower高于閾值,則認為該微服務實例空閑。若某個微服務的全部實例過載,則認為該微服務處于資源過少的狀態;若某個微服務的部分或者全部實例空閑,則認為該微服務處于資源過多的狀態。
在判斷微服務運行狀態時,本文只通過微服務實例使用服務器CPU百分比來判斷,而沒有考慮請求數量。因為請求數量會直接表現在CPU利用率上,通過觀察CPU利用率便可知請求數量的多少。如果CPU利用率過高,說明該微服務實例正在處理數量較多的請求;如果CPU利用率過低,則說明該微服務實例正在處理數量較少的請求。
本文根據調用鏈和微服務實例資源使用情況,每次挑選一條調用鏈上需要添加實例的微服務及全部微服務中資源利用率過低需要減少實例的微服務進行動態伸縮。在挑選需要減少實例的微服務時,本文采取的方法類似于Kubernetes,將該輪MAPE循環中資源利用率過低的微服務全部減少一個實例,而不是從一條調用鏈上挑選。因為通過實驗發現,在集群資源固定且有限的場景下,能更快地減少實例就意味著能更快地為需要資源的微服務空出啟動新實例所需的資源。
通過分析監控模塊收集到的trace信息,可以獲得對應的調用鏈信息。用戶對微服務系統的一次請求,通常需要大量的微服務調用來進行處理。例如,網站amazon.com構建每個頁面平均需要調用100~150個微服務[17]。被請求直接調用的最外層接口,我們稱之為調用鏈的名稱。調用鏈中包括了調用鏈的名稱及所有被調用微服務的名稱組成的列表。
獲得了調用鏈信息后,就可以根據調用鏈信息和分析模塊獲得的微服務實例資源使用率超過上限的次數,判斷該為哪條調用鏈上缺少資源的微服務添加實例;根據調用鏈信息和微服務實例資源使用率低于下限的次數,判斷該為哪些微服務減少實例。算法1給出了基于調用鏈的微服務選取算法。
算法1基于調用鏈的微服務選取算法
01:輸入:監控模塊采樣次數n,微服務實例超過資源使用率上限的次數microserviceId2Upper,微服務實例資源使用率低于下限的次數microserviceId2Lower,微服務實例id列表microserviceIdList
02:輸出:需要添加實例的微服務列表increaseList,需要減少實例的微服務列表decreaseList
03:threshold ← n/2,微服務實例資源使用率超過上限次數大于threshold即為過載,微服務的全部實例均過載則該微服務過載
04:overloadList初始化,過載微服務組成的列表
05:increaseList初始化
06:decreaseList初始化
07:for id in microserviceId2Upper.keySet()
08: if microserviceId2Upper.get(id) > threshold then
09: name ← toServiceName(id)
10: find ← false
11: for id2 in microserviceId2Upper.keySet()
12: if !id2.equals(id) &&
name.equals(toServiceName(id2)) &&
microserviceId2Upper.get(id2) < threshold then
13: find ← true
14: end if
15: end for
16: if !find then
17: overloadList.add(toServiceName(id))
18: end if
19: end if
20:end for
21:callChainOverload ← 挑選出包含缺少資源的微服務數量最多的一條調用鏈
22: for microservice in callChainOverload
23: if overloadList.contains(microservice)
24: increaseList.add(microservice)
25: end if
26:end for
27:for id in microserviceId2Lower.keySet()
28: if microserviceId2Lower.get(id) > threshold then
29: name ← toServiceName(id)
30: find ← false
31: for id2 in microserviceId2Lower.keySet()
32: if !id2.equals(id) &&
name.equals(toServiceName(id2)) then
33: find ← true
34: end if
35: end for
36: if find then
37: decreaseList.add(name)
38: end if
39:end if
40:end for
算法1第3行,計算threshold。第4~6行負責初始化。第7~20行,針對每個微服務的實例判斷所屬微服務是否過載。第8行,判斷該實例是否過載。第9行,獲取該實例對應的微服務名稱。第10~18行,查找是否存在屬于同一微服務的未過載的不同實例。若存在,說明這個實例過載是負載不均衡導致的,不應該通過加實例的方式解決該實例過載的問題;若不存在,說明該微服務全部實例過載,則此微服務應該增加實例,將其添加到過載微服務列表overloadList中。第21行,針對調用鏈緩存中的每條調用鏈,分別計算該調用鏈上需要添加實例的微服務數量,選擇數量最多的一條調用鏈。第22~26行,將該調用鏈上需要添加實例的微服務添加到increaseList中。第27~40行,針對每個微服務實例判斷是否應該將對應的微服務加入decreaseList中。第28行,判斷該微服務實例是否空閑。第29行,根據微服務實例id獲得微服務名稱。30~35行,判斷是否存在屬于同一微服務的不同實例,若存在find為true,不存在find為false。第36~38行,將實例數量大于1的微服務加入decreaseList中。
獲得了需要增加實例的微服務列表和需要減少實例的微服務列表后,根據集群資源使用情況,綜合判斷生成最終的部署調節方案。算法2給出了部署調節方案生成算法。
算法2部署調節方案生成算法
01:輸入:集群資源利用情況serverResources,服務器CPU利用上限cpu_threshold,服務器內存利用上限memory_threshold、需要添加實例的微服務列表increaseList、需要減少實例的微服務列表decreaseList
02:輸出:最終的部署調節方案changes
03:enough ← false
04:for serverResource in serverResources
05: if serverResource.CPU < cpu_threshold &&
serverResource.memeory < memory_threshold then
06: enough ← true
07:end if
08:end for
09:if enough then
10: if increaseList !=null then
11: changes.add(increaseList)
12: end if
13:else
14: if decreaseList !=null then
15: changes.add(decreaseList)
16: if increaseList !=null then
17: if increaseList.size() > decreaseList.size() then
18: list ← 從 increaseList中隨機選擇m個微服務,其中m=decreaseList.size()
19: changes.add(list)
20: end if
21: else
22: changes.add(increaseList)
23: end if
24:end if
25:end if
算法2第3行負責初始化。第4行,針對每臺服務器的資源使用情況進行判斷。第5行,如服務器CPU和內存的使用率分別低于cpu_threshold和memory_threshold,則認為該服務器資源充足。只要存在一臺服務器資源充足,即認為整個集群資源充足。第9~12行,若集群資源充足,則僅僅為需要添加實例的微服務各添加一個實例。原因在于,在集群資源固定且有空余時,不需要通過減少空閑微服務實例的方式來降低資源消耗。此外,這種做法還可以應對請求突增的情況。第13~25行,對應集群資源不足的情況。第14~24行,對應decreaseList不為空的情況,即存在微服務可以減少實例。第15行,將這些微服務加入changes中,為這些微服務各減少一個實例。第16行,若increaseList不為空,即存在微服務可以增加實例。第17~20行,若increaseList中微服務個數大于decreaseList中微服務個數,則從increaseList中隨機選擇m個微服務加入changes中,為這些微服務各添加一個實例,其中m為decreaseList中微服務的個數。因此時集群資源不足,當減少m個實例后,粗略認為可以增加m個實例到集群中。第21~23行,若increaseList中微服務個數小于等于decreaseList中微服務個數,則將increaseList中微服務全部加入changes中,為這些微服務各添加一個實例。但如果集群資源不足,且沒有微服務可以減少實例時,不添加也不減少任何實例。
執行模塊根據規劃模塊生成的自動伸縮方案,通過調用外部系統提供的接口來增加或減少某些微服務的實例數量,從而實現動態地伸縮微服務。Kubernetes、Docker Swarm等當下流行的微服務管理工具均提供了動態調節微服務實例數量的功能。本文實現的部署優化工具中,執行模塊通過遠程調用Docker Swarm提供的伸縮微服務的Docker update指令[18]來添加或刪除微服務實例。
為了驗證本文所提出的基于MAPE環路的自適應部署優化工具的有效性,本文進行了仿真實驗。本文使用Train_ticket作為微服務系統,將其和本文開發的部署優化工具共同部署,從而進行仿真實驗。
服務器為四臺虛擬機(操作系統Centos 7、8 * Intel Xeon E5649@2.53 GHz CPU、24 GB內存、50 GB硬盤、10 M/s帶寬)。微服務系統和部署優化工具部署在兩臺服務器上,另外兩臺服務器部署壓力測試工具Jmeter。微服務系統部署在Docker Swarm集群中,每個微服務部署兩個實例,每個實例部署在一個Docker容器中,且一個Docker容器中只部署一個實例。
使用Jmeter模擬400個用戶的并發訪問,請求分別為登錄和查票。如表1所示,登錄對應調用鏈包含3個微服務,查票對應調用鏈包含10個微服務,且兩條調用鏈包含的微服務沒有交集。請求到達速度如圖9所示,持續時間為35分鐘。

表1 登錄和查票對應調用鏈包含微服務列表

圖9 請求到達速度圖
登錄和查票的平均響應時間隨時間變化圖像如圖10所示。前2分鐘登錄和查票的平均響應時間均較長,這與訂票系統Train_ticket本身相關。在使用Train_ticket過程中發現,系統初啟動時明顯慢于啟動一段時間之后。查看部署優化工具的日志發現,第4分鐘由于集群資源不足,觸發了擴縮容操作,共有31個微服務縮容、4個微服務擴容。登錄相關的3個微服務保持不變,查票相關的4個微服務執行了擴容操作、5個微服務執行了縮容操作、1個微服務保持不變,使得查票的平均響應時間在第4~7分鐘變短。

圖10 平均響應時間變化圖
從第5分鐘開始,查票請求數量逐步增加,登錄請求數量逐步減少。第7分鐘處,查票相關的2個微服務執行了擴容操作,使查票平均響應時間在第7~8分鐘略微降低。
第8~15分鐘,查票平均響應時間隨著請求數量遞增,在整體上呈變長的趨勢。但在第11分鐘處,查票相關的7個微服務執行了擴容操作,使得平均響應時間明顯縮短。
第16分鐘處,集群資源不足導致同時觸發了縮容和擴容操作。登錄相關的3個微服務縮容,查票相關的微服務中5個需要添加實例。但由于此時集群資源不足,且需要執行擴容操作的微服務數量多于執行縮容操作的微服務數量。因此從需要擴容的5個微服務中隨機挑選了3個進行擴容,分別為ts-seat-service、ts-route-service和ts-station-service。擴容操作在帶來查票平均響應時間縮短的同時,縮容操作也使得登錄平均響應時間在短時間內急劇變長。隨后登錄平均響應時間降至正常水平,這與登錄的請求數量減少有關。登錄相關的3個微服務初始部署時各有2個實例,可正常響應100 req/s(request/s,每秒鐘請求數量)的請求到達速度而不觸發擴容操作。16分鐘后,雖然登錄相關的3個微服務實例數量均變成了1個,但登錄的請求到達速度已變為10 req/s。
查票的請求到達速度在15~20分鐘一直維持在50 req/s。從21分鐘開始,查票的請求到達速度開始下降。而在21分鐘時,恰好因為20分鐘時較高請求到達速度執行了擴容操作,使得第21和22分鐘平均響應時間在擴容和請求數量變少的雙重作用下顯著縮短。
第27分鐘處,查票相關的6個微服務觸發了縮容操作,導致平均響應時間短暫變長,而后又隨請求數量的降低而縮短。
綜合以上分析,本文提出的基于MAPE環路的自適應部署優化工具,在集群資源固定且有限的場景下,能有效地調節微服務的實例數量、降低系統整體響應時間。但同時也發現了一些問題,第15~20分鐘,查票請求到達速度維持在50 req/s,此時為相應微服務執行擴容操作,雖能夠降低平均響應時間,但仍然無法將平均響應時間縮短至第5~9分鐘的水平。經分析Train_ticket發現,存在單個微服務的多個實例使用同一數據庫服務的情況,如ts-station-service 的四個實例均使用ts-station-mongo。這使得系統的瓶頸轉移至數據庫處,此時再為ts-station-service增加實例,不能有效降低平均響應時間。另外,從第10~13分鐘可以看出,本文提出的基于MAPE環路的自適應部署優化方法有一定滯后性,平均響應時間隨請求數量增長一小段時間后,才會觸發自適應調節操作。
本文提出一個基于MAPE環路的自適應部署優化方法,在開源系統Spring Cloud Sleuth和Zipkin的基礎上,實現了面向微服務系統的運行時監控和部署優化工具,并通過實驗驗證了部署優化方法的有效性。在集群資源固定且有限的場景下,部署優化方法能有效調節微服務實例數量、降低系統整體響應時間。
本文提出的基于MAPE環路的自適應部署優化方法還存在一些不足。不同微服務實例對服務器資源需求不同,當集群資源不足時需要先減少一些實例再啟動相同數量的實例,但這是建立在不同微服務實例需要資源相當的基礎上的。在一定程度上,為微服務添加實例可降低平均響應時間,但當實例數量到達一定程度時,系統的性能瓶頸可能轉移到數據庫等部分,此時繼續添加實例無法有效降低平均響應時間。通過實驗發現,自適應調節仍有一定的滯后性。