????在各大廠分布式鏈路跟蹤系統(tǒng)架構(gòu)對比中已經(jīng)介紹了幾大框架的對比,如果想用免費的可以用zipkin和pinpoint還有一個忘了介紹:SkyWalking伏嗜,具體介紹可參考:https://github.com/apache/incubator-skywalking/blob/master/README_ZH.md
由于追蹤的要求是Net平臺和Java平臺都要支持坛悉,對于java平臺各組件都是天生的支持的,但對于net的支持找了些開源組件承绸,發(fā)現(xiàn)Pinpoint和SkyWalking給出的Demo都是基于NetCore(SkyWalking可以在github上搜skywalking-netcore裸影,Pinpoint沒有好的推薦),版本要求比較高军熏,但不可能更改現(xiàn)有平臺的FW框架轩猩,Zipkin有開源項目 Medidata.zipkinTracerModule 、zipkin.net、zipkin-csharp界轩,網(wǎng)上依次推薦是從前到后画饥,經(jīng)過測試發(fā)現(xiàn)Medidata.zipkinTracerModule、zipkin.net也是用于Net Core的浊猾,在NuGet上安裝報錯抖甘。最后測試zipkin-csharp(https://github.com/openzipkin-attic/zipkin-csharp)可以成功,在NuGet中搜索Zipkin.Core葫慎,現(xiàn)在版本也只有一個衔彻,如下:
然后查看給出的demo中代碼:zipkin-csharp/examples/ZipkinExample/Program.cs
using System;using System.Net;using System.Threading;using Zipkin;using Zipkin.Tracer.Kafka;namespace ZipkinExample
{
? ? class Program
? ? {
? ? ? ? staticvoidMain(string[] args)
? ? ? ? {
? ? ? ? ? ? varrandom =new Random();
? ? ? ? ? ? // make sure Zipkin with Scribe client is working
? ? ? ? ? ? //var collector = new HttpCollector(new Uri("http://localhost:9411/"));varcollector =new KafkaCollector(KafkaSettings.Default);
? ? ? ? ? ? vartraceId =newTraceHeader(traceId: (ulong)random.Next(), spanId: (ulong)random.Next());
? ? ? ? ? ? varspan =newSpan(traceId,newIPEndPoint(IPAddress.Loopback,9000),"test-service");
? ? ? ? ? ? span.Record(Annotations.ClientSend(DateTime.UtcNow));
? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? span.Record(Annotations.ServerReceive(DateTime.UtcNow));
? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? span.Record(Annotations.ServerSend(DateTime.UtcNow));
? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? span.Record(Annotations.ClientReceive(DateTime.UtcNow));
? ? ? ? ? ? collector.CollectAsync(span).Wait();
? ? ? ? }
? ? }
}
可以看出這里的traceId和spanId都是隨機生成的,在這里推薦自己生成ID偷办,注意是ulong型艰额,這里毫秒數(shù)只格式化兩位(數(shù)據(jù)庫的位數(shù)20位,會超)椒涯,也可以用更保險的其它方法柄沮。
////// 獲得隨機數(shù)
? ? ? ? //////privatestaticulong getRandom()
? ? ? ? {
? ? ? ? ? ? varrandom =new Random();
? ? ? ? ? ? returnulong.Parse(DateTime.Now.ToString("yyyyMMddHHmmssff") + random.Next(100,999));
? ? ? ? }
? ? }
collector這里使用Http來接收,注釋kafka的废岂,放開http的祖搓。去掉?collector.CollectAsync(span).Wait(); 中的Wait。
Zipkin的幾個基本概念
Span:基本工作單元湖苞,一次鏈路調(diào)用(可以是RPC拯欧,DB等沒有特定的限制)創(chuàng)建一個span,通過一個64位ID標識它财骨, span通過還有其他的數(shù)據(jù)镐作,例如描述信息,時間戳隆箩,key-value對的(Annotation)tag信息该贾,parent-id等,其中parent-id 可以表示span調(diào)用鏈路來源捌臊,通俗的理解span就是一次請求信息
Trace:類似于樹結(jié)構(gòu)的Span集合杨蛋,表示一條調(diào)用鏈路,存在唯一標識娃属,即TraceId
Annotation:注解,用來記錄請求特定事件相關(guān)信息(例如時間)护姆,通常包含四個注解信息
cs - Client Start矾端,表示客戶端發(fā)起請求
sr - Server Receive,表示服務(wù)端收到請求
ss - Server Send卵皂,表示服務(wù)端完成處理秩铆,并將結(jié)果發(fā)送給客戶端
cr - Client Received,表示客戶端獲取到服務(wù)端返回信息
BinaryAnnotation:提供一些額外信息,一般以key-value對出現(xiàn)
啟動服務(wù)端測試
下載 https://github.com/openzipkin/zipkin/releases 最近的穩(wěn)定版 release-2.7.1的jar包殴玛,這里采用mysql的型式保存記錄捅膘,因此需要創(chuàng)建數(shù)據(jù)庫zipkin,創(chuàng)建表:
SETFOREIGN_KEY_CHECKS=0;-- ------------------------------ Table structure for `zipkin_annotations`-- ----------------------------DROPTABLEIFEXISTS `zipkin_annotations`;CREATETABLE `zipkin_annotations` (
? `trace_id_high` bigint(20)NOTNULLDEFAULT'0'COMMENT'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
? `trace_id` bigint(20)NOTNULLCOMMENT'coincides with zipkin_spans.trace_id',
? `span_id` bigint(20)NOTNULLCOMMENT'coincides with zipkin_spans.id',
? `a_key` varchar(255)NOTNULLCOMMENT'BinaryAnnotation.key or Annotation.value if type == -1',
? `a_value` blob COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
? `a_type` int(11)NOTNULLCOMMENT'BinaryAnnotation.type() or -1 if Annotation',
? `a_timestamp` bigint(20)DEFAULTNULLCOMMENT'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
? `endpoint_ipv4` int(11)DEFAULTNULLCOMMENT'Null when Binary/Annotation.endpoint is null',
? `endpoint_ipv6` binary(16)DEFAULTNULLCOMMENT'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
? `endpoint_port` smallint(6)DEFAULTNULLCOMMENT'Null when Binary/Annotation.endpoint is null',
? `endpoint_service_name` varchar(255)DEFAULTNULLCOMMENT'Null when Binary/Annotation.endpoint is null',
? UNIQUEKEY`trace_id_high` (`trace_id_high`,`trace_id`,`span_id`,`a_key`,`a_timestamp`) COMMENT'Ignore insert on duplicate',
? UNIQUEKEY`trace_id_high_4` (`trace_id_high`,`trace_id`,`span_id`,`a_key`,`a_timestamp`) COMMENT'Ignore insert on duplicate',
? KEY`trace_id_high_2` (`trace_id_high`,`trace_id`,`span_id`) COMMENT'for joining with zipkin_spans',
? KEY`trace_id_high_3` (`trace_id_high`,`trace_id`) COMMENT'for getTraces/ByIds',
? KEY`endpoint_service_name` (`endpoint_service_name`) COMMENT'for getTraces and getServiceNames',
? KEY`a_type` (`a_type`) COMMENT'for getTraces',
? KEY`a_key` (`a_key`) COMMENT'for getTraces',
? KEY`trace_id` (`trace_id`,`span_id`,`a_key`) COMMENT'for dependencies job',
? KEY`trace_id_high_5` (`trace_id_high`,`trace_id`,`span_id`) COMMENT'for joining with zipkin_spans',
? KEY`trace_id_high_6` (`trace_id_high`,`trace_id`) COMMENT'for getTraces/ByIds',
? KEY`endpoint_service_name_2` (`endpoint_service_name`) COMMENT'for getTraces and getServiceNames',
? KEY`a_type_2` (`a_type`) COMMENT'for getTraces',
? KEY`a_key_2` (`a_key`) COMMENT'for getTraces',
? KEY`trace_id_2` (`trace_id`,`span_id`,`a_key`) COMMENT'for dependencies job') ENGINE=InnoDBDEFAULTCHARSET=utf8 ROW_FORMAT=COMPRESSED;-- ------------------------------ Records of zipkin_annotations-- ------------------------------ ------------------------------ Table structure for `zipkin_dependencies`-- ----------------------------DROPTABLEIFEXISTS `zipkin_dependencies`;CREATETABLE `zipkin_dependencies` (
? `day` dateNOTNULL,
? `parent` varchar(255)NOTNULL,
? `child` varchar(255)NOTNULL,
? `call_count` bigint(20)DEFAULTNULL,
? `error_count` bigint(20)DEFAULTNULL,
? UNIQUEKEY`day` (`day`,`parent`,`child`),
? UNIQUEKEY`day_2` (`day`,`parent`,`child`)
) ENGINE=InnoDBDEFAULTCHARSET=utf8 ROW_FORMAT=COMPRESSED;-- ------------------------------ Records of zipkin_dependencies-- ------------------------------ ------------------------------ Table structure for `zipkin_spans`-- ----------------------------DROPTABLEIFEXISTS `zipkin_spans`;CREATETABLE `zipkin_spans` (
? `trace_id_high` bigint(20)NOTNULLDEFAULT'0'COMMENT'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
? `trace_id` bigint(20)NOTNULL,
? `id` bigint(20)NOTNULL,
? `name` varchar(255)NOTNULL,
? `parent_id` bigint(20)DEFAULTNULL,
? `debug` bit(1)DEFAULTNULL,
? `start_ts` bigint(20)DEFAULTNULLCOMMENT'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
? `duration` bigint(20)DEFAULTNULLCOMMENT'Span.duration(): micros used for minDuration and maxDuration query',
? UNIQUEKEY`trace_id_high` (`trace_id_high`,`trace_id`,`id`) COMMENT'ignore insert on duplicate',
? UNIQUEKEY`trace_id_high_4` (`trace_id_high`,`trace_id`,`id`) COMMENT'ignore insert on duplicate',
? KEY`trace_id_high_2` (`trace_id_high`,`trace_id`,`id`) COMMENT'for joining with zipkin_annotations',
? KEY`trace_id_high_3` (`trace_id_high`,`trace_id`) COMMENT'for getTracesByIds',
? KEY`name` (`name`) COMMENT'for getTraces and getSpanNames',
? KEY`start_ts` (`start_ts`) COMMENT'for getTraces ordering and range',
? KEY`trace_id_high_5` (`trace_id_high`,`trace_id`,`id`) COMMENT'for joining with zipkin_annotations',
? KEY`trace_id_high_6` (`trace_id_high`,`trace_id`) COMMENT'for getTracesByIds',
? KEY`name_2` (`name`) COMMENT'for getTraces and getSpanNames',
? KEY`start_ts_2` (`start_ts`) COMMENT'for getTraces ordering and range') ENGINE=InnoDBDEFAULTCHARSET=utf8 ROW_FORMAT=COMPRESSED;-- ------------------------------ Records of zipkin_spans-- ----------------------------
啟動
進入程序的當前目錄啟動滚粟,注意參數(shù)內(nèi)容寻仗,如果想要保存到elasticsearch,需要按官方文檔更改凡壤。
java -jar zipkin-server-2.7.1.jar --STORAGE_TYPE=mysql --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=123456--MYSQL_HOST=localhost --MYSQL_TCP_PORT=3306
啟動后看到如下內(nèi)容表明成功署尤。
啟動成功后瀏覽器訪問?http://localhost:9411/
至此服務(wù)端和展示頁面已經(jīng)啟動,不過功能還是很簡單的亚侠,具體的使用可另行查詢資料曹体。
? 這里用來測試的服務(wù)采用網(wǎng)友提供的 源碼:mircoservice分布式跟蹤系統(tǒng)(zipkin+springboot)?https://github.com/dreamerkr/mircoservice,文章可參考:微服務(wù)之分布式跟蹤系統(tǒng)(springboot+zipkin)https://blog.csdn.net/qq_21387171/article/details/53787019
用默認配置分別運行4個客戶端服務(wù)后運行效果:
(1)分別啟動每個服務(wù)硝烂,然后訪問服務(wù)1箕别,瀏覽器訪問(http://localhost:8081/service1/test)
(2)輸入zipkin地址,每次trace的列表
?點擊其中的trace滞谢,可以看trace的樹形結(jié)構(gòu)串稀,包括每個服務(wù)所消耗的時間:
?點擊每個span可以獲取延遲信息:
?同時可以查看服務(wù)之間的依賴關(guān)系:
測試Net平臺程序
將demo代碼改為:
staticvoidMain(string[] args)
? ? ? ? {
? ? ? ? ? ? varrandom =new Random();
? ? ? ? ? ? // make sure Zipkin with Scribe client is workingvarcollector =newHttpCollector(newUri("http://localhost:9411/"));
? ? ? ? ? ? //var collector = new KafkaCollector(KafkaSettings.Default);vartraceId =newTraceHeader(traceId: (ulong)random.Next(), spanId: (ulong)random.Next());
? ? ? ? ? ? varspan =newSpan(traceId,newIPEndPoint(IPAddress.Loopback,9000),"zipkinweb");
? ? ? ? ? ? span.Record(Annotations.ClientSend(DateTime.UtcNow));
? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? span.Record(Annotations.ServerReceive(DateTime.UtcNow));
? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? span.Record(Annotations.ServerSend(DateTime.UtcNow));
? ? ? ? ? ? Thread.Sleep(100);
? ? ? ? ? ? span.Record(Annotations.ClientReceive(DateTime.UtcNow));
? ? ? ? ? ? collector.CollectAsync(span);
? ? ? ? }
然后運行一次再查看,會多出一條信息
點進去會看到請求的詳細信息和備注信息:
右上角查看json
驗證了NET平臺下是可以成功調(diào)用的爹凹,而且可以看到zipkin服務(wù)前端展示是通過api請求的厨诸,前后臺分開的,因此我們可以以此來做二次開發(fā)禾酱,我們知道了數(shù)據(jù)結(jié)構(gòu)或者通過自己請求數(shù)據(jù)庫內(nèi)容做更復(fù)雜的業(yè)務(wù)前端微酬。
這里強調(diào)一點的是net最好用framework4.5以上的版本,由net的demo來看其實封裝性不高颤陶,所以靈活性能很高颗管,需要自己進一步封裝才能達到代碼的侵入性更少,性能更高滓走。后面考慮到性能和數(shù)據(jù)量可改用kafka接收和ES保存數(shù)據(jù)垦江。