閱讀310 返回首頁    go 技術社區[雲棲]


微服務治理實戰:服務流的自動化構建與應用

 

講師介紹  20170116103321477.jpg

張真

宜信技術研發中心高級架構師

 

  • 目前負責金融基礎服務、微服務架構演進/計算平台、DevOps平台等。

  • 曾任IBM,負責雲計算、應用服務器等,擁有多個國際專利。開源社區活躍貢獻者。

 

主題簡介:

  1. 服務流及微服務架構下服務流構建的挑戰

  2. 自動化構建(微)服務流

  3. 自動化構建服務流的應用場景

 

先談談這個話題的早期背景,作為一個發展了十年的企業,我們公司內部存在大量的係統,這些係統可能包括多種架構,多種技術棧,它們互相關聯,互相作用成就了複雜的業務體係。隨著業務演變,人員更迭,係統演進等諸多因素的疊加,公司級係統的關聯關係與狀態逐步變得難以精確梳理,難以精細維護。

 

基於這樣的痛點,便產生了這個話題的思考:能否使用技術手段自動地、精確地、具現化地勾勒公司級的應用/服務關聯圖譜?

 

1服務流以及微服務架構下麵臨的挑戰

 

描述關聯關係會讓人聯想到一個詞“拓撲”。拓撲是源於數學的一門方法論,它是研究與大小,形狀無關的點、線關係的方法。在計算機領域,拓撲是一組計算機相關的抽象點,以及點之間聯線構成的圖形。

 

大家最熟悉的是網絡拓撲,它是把計算機和通信設備抽象為一個點,把傳輸介質抽象為一條線,由點和線組成的幾何圖形,是對物理網絡環境的描述。網絡拓撲的核心標識是IP地址,所以每個點就是一個IP地址的抽象,而點與點之間的連線代表網線,光纖或無線連接。這個圖形描繪了物理網絡的靜態結構。

 

網絡拓撲舉例:

20170116103332423.png

(注:圖源自網絡)

 

在APM(應用性能管理)領域,提供了應用拓撲。它是將終端(用戶),中間件(包含應用),數據庫等抽象成點,用有向的連線來描述訪問關係(數據交流傳輸的路徑)。它強調端到端的流程繪製。

 

應用拓撲舉例:

 

20170116103339444.png

(注:圖源自網絡)

 

說說今天的話題,什麼是服務流?它與前麵二者的區別與聯係是什麼?

 

服務流(Service Exchanging Topology)是描述服務與服務的靜態拓撲和運行時特性的圖譜。之所以稱為服務“流”,是強調它更加動態,它涵蓋應用拓撲的內容,比應用拓撲提供更加深入的抽象粒度,也提供更加豐富的運行時狀態。同時它不強調應用的概念,但兼容不同架構下的應用概念。

 

在說明服務流如何抽象節點之前,先簡單梳理一下服務,應用以及進程的概念:

 

  • 進程:提供運行資源的載體,這些資源包括CPU,內存,網絡,IO等。

  • 應用:符合某種IT工業標準的,可獨立部署的單元,比如JEE應用通常包括WAR包,EAR包,EBA包等。

  • 服務:提供某種處理或計算能力的代碼集合。

 

通常我們會麵對3種架構:單體架構,SOA架構和微服務架構。

 

單體架構並不強調服務的概念,所以可能有服務,也可能無服務,而同一個進程中可能包含多個應用,比如Tomcat啟動後,是一個進程,允許部署多個應用。如下圖:

 

20170116103346800.png

單體架構中三者的關係

 

從SOA開始,服務開始作為應用的必須單元,一個應用中可能包含多個服務。同時隨著服務思想的發展,進程被建議部署單個應用(當然多應用也被允許),服務之間通過服務總線進行交互。如下圖:

 

20170116103355786.png

SOA架構中三者的關係

 

微服務架構進一步強化服務的概念,要求服務成為可獨立部署的單元,所以從部署形態上出現了兩種基本模式:

 

  • 一進程一應用一服務,例如一個tomcat裏麵部署一個war應用,這個應用隻包含一個服務

  • 一進程無應用一服務,例如SpringBoot取代了傳統的war部署,直接實現服務部署。

 

同時微服務之間基於服務發現進行直連交互,而對外部交互通過服務網關進行。如下圖:

 

20170116103402669.png

微服務架構中三者的關係

 

接下來,闡述一下服務流提出的靜態拓撲和運行時特性的含義。

 

1、靜態拓撲:是描繪服務本體,服務之間的關聯。

 

