前言

随着应用愈发复杂,请求的链路也愈发复杂,微服务化下,更是使得不同的服务分布在不同的机器,地域,语言也不尽相同。因此需要借助工具帮助分析,跟踪,定位请求中出现的若干问题,以此来保障服务治理,链路追踪也就出现了。

OpenTracing协议

OpenTracing是一套分布式追踪协议,与平台,语言、厂商无关的Trace协议,统一接口,使得开发人员能够方便的添加或更换更换不同的分布式追踪系统。

  • 语义规范 : 描述定义的数据模型 Tracer,Sapn 和 SpanContext 等;
  • 语义惯例 : 罗列出 tag 和 logging 操作时,标准的key值;

同样作为分布式追踪协议的还有OpenCensus,以及两者的合并体OpenTelemetry。

Jaeger介绍

Jaeger[ˈdʒɛgər]是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API,已在Uber大规模使用,且已加入CNCF开源组织(Cloud Native Computing Foundation-云原生计算基金会)。其主要功能是聚合来自各个异构系统的实时监控数据。

Jager提供了一套完整的追踪系统包括Jaeger-client、Jaeger-agent、Jaeger-collector、Database和Jaeger-query UI等基本组件。

  1. Jaeger-client:为不同开发语言实现了符合OpenTracing协议的客户端。
  2. Jaeger-agent:一个监听在UDP端口上接收链路数据的网络守护进程,它从应用程序收集,批处理,并发送给Collector,(也可以没有这个,client直接上报)。
  3. Jaeger-collector:负责接收Jaeger-client或Jaeger-agent上报的调用链路数据,并通过处理管道运行它们,该管道验证跟踪、对它们进行索引、执行任何转换并最终保存到内存或外部存储系统中,供UI展示。
  4. Jaeger-query:查询服务从存储中检索跟踪并呈现 UI 来显示它们。

Jaeger安装

在个人使用或者测试上,Jaeger提供了jaegertracing/all-in-one镜像,搭建过程十分简单,数据存储在内存中,但需要注意容器挂了后数据就没了。

docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest

创建容器运行后,可以访问ip:16686查看Jaeger的仪表面板

Jaeger应用

服务设计

简化大部分服务设计,整个结构上差不多是如下所示,服务层常见金字塔结构,服务上下游明确,以避免服务间的循环依赖。

此处建立四个服务以及一个BFF网关层,以满足服务同步调用,服务间上下游调用,以及服务间事件通信。

  • JaegerDemo.BFF.Host
  • JaegerDemo.AService.Host
  • JaegerDemo.BService.Host
  • JaegerDemo.CService.Host
  • JaegerDemo.DService.Host

为这几个服务设定期望如下

  • 执行Get请求时,从Gateway调用,请求A服务,在同步请求B和C服务,拿到结果组装后对外返回。
  • 执行Post请求时,从Gateway调用,请求A服务,在发布事件到MQ中,D服务订阅事件,数据写入到Sqlite中。

Nuget包引用

  • Jaeger,用来上传数据到Jaeger。
  • OpenTracing.Contrib.NetCore,基于OpenTracing.Net的增强,用来采集应用数据。
  • MassTransit和MassTransit.RabbitMQ,用来完成事件的发布订阅。
<ItemGroup>
  <PackageReference Include="OpenTracing" Version="0.12.1" />
  <PackageReference Include="Jaeger" Version="1.0.3" />
  <PackageReference Include="MassTransit" Version="8.0.8" />
  <PackageReference Include="MassTransit.RabbitMQ" Version="8.0.8" />
</ItemGroup>

服务注册

将服务注册到容器中,设置上报地址,注意此处上报地址是UDP类型,因此在云服务器中开安全组时需要是UDP类型

builder.Services.AddOpenTracing();
builder.Services.AddSingleton<ITracer>(serviceProvider =>
{
    var serviceName = serviceProvider.GetRequiredService<IWebHostEnvironment>().ApplicationName;     var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
    var sampler = new ConstSampler(sample: true);
    var reporter = new RemoteReporter.Builder()
            .WithLoggerFactory(loggerFactory)
            .WithSender(new UdpSender("xxx.xxx.xxx.xxx", 6831, 0))
            .Build();     var tracer = new Tracer.Builder(serviceName)
        .WithLoggerFactory(loggerFactory)
        .WithSampler(sampler)
        .WithReporter(reporter)
        .Build();     GlobalTracer.Register(tracer);     return tracer;
});

