總體結(jié)構(gòu)與追蹤數(shù)據(jù)管理
我們的方案分為兩大部分:所有服務(wù)集成到追蹤庫中,分配一個(gè)內(nèi)存塊來存儲與查看追蹤數(shù)據(jù)。我們選擇Zipkin,在Twitter開發(fā)的一個(gè)可擴(kuò)展的開源追蹤框架,用于存儲與查看追蹤數(shù)據(jù)。Zipkin通常以Finagle對的形式出現(xiàn),但是,像上一節(jié)提及的一樣,我們排除了與我們現(xiàn)有基礎(chǔ)設(shè)施沖突的并發(fā)癥。Knewton構(gòu)建追蹤庫,稱為TDist,從地面起,開始作為公司“黑客日”的實(shí)驗(yàn)。
追蹤數(shù)據(jù)模型
就我們的方案而言,我們選擇使用Zipkin來匹配數(shù)據(jù)模型,輪流從Dapper大量借入。一個(gè)追蹤樹由一系列的跨度組成?缍却硪粋(gè)特殊的呼叫從服務(wù)器接收開始,到服務(wù)器發(fā)送,最后是客戶端接收。舉個(gè)例子,在服務(wù)器A和服務(wù)器B之間的呼叫與響應(yīng)將會作為一個(gè)簡單的跨度:
每一個(gè)跨度(span)有三個(gè)ID:
-
Trace ID:一個(gè)軌跡中所有的跨度(span)共享同一個(gè)Trace ID。
-
Span ID:用以標(biāo)示不同的跨度(span)。Span ID與Trace ID不一定相同。
-
Parent Span ID: 只有子跨度持有這個(gè)ID,根跨度沒有Parent Span ID。
下面的圖展示了在一個(gè)樹結(jié)構(gòu)的調(diào)用中,上面三個(gè)ID是如何應(yīng)用的。注意在整個(gè)樹結(jié)構(gòu)中Trace ID是一致的。
更多詳情, 請參見 Dapper paper.
TDist
TDist是Knewton開發(fā)的一個(gè)Java庫。利用該庫我們可以追蹤所有的應(yīng)用. TDist 目前支持Thrift, HTTP, and Kafka, 還可以用來追蹤使用了注解(參見Guice)的方法的調(diào)用。
對每一個(gè)線程服務(wù)或者對另外一個(gè)服務(wù)發(fā)起的請求都分配一個(gè)跨度(span),類庫會在后臺對跨度進(jìn)行傳播和更新。收到一個(gè)請求(或者即將發(fā)出一個(gè)請求),追蹤數(shù)據(jù)會被添加到一個(gè)內(nèi)部隊(duì)列中,DataManager將TraceID修改添加到處理請求的線程的名稱中。工作線程消費(fèi)隊(duì)列,然后將追蹤數(shù)據(jù)發(fā)布到追蹤消息總線。Java ThreadLocal可以很方便的存儲和讀取線程范圍內(nèi)的全局變量,我們在DataManager中使用了這種方法。
通常,線程將遠(yuǎn)程調(diào)用或者報(bào)告回父線程這樣的實(shí)際的工作轉(zhuǎn)嫁給其他線程來做。因此,我們也實(shí)現(xiàn)了線程factory和executor,這樣就知道如何檢索父線程的追蹤數(shù)據(jù),并將其分配給子線程,從而使得子線程也可以追蹤。
Zipkin
追蹤數(shù)據(jù)一旦經(jīng)過TDist到達(dá)追蹤消息總線,Zipkin基礎(chǔ)設(shè)施會處理剩下的流程。多收集器實(shí)例,從消息總線中消費(fèi),存儲追蹤數(shù)據(jù)中每個(gè)記錄。一個(gè)分離的查詢集合與web服務(wù),Zipkin的部分源代碼,為了追蹤依次查詢數(shù)據(jù)庫。我們?yōu)榱耸沟檬虑樽兊煤唵,決定參與查詢和web服務(wù),并且也因?yàn)檫@種組合服務(wù)是內(nèi)部的,并且有可預(yù)測的交通模式。但是,收集器是從查詢與web服務(wù)中分離的,因?yàn)樵蕉郖newton服務(wù)集成到收集器,越多追蹤數(shù)據(jù)需要處理。
Zip kin UI
盒子外部,Zipkin在整個(gè)服務(wù)中提供一套簡單的UI給視圖追蹤。當(dāng)在所有服務(wù)中,非常容易地打印視圖日志用于一個(gè)特殊的追蹤ID號,Zipkin UI在每次調(diào)用的持續(xù)時(shí)長中提供一個(gè)總體視圖,不需要查詢數(shù)百個(gè)日志語句。在一個(gè)特定的周期時(shí)間內(nèi),它也是一個(gè)有效的方式來辨別最大的或者最慢的追蹤。在發(fā)現(xiàn)這些異常值的情況下,允許我們標(biāo)志出哪里重復(fù)調(diào)用其他服務(wù),為了總體調(diào)用鏈而放慢我們的SLA。以下是Zipkin UI中的追蹤截圖:
當(dāng)然,UI并不會撤銷。雖然它很容易看清楚個(gè)人痕跡,我們發(fā)現(xiàn)Zipkin UI 缺乏檢查匯總數(shù)據(jù)。比如說,目前還沒有方法獲取總的時(shí)間信息或者總的數(shù)據(jù),稱之為端點(diǎn),服務(wù)等等。
在整個(gè)發(fā)展過程中,推出Zipkin基礎(chǔ)設(shè)施,我們對Zipkin的開源做出了些許貢獻(xiàn),感謝它的活躍以及成長社區(qū)的支持。
Splunk
正如上面所提到,當(dāng)前處理請求的線程名也會變動,其Trace ID會追加在上面。因?yàn)檫@樣,我們在需要特定請求的時(shí)候,才能從所有啟用追蹤的服務(wù)上查詢?nèi)罩。這使得調(diào)試更加方便,同時(shí)事實(shí)證明其用于事后分析、日志聚合、獨(dú)立問題的調(diào)試及解釋平臺的異常行為時(shí)也比較有用!
Thrift
Thrift是一個(gè)用于構(gòu)建可拓展服務(wù)的跨平臺的RPC框架。在Thrift中,用戶可以定義一個(gè)服務(wù)、數(shù)據(jù)類型的規(guī)則,Thrift就會在許多不同的語言中編譯其規(guī)則,這時(shí)用戶就可以用想要的開發(fā)語言實(shí)現(xiàn)所生成的服務(wù)接口。Thrift同時(shí)自動生成客戶端代碼及用戶為服務(wù)所定義的數(shù)據(jù)結(jié)構(gòu)。
在Knewton中Thrift是服務(wù)之間使用最普遍使用的RPC框架,我們服務(wù)的大多數(shù)通過使用此框架進(jìn)行通信,所以在維護(hù)后端兼容性的時(shí)候支持它,對于此項(xiàng)目的成功性而言有著重大的影響。更準(zhǔn)確的說,我們想讓未啟用追蹤的服務(wù)能與啟用追蹤的服務(wù)通信。
當(dāng)我們開始研究給Thrift添加追蹤支持的時(shí)候,我們與不同的兩個(gè)方式進(jìn)行實(shí)驗(yàn)。第一種方式涉及到一個(gè)修改過的Thrift編譯器,而第二種涉及到修改后的序列協(xié)議及服務(wù)處理器。兩種方法都有其優(yōu)缺點(diǎn)。
自定義編譯器
在這個(gè)方法中,我們體驗(yàn)修改C++簡易編譯器來生成額外的服務(wù)接口,可以傳遞追蹤數(shù)據(jù)給用戶?赡茏钪泳褪荢crooge,修改簡易的編譯器并不罕見。修改過的編譯器的其中一個(gè)優(yōu)勢是,客戶端在它們代碼中交換較少的類實(shí)現(xiàn),由于在生成的代碼中支持追蹤?蛻舳艘部梢垣@得來自服務(wù)接口的追蹤數(shù)據(jù)作為參考。
雖然我們沒有檢測,我們也可以認(rèn)為這個(gè)方法將會更快速地受到應(yīng)用,由于只有較少的類調(diào)用追蹤實(shí)現(xiàn)。但是,我們我么將會重編譯我們所有的簡易代碼,偏離開源版本,使得它在將來更難升級。我們也將會認(rèn)識到,允許用戶訪問追蹤數(shù)據(jù)將缺乏渴望或者安全,并且數(shù)據(jù)管理可以更好地保證TDist的一致性。
自定義協(xié)議與服務(wù)處理器
最終我們應(yīng)用這個(gè)方法到生產(chǎn)中。并不會維持一個(gè)自定義編譯器來大量降低我們開發(fā)成本。自定義協(xié)議與服務(wù)接口的最大缺陷是,我們不得不升級來節(jié)儉0.9.0(從0.7.0),利用一些特征將會使得它更加容易插入我們自定義協(xié)議與處理器的追蹤組件中。升級需要許多組織的協(xié)調(diào)。令人慶幸的是,更新的簡易版本是向后兼容舊版本的,我們可以在TDist工作,當(dāng)Knewton服務(wù)被更新到新版本時(shí)。但是,在我們可以開始用我們的分布式追蹤方案來集成它們時(shí),我們?nèi)匀徊坏貌会尫潘械腒newton服務(wù)。
升級 Thrift
一般來說通過依賴管理工具升級依賴庫相對比較容易,但如果是那些類似Thrift的RPC框架,或者一些有很深調(diào)用鏈的
SOA框架,問題就會復(fù)雜很多。一個(gè)典型的服務(wù)通常會同時(shí)包含服務(wù)端和客戶端的代碼,而服務(wù)端的代碼往往會依賴其他的一些客戶端的依賴庫。所以,升級的時(shí)候需要從服務(wù)調(diào)用樹的葉子節(jié)點(diǎn)開始向上逐級升級,以避免服務(wù)調(diào)用的兼容性問題。因?yàn)榉⻊?wù)提供方可能并不知道調(diào)用方是否可以檢測出那些附加的追蹤數(shù)據(jù)。
另外一個(gè)障礙是,一些服務(wù)會依賴Thrift 0.7.0(譯者注:上文談到需要升級為0.9.0),比如Cassandra客戶端依賴Astyanax,Astyanax依賴的一些第三方依賴庫會反過來依賴Thrift 0.7.0。對于Astyanax,我們不得不通過Maven將依賴的JAR包屏蔽(shade )并且修改包名來避免新舊版本Thrift庫之間的沖突。整個(gè)升級過程必須迅速,并且沒有停機(jī)時(shí)間。為了不讓升級過程給Knewton的其他團(tuán)隊(duì)帶來額外成本,分布式追蹤小組不得不實(shí)施并推動整個(gè)變更過程。
追蹤簡約組件:他是如何工作的
我們的簡約方案由自定義、向后兼容的協(xié)議與自定義服務(wù)處理器組成,提取跟蹤數(shù)據(jù),放在路由到相應(yīng)的RPC調(diào)用。我們的協(xié)議基本上在每個(gè)消息頭寫入追蹤數(shù)據(jù)。當(dāng)RPC調(diào)用到達(dá)服務(wù)器,處理器將會識別并且標(biāo)記呼入調(diào)用是否有追蹤數(shù)據(jù),因此它可以恰到好處地響應(yīng)。追蹤數(shù)據(jù)的調(diào)用也從中獲取響應(yīng),來自非集成服務(wù)的請求不會攜帶沒有響應(yīng)的追蹤數(shù)據(jù)。這使得追蹤協(xié)議向后兼容,因?yàn)榉⻊?wù)器傳出的協(xié)議不會寫追蹤數(shù)據(jù),如果指示不是出自處理器所服務(wù)的請求。一個(gè)追蹤協(xié)議可以檢測有效載荷是否包含追蹤數(shù)據(jù)基于前面幾個(gè)字節(jié)。簡約追加協(xié)議ID到協(xié)議頭,假如讀取協(xié)議時(shí)發(fā)現(xiàn)前面幾個(gè)字節(jié)不顯示追蹤數(shù)據(jù),緩存與有效載荷的存在作為一個(gè)非追蹤載荷來重讀。當(dāng)正在讀取一個(gè)消息時(shí),協(xié)議將會提取追蹤數(shù)據(jù),并且使用數(shù)據(jù)管理器來保存它們至本地線程,用于RPC呼入調(diào)用的線程服務(wù)。假如該線程額外調(diào)用于其他服務(wù)的下游,追蹤數(shù)據(jù)將通過TDist從數(shù)據(jù)管理器自動地被提取出來,并且添加到外部消息。
下面是一個(gè)圖解,展示如何修改載荷來添加追蹤數(shù)據(jù):
Kafka消息追蹤支持
當(dāng)我們?yōu)閗afka提供消息追蹤支持時(shí),我們希望把Kafka服務(wù)(也被稱為brokers),做為一個(gè)黑箱看待。換言之,我們希望brokers不需要知道消息是否被消費(fèi)(即brokers不需要知道消息是否通過它發(fā)送給消費(fèi)者)因此我們不需要修改Kafka的源碼。我們采用與RPC 服務(wù)類似的處理方式,在升級生產(chǎn)者之前先升級消費(fèi)者。消費(fèi)者反向兼容并能檢查到一個(gè)包含追蹤信息的消息,以之前Thrift 協(xié)議描述的方式反序列化內(nèi)容。為了能夠?qū)崿F(xiàn)上述方式,我們需要客戶端封裝他們在追蹤系統(tǒng)中使用的序列化/反序列化實(shí)現(xiàn),用于不包含追蹤信息的消息和包含追蹤信息的消息之間的讀寫轉(zhuǎn)換。
HTTP 請求追蹤支持
在Knewton內(nèi)部的一些基礎(chǔ)構(gòu)件中所有對外開放的節(jié)點(diǎn)都是基于HTTP的,我們需要以一種簡便的方式在HTTP請求中插入需要攜帶的追蹤信息。
這個(gè)實(shí)現(xiàn)起來很簡單,因?yàn)镠TTP請求支持在消息頭中放入任意數(shù)據(jù)。根據(jù)rfc2047的第5章節(jié)中的內(nèi)容,唯一的參考是在放置自定義的消息頭需要為他們加入前綴’X-‘。
我們保持Zipkin傳統(tǒng)并且使用一下標(biāo)題傳播信息:
Knewton 的服務(wù)主要使用Jetty HTTP Server 和 Apache HTTP Client。所有依據(jù)這兩種中間件構(gòu)建的項(xiàng)目都能以便捷的方式實(shí)現(xiàn)對HTTP消息頭的操作。
Jetty 服務(wù)請求被路由到一個(gè) Servlet。作為這個(gè)路由的一部分, Jetty 允許請求和回應(yīng)通過一系列過濾。我們覺得處理追蹤數(shù)據(jù),這是最理想的。當(dāng)任何傳入請求附帶跟蹤數(shù)據(jù)頭時(shí),我們構(gòu)造的跨數(shù)據(jù)會提交到 DataManager 。
與 Apache HTTP 客戶端一起,我們使用一個(gè) HttpRequestInterceptor 和 HttpResponseInterceptor,它被設(shè)計(jì)成與頭部內(nèi)容能交互,并能修改他們。這些攔截器使用 DataManager, 并能從頭中讀出追蹤數(shù)據(jù),反之亦然。
Guice
大多數(shù)人不熟悉 Guice ,這是一個(gè)谷歌開發(fā)的依賴關(guān)系管理框架。TDist 集成到我們現(xiàn)有的服務(wù)模塊,那么我們的客戶端將更簡潔、更少出錯,我們依靠的就是 Guice ,并且實(shí)現(xiàn)了多個(gè)模塊可以讓我們的客戶端更易于安裝。Guice 處理依賴于在對象實(shí)例化期間注入,在交換接口也能更簡潔。如果通過這篇文章你已經(jīng)開始思考集成 TDist ,那么聽起來很復(fù)雜。很多時(shí)候,我們的客戶端需要安裝附加的 Guice 模塊,這些模塊將綁定到我們的追蹤實(shí)現(xiàn)上,實(shí)現(xiàn)現(xiàn)有的 Thrift 接口。這也意味著,我們的客戶端不能實(shí)例化任何我們追蹤啟動的構(gòu)造。無論什么時(shí)候,一個(gè) TDist 客戶端忽略綁定一些東西,Guice 都會在編譯時(shí)通知我們的客戶端。我們把很多心思放在如何制定我們的 Guice 模塊層次結(jié)構(gòu)上,因此 TDist 不會與我們的客戶端沖突,我們都很小心,無論什么時(shí)候,我們都必須暴露元素到外部世界。
追蹤消息總線
所有我們的客戶服務(wù)在使用之前都會放置追蹤數(shù)據(jù),追蹤消息總線是通過 Zipkin 收集器來持續(xù)收集的。 我們的兩個(gè)選項(xiàng)是 Kafka 和 Kinesis,不過最終我們選擇了 Kinesis 。我們考慮到 Kafka 是因?yàn)?Knewton 已經(jīng)穩(wěn)定部署 Kafka 很多年。在那時(shí),我們的 Kafka 集群一直使用我們的子事件總線,在生產(chǎn)環(huán)境中每秒產(chǎn)生超過300條消息。我們初步估計(jì)將超過 400,000 條的跟蹤信息,每秒只有部分進(jìn)行集成。生產(chǎn)系統(tǒng)與儀表數(shù)據(jù)使我們緊張。Kinesis 似乎是一個(gè)有吸引力的替代,它將我們從 Kafka 服務(wù)器分離,這只是生產(chǎn)數(shù)據(jù),而不是儀表上的數(shù)據(jù)。在實(shí)現(xiàn)的時(shí)候,Kinesis 是一個(gè)新的 AWS 服務(wù),我們對它很熟悉。它的價(jià)格,吞吐能力,不用太多維護(hù),這些促成了我們達(dá)成一致?偟膩碚f,我們已經(jīng)滿意它的性能和穩(wěn)定性。它沒有 Kafka 那么快,但是 Kafka 數(shù)據(jù)產(chǎn)生的性質(zhì),它從產(chǎn)生到攝入 SLA 甚至要幾分鐘。自從我們部署追蹤消息總線到生產(chǎn)環(huán)境,我們也很能容易擴(kuò)展大量 Kinesis, 且不會引起任何宕機(jī)。
追蹤數(shù)據(jù)的存儲
所有我們追蹤的數(shù)據(jù)都會被放在追蹤數(shù)據(jù)存儲。數(shù)據(jù)放在那里是有一個(gè)配置時(shí)間的,并且由 Zipkin 查詢服務(wù)顯示在 UI 上。Zipkin 提供了大量開箱即用的數(shù)據(jù)存儲,包括 Cassandra, Redis,MongoDB, Postgres 和 MySQL。我們對 Cassandra 和 DynamoDB 做過實(shí)驗(yàn),這主要是因?yàn)槲覀冊?Knewton 中獲得的習(xí)以為常的知識,最終我們還是選擇了亞馬遜的 Elasticache Redis 。下面這些是我們做出這個(gè)決定的最重要原因。
-
花在生產(chǎn)上的時(shí)間,我們還沒鋪開就要交付了,并且,我們還要維護(hù)一個(gè)新的集群
結(jié) 論
現(xiàn)在在 Knewton, 我們的追蹤解決方案在整個(gè)環(huán)境中已經(jīng)運(yùn)行好多個(gè)月了。目前它已經(jīng)被證明是非常有價(jià)值的。我們只有在開始擦除那個(gè)面的時(shí)候,我們才可以追蹤并且收集時(shí)間數(shù)據(jù)。我們有很多有趣的實(shí)現(xiàn),并且在 Knewton 交付,之后,我們就理解了這個(gè)數(shù)據(jù)的價(jià)值。
核心關(guān)注:拓步ERP系統(tǒng)平臺是覆蓋了眾多的業(yè)務(wù)領(lǐng)域、行業(yè)應(yīng)用,蘊(yùn)涵了豐富的ERP管理思想,集成了ERP軟件業(yè)務(wù)管理理念,功能涉及供應(yīng)鏈、成本、制造、CRM、HR等眾多業(yè)務(wù)領(lǐng)域的管理,全面涵蓋了企業(yè)關(guān)注ERP管理系統(tǒng)的核心領(lǐng)域,是眾多中小企業(yè)信息化建設(shè)首選的ERP管理軟件信賴品牌。
轉(zhuǎn)載請注明出處:拓步ERP資訊網(wǎng)http://www.ezxoed.cn/
本文標(biāo)題:分布式追蹤系統(tǒng)架構(gòu)與設(shè)計(jì)
本文網(wǎng)址:http://www.ezxoed.cn/html/support/11121520058.html