服務本體是對以下四種類型服務的抽象:

 

  • 業務服務:就是業務代碼集合,提供業務邏輯和流程,是服務流主要的抽象存在。

  • 數據源服務:提供數據存儲和查詢,比如關係型數據庫MySQL,緩存Redis,非關係型數據庫MongoDB等。

  • 代理服務:提供訪問代理,比如服務網關,Nginx,Haproxy等。

  • 消息傳輸服務:提供同步或異步消息通道,比如RabbitMQ,Kafka等。

 

分類的標準是按照服務之間的關聯特性來確定的:

 

  • 業務服務可以是輸入入口(有向連線的終點),也可以是輸出出口(有向連線的起點)。

  • 數據源服務隻能是輸入入口。

  • 代理服務盡管不處理任何邏輯,但可以是輸入入口,也可以是輸出出口。

  • 消息傳輸服務隻能是輸入入口,但值得注意的是它的入口類型(客戶端)包括兩種:消息生產者和消息消費者,這是需要區別開的。

 

2、運行時特性: 主要是描述服務過程以及調用過程的一係列監控指標。

 

  • 服務過程指標:被訪問地址,操作方法,請求/響應內容,響應時間,吞吐量,錯誤數,訪問時間戳等。

  • 調用過程指標:調用地址,操作方法,請求/響應內容,異常/錯誤數,響應時間,調用量,調用時間戳,調用服務的特征(服務類型,是否集群,版本,用戶/權限)等。

 

之所以能夠提供更深入的粒度是因為服務流使用了服務畫像數據和客戶端畫像數據,第二部分會詳述。所以從領域來看服務流、應用拓撲、網絡拓撲又分別對應服務監控、APM、機房監控這三個領域(如下圖):

 

20170116103409552.png

 

在微服務架構下,服務流的繪製存在如下挑戰:

 

1) 微服務架構在實現服務獨立部署的同時,也帶來服務節點規模的大幅增長,導致關聯關係更加複雜。依靠人工收集變得難以落地;如果依賴Zookeeper,etcd等建立服務注冊中心,雖然可以收集到服務本體的一些信息,但沒有服務的關聯信息,且如何更新維護依然是問題。

 

2) 服務更加多樣化,變更更頻繁,且不同步。由於服務會被拆分得很細膩(有助於更加靈活的編排和獨立運維),所以服務的種類自然增長,且由於可能是不同團隊維護這些服務,服務的上線,變更等運維過程變得極大的不同步。

 

3) 微服務的部署形態有多樣性。例如傳統JEE應用是由應用服務器提供一個端口接收訪問(一進程一應用一服務);而新的部署形態可能由服務對外提供一個或多個端口接收訪問(一進程無應用一服務),如果是多個端口時,可以把這個服務看出一個聚合服務也可以將每個端口抽象成一個服務,從服務流的角度這種服務抽象需要具備聚合和分散的特性。

 

4) 在一個複雜生產環境下,還要考慮與單體架構,SOA架構的兼容問題。例如單體架構下需要識別“服務組件”或被抽象成一個“大服務”;SOA下同一應用下可能存在多個服務,也需要被識別出來,並被分別抽象。

 

2自動化構建(微)服務流

 

對於服務流的構建,我們仍然采用了微智能的思想,希望服務流的構建過程形成完全反饋閉環。

 

20170116103417994.png

微智能設計思想的三觀

 

關於微智能設計思想的詳述,請參考DBAplus社群的《微服務架構下,如何打造別具一格的服務治理體驗?》

 

首先,構建服務流主要依賴兩種數據:

 

1) 服務畫像

 

是描述服務本體的信息,包括應用唯一標識(AppID)(兼容單體架構,SOA架構),服務名(Service ID),服務實例的URI,服務接口的URI,服務接口的元數據(類,方法,入參出參,注解,部署描述符)。

 

服務端的抽象就是服務本體,但關聯線的對象是服務接口,如果服務本體包含n(n>0)個服務接口,則關聯線類型就有n種,關聯線條數=S[1]+S[2]+S[3]+...+S[n](S[k]代表某個服務接口的實際關聯線個數,S[k]>=0,1<=k<=n)

 

2) 客戶端畫像

 

是描述調用服務行為的信息,是服務關聯抽象的基礎。包括應用唯一標識(AppID)(兼容單體架構、SOA架構),所在服務名(Service ID),訪問的URI,操作的元數據(操作,方法,入參出參)。

 