此处我在云服务器中开放6831的端口,注意是UDP

Http请求

在BFF处发起Http调用A服务,以及A服务发起Http调用B和C。

[HttpGet]
public async Task<string> GetAsync()
{
    using var httpClient = _httpClientFactory.CreateClient();
    httpClient.BaseAddress = new Uri("https://localhost:7001");     var aServiceResult = await httpClient.GetStringAsync("/AValue");
    return aServiceResult;
}

请求发送完毕,从Jaeger的仪表面板查看监控数据,能够看到一个请求的发起时间,所经过的服务数量、所调用服务的依赖关系、消耗的时长等信息。整个请求链路也就看到了,B和C的同步请求,A和B,A和C的上下游请求也明了。

Jaeger提供了有向图描述请求链路,来方便理清节点间的通信边界,整个请求链路也便清晰了。

事件驱动

在BFF处发起Http调用A服务,以及A服务往RabbitMQ发送集成事件。

[HttpPost]
public async Task<IActionResult> CreateAsync(string value)
{
    var actionName = ControllerContext.ActionDescriptor.DisplayName;
    using var scope = _tracer.BuildSpan(actionName).StartActive(finishSpanOnDispose: true);
    var span = scope.Span.SetTag(Tags.SpanKind, Tags.SpanKindClient);
    var dictionary = new Dictionary<string, string>();
    _tracer.Inject(span.Context, BuiltinFormats.TextMap, new TextMapInjectAdapter(dictionary));     // Do something
    // ...     // Send integration event
    await _publishEndpoint.Publish(new ValueCreatedIntegrationEvent()
    {
        Value = value,
        TrackingKeys = dictionary
    });     return Ok();
}

D服务中消费集成事件,并写入Sqlite库中

public async Task Consume(ConsumeContext<ValueCreatedIntegrationEvent> context)
{
    using var scope = TracingExtension.StartServerSpan(_tracer, context.Message.TrackingKeys, "Value created integration event handler");     var value = context.Message.Value;
    Console.WriteLine($"Value:{value}");
    await _dbContext.ValueAggregates.AddAsync(new ValueAggregate(value));
    await _dbContext.SaveChangesAsync();
}

当请求发送完毕,事件消费完毕后,可以在Jaeger上看到在事件驱动下的链路调用过程,以及在调用过程中增加的tags和logs,写入Sqlite的Sql。

在原有链路结构上,便又多了一个D服务。

2022-11-28,望技术有成后能回来看见自己的脚步

