zoukankan      html  css  js  c++  java
  • ASP.NET Core使用Jaeger实现分布式追踪

    前言

    最近我们公司的部分.NET Core的项目接入了Jaeger,也算是稍微完善了一下.NET团队的技术栈。

    至于为什么选择Jaeger而不是Skywalking,这个问题我只能回答,大佬们说了算。

    前段时间也在CSharpCorner写过一篇类似的介绍
    Exploring Distributed Tracing Using ASP.NET Core And Jaeger

    下面回到正题,我们先看一下Jaeger的简介

    Jaeger的简单介绍

    Jaeger是Uber开源的一个分布式追踪的工具,主要为基于微服务的分布式系统提供监测和故障诊断。包含了下面的内容

    • Distributed context propagation
    • Distributed transaction monitoring
    • Root cause analysis
    • Service dependency analysis
    • Performance / latency optimization

    下面就通过一个简单的例子来体验一下。

    示例

    在这个示例的话,我们只用了jaegertracing/all-in-one这个docker的镜像来搭建,因为是本地的开发测试环境,不需要搭建额外的存储,这个感觉还是比较贴心的。

    我们会用到两个主要的nuget包

    1. Jaeger 这个是官方的client
    2. OpenTracing.Contrib.NetCore.Unofficial 这个是对.NET Core探针的处理,从opentracing-contrib/csharp-netcore这个项目移植过来的(这个项目并不活跃,只能自己做扩展)

    然后我们会建两个API的项目,一个是AService,一个是BService

    其中BService会提供一个接口,从缓存中读数据,如果读不到就通过EF Core去从sqlite中读,然后写入缓存,最后再返回结果。

    AService 会通过HttpClient去调用BService的接口,从而会形成调用链。

    开始之前,我们先把docker-compose.yml配置一下

    version: '3.4'
    
    services:
      aservice:
        image: ${DOCKER_REGISTRY-}aservice
        build:
          context: .
          dockerfile: AService/Dockerfile
        ports:
          - "9898:80"  
        depends_on:
          - jagerservice
          - bservice
        networks:  
          backend:
          
      bservice:
        image: ${DOCKER_REGISTRY-}bservice
        build:
          context: .
          dockerfile: BService/Dockerfile
        ports:
          - "9899:80"
        depends_on:
          - jagerservice    
        networks:  
          backend:
          
      jagerservice:
        image: jaegertracing/all-in-one:latest
        environment:
          - COLLECTOR_ZIPKIN_HTTP_PORT=9411 
        ports:
          - "5775:5775/udp"
          - "6831:6831/udp"
          - "6832:6832/udp"
          - "5778:5778"
          - "16686:16686"
          - "14268:14268"
          - "9411:9411"
        networks:  
          backend:
          
    networks:  
      backend:      
        driver: bridge
    

    然后就在两个项目的Startup加入下面的一些配置,主要是和Jaeger相关的。

    public void ConfigureServices(IServiceCollection services)
    {
        // others ....
        
        // Adds opentracing
        services.AddOpenTracing();
    
        // Adds the Jaeger Tracer.
        services.AddSingleton<ITracer>(serviceProvider =>
        {
            string serviceName = serviceProvider.GetRequiredService<IHostingEnvironment>().ApplicationName;
            
            var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
            var sampler = new ConstSampler(sample: true);
            var reporter = new RemoteReporter.Builder()
                    .WithLoggerFactory(loggerFactory)
                    .WithSender(new UdpSender("jagerservice", 6831, 0))
                    .Build();
    
            var tracer = new Tracer.Builder(serviceName)
                .WithLoggerFactory(loggerFactory)
                .WithSampler(sampler)
                .WithReporter(reporter)
                .Build();
    
            GlobalTracer.Register(tracer);
    
            return tracer;
        });
    }
    

    这里需要注意的是我们要根据情况来选择sampler,演示这里用了最简单的ConstSampler。

    回到BService这个项目,我们添加SQLite和EasyCaching的相关支持。

    public void ConfigureServices(IServiceCollection services)
    {
        // Adds an InMemory-Sqlite DB to show EFCore traces.
        services
            .AddEntityFrameworkSqlite()
            .AddDbContext<BDbContext>(options =>
            {
                var connectionStringBuilder = new SqliteConnectionStringBuilder
                {
                    DataSource = ":memory:",
                    Mode = SqliteOpenMode.Memory,
                    Cache = SqliteCacheMode.Shared
                };
                var connection = new SqliteConnection(connectionStringBuilder.ConnectionString);
    
                connection.Open();
                connection.EnableExtensions(true);
    
                options.UseSqlite(connection);
            });
    
        // Add EasyCaching Inmemory provider.
        services.AddEasyCaching(options =>
        {
            options.UseInMemory("m1");
        });
    }
    

    然后控制器上面就比较简单了。

    // GET api/values
    [HttpGet]
    public async Task<IActionResult> GetAsync()
    {
        var provider = _providerFactory.GetCachingProvider("m1");
    
        var obj = await provider.GetAsync("mykey", async () => await _dbContext.DemoObjs.ToListAsync(), TimeSpan.FromSeconds(30));
    
        return Ok(obj);
    }
    

    AService就是通过HttpClient去调用上面的这个接口即可。

    // GET api/values
    [HttpGet]
    public async Task<string> GetAsync()
    {
        var res = await GetDemoAsync();
        return res;
    }
            
    private async Task<string> GetDemoAsync()
    {
        var client = _clientFactory.CreateClient();
    
        var request = new HttpRequestMessage
        {
            Method = HttpMethod.Get,
            RequestUri = new Uri($"http://bservice/api/values")
        };
    
        var response = await client.SendAsync(request);
    
        response.EnsureSuccessStatusCode();
    
        var body = await response.Content.ReadAsStringAsync();
    
        return body;
    }
    

    到这里的话,代码这块是ok了,下面就来看看效果。

    先通过http://localhost:9898/api/values/访问几次AService

    大概能得到一个这样的结果

    然后去Jaeger的界面上我们可以看到,两个服务已经注册上来了。

    选A,B其中一个去搜索,就可以看到下面的结果

    这个就最外层,能看到这些请求一些宏观的信息。

    我们选界面上最后一个,也就是第一个请求,进去看看细节

    从上面这个图大概也能看出来,做了一些什么操作,请求来到AService,它就发起了HTTP请求到BServiceBService则是先通过EasyCaching去取缓存,显然缓存中没数据,它就去读数据库了。

    和另外的请求对比一下,可以发现是少了查数据库这一步操作的。这也是为什么上面的是10个span,而下面的才8个。

    再来看看两个请求的对比图。

    上图中那些红色和绿色的块就是两个请求的差异点了。

    回去看看其他细节,可以发现类似下面的内容

    有很多日志相关的东西,这些东西在这里可能没有太多实际的作用,我们可以通过调整日志的级别来不让它写入到Jaeger中。

    或者是通过下面的方法来过滤

    services.AddOpenTracing(new System.Collections.Generic.Dictionary<string,LogLevel>
    {
        {"AService", LogLevel.Information}
    });
    

    最后就是依赖图了。

    写在最后

    虽说Jaeger用起来挺简单的,但是也是有点美中不足的,不过这个锅不应该是Jaeger来背的,主要还是很多我们常用的库没有直接的支持Diagnostic,所以能监控到的东西还是略少。

    不过在github发现了ClrProfiler.Trace这个项目,可以通过clrprofiler来解决上面的问题。

    最后是本文的示例代码

    JaegerDemo

  • 相关阅读:
    纳米丸穿透癌细胞(癌症)
    Linux生成动态库系统
    Unity3d在线游戏Socket通讯
    TCP/UDP差异
    Android 录音
    使用八种牛云存储解决方案ios7.1的app部署问题
    Android Studio如何设置自己主动提示代码
    QML Image得到的图片资源路径的详细信息
    大约php,mysql,html数字寻呼和文本分页2分页样式供大家参考
    基础总结篇之中的一个:Activity生命周期
  • 原文地址:https://www.cnblogs.com/catcher1994/p/10662999.html
Copyright © 2011-2022 走看看