從抽象的角度,客戶端抽象是以訪問的URI(關聯線)為區分的:

 

  • 同類型的客戶端有多個實例,都訪問同一個服務URI,是一個客戶端抽象

  • 同類型的客戶端有多個實例,訪問n(n>1)個不同的服務URI,則應有n個客戶端抽象

  • 不同類型的客戶端有若幹實例,都訪問同一個服務URI,是一個客戶端抽象

  • 不同類型的客戶端有若幹實例,訪問n(n>1)個不同的服務URI,則應有n個客戶端抽象

 

那麼實踐微智能思想,自動化構建服務流,采用以下技術來捕獲這兩種數據:

 

1)中間件劫持技術

 

這裏的中間件是一個廣泛的服務運行時的代稱,它可能是應用服務器(例如Tomcat),類應用服務器運行時(例如SpringBoot)等。采用劫持技術的目的是希望無侵入的實現服務畫像,這裏的無侵入是無需研發團隊去做代碼埋點,也無需在服務代碼層麵增加任何依賴(比如jar包)。

 

服務畫像的操作是發生在應用/服務啟動的階段,根據工業標準做類掃描和部署描述文件分析提取畫像數據。由於每次啟動,都會觸發畫像,所以畫像數據一直保持最新的實際狀態。

 

2)客戶端劫持技術

 

客戶端劫持是根據客戶端實現,清楚分析客戶端的編程模型,然後通過編程模型配合客戶端實現源代碼,定位需要埋入劫持代碼的位置。從實現上它是以中間件劫持為基礎的,所以也是無侵入的,是對其進行擴展從而實現客戶端畫像。

 

客戶端畫像的操作是發生在調用實際發生的階段,並不像服務畫像可以在啟動階段一次捕獲,所以客戶端畫像的過程是逐步積累的,最終達到完整勾勒。如果服務重啟後,客戶端畫像會重新開始這個過程,同樣也保持最新的實際狀態。

 

其次,在實際構建時,還需要一種輔助數據,以達到更準確的擬合效果,這就是溯源數據。

 

溯源數據是從訪問協議中提取的特征數據,用來追溯訪問源頭以及訪問可能經過的路徑。

 

訪問協議可能是工業標準協議(http,rmi,soap,smtp等),也可能是自定義協議。一般來說協議載體都分為header和body兩個部分,這裏主要談談header,header存放協議必須的元數據。我們需要從這些元數據中找到可以用來描述訪問源的線索。

 

典型場景是兩個服務之間可能通過正向或反向代理進行訪問,僅僅依靠服務畫像和客戶端畫像是無法真正關聯這兩個服務(如下圖):

 

20170116103428984.png

 

那麼通過提取各個代理的IP(溯源數據),就能掌握請求的通過路徑,進而關聯兩個服務(如下圖)。

 

20170116103435645.png

 

接下來,在獲取服務流相關的基礎數據後需要進行一個擬合過程。

 

首先,建立每個服務抽象的IPO模型,IPO是指Input(輸入),Process(處理),Output(輸出)。

 

20170116103446244.png

 

  • 輸入是使用溯源數據實現溯源感知。這裏補充了瀏覽器用戶這個抽象節點,模型展示了四種基本形態:業務服務直連,業務服務通過代理服務,瀏覽器用戶直連,瀏覽器用戶通過代理服務。

 

  • 輸出是使用客戶端畫像實現調用感知。模型展現了四種調用目標:代理服務,業務服務,數據服務,消息服務(注意:消息消費者實際也是客戶端,從關聯角度,並不作為輸入)。

 

  • 處理是使用服務畫像實現服務抽象。

 

接著,當每個服務抽象的IPO模型建立後,就可以進行擬合。擬合的基本算法是離散點的有向圖匹配(廣度優先better)。在實際生產中,我們還進行了一些優化,因為實際情況複雜得多,而且算法複雜度在萬級節點時會有各種瓶頸:

 

  • 增加了未知服務(可能是第三方係統),未知服務隻能出現在溯源感知和調用感知中,它沒有IPO模型

  • 已知節點優先原則,它有完整的IPO模型,它的調用和溯源是必然存在的,避免重繪

  • 優先調用關聯,後處理溯源關聯。調用關聯包含其他已知節點(可自動延續繪製),終結點(各種數據源,MQ,無調用無溯源),未知節點(無溯源,可能有調用)

 

下麵以TOMCAT+JEE服務為例,剖析關鍵實現 。

 