Asp.Net Core&Jaeger实现链路追踪的更多相关文章

  1. ASP.NET Core整合Zipkin链路跟踪

    前言     在日常使用ASP.NET Core的开发或学习中,如果有需要使用链路跟踪系统,大多数情况下会优先选择SkyAPM.我们之前也说过SkyAPM设计确实比较优秀,巧妙的利用Diagnosti ...

  2. Jaeger Client Go 链路追踪|入门详解

    目录 从何说起 Jaeger 部署 Jaeger 从示例了解 Jaeger Client Go 了解 trace.span tracer 配置 Sampler 配置 Reporter 配置 分布式系统 ...

  3. .NET Core----zipkin链路追踪使用

    本文主要是说明core怎么使用链路追踪 一.添加nuget包 二.在Startup中添加配置 /// <summary> /// 注册zipkinTrace /// </summar ...

  4. ASP.NET Core使用HostingStartup增强启动操作

    概念 在ASP.NET Core中我们可以使用一种机制来增强启动时的操作,它就是HostingStartup.如何叫"增强"操作,相信了解过AOP概念的同学应该都非常的熟悉.我们常 ...

  5. Go微服务框架go-kratos实战05:分布式链路追踪 OpenTelemetry 使用

    一.分布式链路追踪发展简介 1.1 分布式链路追踪介绍 关于分布式链路追踪的介绍,可以查看我前面的文章 微服务架构学习与思考(09):分布式链路追踪系统-dapper论文学习(https://www. ...

  6. ASP.NET Core使用Jaeger实现分布式追踪

    前言 最近我们公司的部分.NET Core的项目接入了Jaeger,也算是稍微完善了一下.NET团队的技术栈. 至于为什么选择Jaeger而不是Skywalking,这个问题我只能回答,大佬们说了算. ...

  7. .NET Core 中的日志与分布式链路追踪

    目录 .NET Core 中的日志与分布式链路追踪 .NET Core 中的日志 控制台输出 非侵入式日志 Microsoft.Extensions.Logging ILoggerFactory IL ...

  8. [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(五)

    概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - 捕获异常,这篇文章咱们分享:路由中间件 - Jaeger 链路追踪. 啥是链路追踪? 我理解链路追踪其实是为微服务架构提供服务的,当一个请求 ...

  9. [系列] go-gin-api 路由中间件 - Jaeger 链路追踪(六)

    [DOC] 概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(理论篇),这篇文章咱们接着分享:路由中间件 - Jaeger 链路追踪(实战篇). 这篇文章,确实让大家 ...

  10. go-gin-api 路由中间件 - Jaeger 链路追踪

    概述 首先同步下项目概况: 上篇文章分享了,路由中间件 - Jaeger 链路追踪(理论篇). 这篇文章咱们分享:路由中间件 - Jaeger 链路追踪(实战篇). 说实话,这篇文章确实让大家久等了, ...

随机推荐

  1. 《Java基础——方法的调用》

    Java基础--方法的调用     总结: 1. 在同一个类中-- 对于静态方法,其它的静态和非静态方法都可以直接通过"方法名"或者"类名.方法名"调用它. 对 ...

  2. dotnet7 aot编译实战

    0 起因 这段日子看到dotnet7-rc1发布,我对NativeAot功能比较感兴趣,如果aot成功,这意味了我们的dotnet程序在防破解的上直接指数级提高.我随手使用asp.netcore-7. ...

  3. Typora如何配置gitee图床

    转载自:https://mp.weixin.qq.com/s/5dPLbr2vFgL18XKL1Y05Og 要求: 1.Typora需要升级到最新版 2.需要安装nodejs PicGo软件下载地址: ...

  4. 第五章:Admin管理后台 - 2:自定义Admin actions

    通常情况下,admin的工作模式是"选中目标,然后修改目标",但在同时修改大量目标的时候,这种模式就变得重复.繁琐. 为此,admin提供了自定义功能函数actions的手段,可以 ...

  5. 使用docker-compose运行nginx容器挂载时遇到的文件/目录问题

    单独使用docker run命令指定挂载文件路径运行nginx容器是可以的,但是用在docker-compose中就不行了 报错如下: 原因就是挂载出错,不能直接挂载文件,还有挂载的容器里的目录要正确 ...

  6. Jquery封装的ajax的使用过程发生的问题

    Jquery封装的ajax的使用过程发生的问题 今天在做项目的时候使用到了ajax来完成项目前后端数据交互,在之后发现在前端没有数据显示,而后端数据确实存在,在多次检查代码之后,发现代码并不存在问题, ...

  7. 媒介查询兼容各种端口的响应式范围取值(移动端、PC端、ipad、移动端侧屏)

    !!!(chrome作者亲测)!!!数据仅供参考 /*ipad*/@media screen and (min-width:760px) and (max-width:1000px) /*移动端*/@ ...

  8. SQL语句——为什么分组函数(多行处理函数)不能用在where语句之后?

    在SQL语句中,常见的分组查询函数为: sum()求和 avg()求平均值 min()求最小值 max()求最大值 count()求数目 在分组函数中有几个重要的特征: 1.分组函数进行时自动忽略nu ...

  9. PHP全栈开发(三):CentOS 7 中 PHP 环境搭建及检测

    简单回顾一下我们在(一).(二)中所做的工作. 首先我们在(一)中设置了CentOS 7的网络. 其实这些工作在CentOS 6中都是很容易的,因为有鸟哥的Linux私房菜这样好的指导. 但是这些操作 ...

  10. AVX图像算法优化系列一: 初步接触AVX。

    弄了SSE指令集,必然会在不同的场合不同的人群中了解到还有更为高级的AVX指令集的存在,早些年也确实有偶尔写点AVX的函数,但是一直没有深入的去了解,今年十一期间也没到那里去玩,一个人在家里抽空就折腾 ...