Net和Java基于zipkin的全链路追踪

    在各大厂分布式链路跟踪系统架构对比中已经介绍了几大框架的对比,如果想用免费的可以用zipkin和pinpoint还有一个忘了介绍:SkyWalking,具体介绍可参考:https://github.com/apache/incubator-skywalking/blob/master/README_ZH.md

  由于追踪的要求是Net平台和Java平台都要支持,对于java平台各组件都是天生的支持的,但对于net的支持找了些开源组件,发现Pinpoint和SkyWalking给出的Demo都是基于NetCore(SkyWalking可以在github上搜skywalking-netcore,Pinpoint没有好的推荐),版本要求比较高,但不可能更改现有平台的FW框架,Zipkin有开源项目 Medidata.zipkinTracerModule 、zipkin.net、zipkin-csharp,网上依次推荐是从前到后,经过测试发现Medidata.zipkinTracerModule、zipkin.net也是用于Net Core的,在NuGet上安装报错。最后测试zipkin-csharp(https://github.com/openzipkin-attic/zipkin-csharp)可以成功,在NuGet中搜索Zipkin.Core,现在版本也只有一个,如下:

然后查看给出的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型,这里毫秒数只格式化两位(数据库的位数20位,会超),也可以用更保险的其它方法。

////// 获得随机数

        //////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:基本工作单元,一次链路调用(可以是RPC,DB等没有特定的限制)创建一个span,通过一个64位ID标识它, span通过还有其他的数据,例如描述信息,时间戳,key-value对的(Annotation)tag信息,parent-id等,其中parent-id 可以表示span调用链路来源,通俗的理解span就是一次请求信息

Trace:类似于树结构的Span集合,表示一条调用链路,存在唯一标识,即TraceId

Annotation:注解,用来记录请求特定事件相关信息(例如时间),通常包含四个注解信息

cs - Client Start,表示客户端发起请求

sr - Server Receive,表示服务端收到请求

ss - Server Send,表示服务端完成处理,并将结果发送给客户端

cr - Client Received,表示客户端获取到服务端返回信息

BinaryAnnotation:提供一些额外信息,一般以key-value对出现


启动服务端测试

下载 https://github.com/openzipkin/zipkin/releases 最近的稳定版 release-2.7.1的jar包,这里采用mysql的型式保存记录,因此需要创建数据库zipkin,创建表:

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-- ----------------------------

启动

进入程序的当前目录启动,注意参数内容,如果想要保存到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

启动后看到如下内容表明成功。

启动成功后浏览器访问 http://localhost:9411/

  至此服务端和展示页面已经启动,不过功能还是很简单的,具体的使用可另行查询资料。

   这里用来测试的服务采用网友提供的 源码:mircoservice分布式跟踪系统(zipkin+springboot) https://github.com/dreamerkr/mircoservice,文章可参考:微服务之分布式跟踪系统(springboot+zipkin)https://blog.csdn.net/qq_21387171/article/details/53787019

用默认配置分别运行4个客户端服务后运行效果:

(1)分别启动每个服务,然后访问服务1,浏览器访问(http://localhost:8081/service1/test

(2)输入zipkin地址,每次trace的列表

 点击其中的trace,可以看trace的树形结构,包括每个服务所消耗的时间:

 点击每个span可以获取延迟信息:

 同时可以查看服务之间的依赖关系:

测试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平台下是可以成功调用的,而且可以看到zipkin服务前端展示是通过api请求的,前后台分开的,因此我们可以以此来做二次开发,我们知道了数据结构或者通过自己请求数据库内容做更复杂的业务前端。

  这里强调一点的是net最好用framework4.5以上的版本,由net的demo来看其实封装性不高,所以灵活性能很高,需要自己进一步封装才能达到代码的侵入性更少,性能更高。后面考虑到性能和数据量可改用kafka接收和ES保存数据。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容