Java技術棧實現代碼劫持是依賴兩種常見的AOP技術(Java三板斧之一)。

 

  • 字節碼編程:不同jdk版本可能存在兼容性問題,謹慎使用。推薦javassit,如果需要支持多版本jdk,需要考慮根據不同版本動態加載兼容的javassit。我們是在劫持入口類的方法上使用,在之前或之後增加代碼,盡量避免修改代碼(減少出錯,通用性更強)。

  • Java原生代理:無接口不代理。多多益善,性能影響小,無兼容問題。

 

為了獲取服務畫像和溯源數據,先對Tomcat進行中間件劫持。劫持核心是掌控Tomcat ClassLoader Tree,獲得優先加載權,從而可以改變這些行為。盡管各種JEE應用服務器實現不同,但其ClassLoader Tree結構基本類似。通過植入一個ClassLoader來獲取優先加載權。通過加載改寫後的class,來改變行為。我們把這個ClassLoader稱為UAVClassLoader(無人機類加載器):

 

20170116103454167.png

 

UAVClassLoader算法基本原理

 

1) UAVClassLoader創建時,將能夠讀取到的Class文件對應的Class名存儲到ClassMap中。

 

2)將TomcatLoader設置為UAVClassLoader的Parent。

 

3)將UAVClassLoader設置為TomcatLoader的一個屬性。

 

4)重寫TomcatLoader的loadClass方法。

  • 如果UseUAVClassLoaderFlag為true,則使用UAVClassLoader.loadClass;

  • 加載成功則返回Class;

  • 失敗則使用TomcatLoader自己的loadClass;

 

5)UAVClassLoader的LoadClass方法。

  • 如果ClassMap中含有要加載的Class,則使用自己的findClass加載Class

  • 否則,將UseUAVClassLoaderFlag設置為false;

  • 使用TomcatLoader.loadClass(注:這時TomcatLoader會直接用自己的loadClass);

  • 將UsePlusLoader Flag設置為true。

 

服務畫像收集

 

JEE服務啟動實際是Web容器的創建過程。在Tomcat中的StandardContext就是Web容器的根類,在其加載的時候,UAVClassLoader會感知,通過改寫或字節碼手段在其start方法的最後植入代碼,完成兩個步驟:

 

1)收集將Web容器的上文信息:包括WebAppClassLoader實例,Context Path,應用名,ServletContext,BasePath(應用實際路徑),WorkDir(應用工作目錄)等

 

2)植入應用、服務畫像的代碼。服務畫像是按照技術規範,常見的技術規範:Servlet,JAXWS,JAXRS,Spring,RMI,RPC(Netty,Thrift,Hessian等)。針對每種技術規範從3個方麵進行收集:

 

  • Class和Method:通過Java的反射方式提取信息,如服務類名,方法名,入參出參。

  • Annotation:通過注解掃描工具提取具有相關注解的類,然後通過注解API提取注解信息。

  • 部署描述符:通過WebAppClassLoader獲取web.xml, spring-config.xml, log4j.xml等部署描述符文件路徑,然後使用DOM解析提取關注的tag信息。

 

20170116103502855.png

 

溯源數據收集

 

溯源數據的捕獲實際與服務監控數據捕獲發生在同一個階段。運用中間件劫持技術改寫Tomcat的CoyoteAdaptor.service()方法,它負責整個Tomcat的請求處理,在方法開頭攔截請求,方法結尾攔截響應。這裏獲取應用服務器,應用,所有的URL的性能指標;同樣,運用中間件劫持技術改寫Tomcat的StandardWrapper.service()方法,它負責Servlet的請求處理,同上如法炮製,在這裏捕獲溯源數據即可,同時也獲取服務的性能指標。

 

20170116103511630.png
 

Tomcat是以HTTP協議為基礎的。HTTP協議的Header中的字段可以幫助溯源:

 

  • Client Address:直連客戶端IP地址

  • X-Forwarded-For: 如果存在,則為代理路由地址鏈,則直連客戶端為代理服務

  • Host:表明遠程主機甚至端口信息,如果直連客戶端是代理服務,則Host為代理IP地址和端口

  • User-Agent:代理描述,可用來區分瀏覽器還是程序客戶端,當然還可以提取很多瀏覽器終端信息。

     

同時,還可以提取一些自定義的Header信息幫助擬合,這需要結合客戶端劫持,下文會進行說明。

 

客戶端畫像收集

 

首先要標準化客戶端畫像的元數據體係。調用感知是基於調用地址,訪問協議,調用結果的特征提取來確定目標服務的。

 

1)調用地址:以類URI格式。

  • http/https服務(業務/代理服務): https://:/

  • 關係型數據庫(數據源服務): jdbc:<數據庫類型>://:,:/<數據庫名>

  • 非關係型數據庫或緩存(數據源服務):<數據源類型>://:,:/<數據庫集合名>

  • 消息隊列(消息服務):mq:<消息中間件類型>://:/<隊列名>

 

2)訪問協議:某種訪問動作。例如HTTP的POST,SQL插入,發送/訂閱消息,Redis的hgethashall,MongoDB的Collection操作等。

 

3)訪問結果特征:服務的基礎棧類型,是否集群,例如nginx,tomcat,apache等。

 

接下來,就是通過客戶端劫持,以常用的http客戶端Apache HttpClient為例,隻需兩步:

  • 識別org.apache.http.impl.client.InternalHttpClient是Apache同步客戶端的核心類

  • 運用字節碼改寫其doExecute方法,在方法的開頭和結尾插入畫像代碼獲取調用感知的特征信息

 

前文還提到為了幫助溯源感知,可以在上遊服務的客戶端,通過客戶端劫持在訪問協議的Header中加入自定義的一些信息。

 

例如為了在擬合時,合並兩個通過HTTP代理服務關聯的服務,在上遊服務客戶端調用時,可以添加一個Header字段(比如:UAV-Client-Src),這個字段存放服務抽象的唯一標識;當下遊服務提取溯源數據時,可以將該字段取出,作為源頭服務的唯一標識,這樣就能完成合並。

 

20170116103520965.png

 

3服務流的應用場景

 

場景一:具現化的應用/服務運維

 

我們的服務監控係統叫無人機,代號UAV。UAV定義了服務流的三種視圖:

 

1)應用/服務級:如果是單體架構或SOA架構,就是應用集群;如果是微服務架構,就是服務集群。集群內每個進程就是一個應用/服務實例。

 

2)應用/服務組級:這是個邏輯概念,可以根據產品線或業務架構來確定。它由多個不同類型的應用/服務集群組成。

 

3)全網級:就是整個IDC中心所有應用/服務集群。

 

在IPO模型基礎上定義視圖的表現層(如下圖):

 

20170116103529463.png

 

下麵是實際係統截圖:

 

應用/服務級視圖(App /Service Cluster)

 

20170116103539349.png

 

應用/服務組級視圖(App/Service Business Group)

 

20170116103547500.png

 

全網服務流視圖(Global Service Business Topology)

 

20170116103559100.png

 

全網視圖又叫服務星雲,因為上圖是縮放後的效果,全網可能如下圖:

 

20170116103611208.png
 

或者:

 

20170116103620788.png

 

場景二:服務風控與關聯分析

 

這是利用服務的關聯,在一個完整業務鏈路(由一係列的服務組成),當某些服務出現問題(比如很慢)時,可以通過關聯分析快速定位問題源並自動的采取某些措施,常用的措施:實時的,自動化及時管控/熔斷等;預測整體業務鏈路風險,提前可控切換或其他預案措施。

 

場景三:自動化調用鏈生成

 

調用鏈一直是服務治理的熱門話題。經典的做法是在業務代碼中進行埋點。通過自動化構建服務流之後,可以大量減少埋點工作。通過一個比喻來說明為什麼可以達到這樣的效果,可以把服務流看成城市間的道路,每個請求的流動可以看成道路上的車輛,當道路已經很清楚的被描繪出來後,每輛車就可以被更自動的追蹤了。

 

基本思路:

1) 通過中間件劫持,在服務畫像的位置,產生或繼承請求ID;

2) 通過客戶端劫持,在客戶端畫像的位置,產生或繼承請求ID;

3) 在單線程模式下,自動傳遞請求ID;

4) 在跨線程模式下,交換請求ID,此處是唯一需要少量的代碼埋點的場景。

 

由於篇幅的關係,對於場景二,三沒有展開說明,希望未來可以和大家分享。

 

4總結

 

最後,總結一下。服務流是應用拓撲的擴展,可以更加深入細致的描繪服務的關聯關係。可以通過中間件劫持,客戶端劫持,溯源數據提取等手段實現服務抽象的擬合,進而自動化的構建服務流。具體落地需要根據實際使用的技術棧來考慮,本文列舉了Tomcat+JEE應用的場景下的一種實現方法供大家參考。另外,服務流可以被應用到很多生產場景中去,例如服務監控,服務風控,自動化調用鏈等。

原文發布時間為:2017-01-16

本文來自雲棲社區合作夥伴DBAplus

最後更新:2017-05-15 17:03:17

  上一篇:go  互聯網企業安全高級指南2.2 如何建立一支安全團隊
  下一篇:go  互聯網企業安全高級指南2.1 創業型企業一定需要CSO嗎