本文主要讲解使用ZipKin构建NetCore分布式链路跟踪


场景

因为最近公司业务量增加,而项目也需要增大部署数量,K8S中Pod基本都扩容了一倍,新增了若干物理机,部分物理机网络通信存在问题,导致部分请求打入有问题的物理机时总会出现超时的情况,由于之前系统中没有使用链路跟踪,导致排查问题比较慢,所以就去研究了市面上的链路框架,结果发现了ZipKin这款比较轻量级的链路跟踪框架。


实例代码

本文日志系统采用Exceplesstion

示例代码请求链路为SimpleZipkin(网关服务)--->WebApi(Api服务)--->OrderApi(订单服务)

首先创建公用类库,引用以下包(本文以1.5.0版本为例)

如果部署Zipkin使用的是Mysql作为存储,切记Mysql版本不要高于8.0,Zipkin暂不支持8.0的版本

zipkin4net
zipkin4net.middleware.aspnetcore

创建ZipKin帮助类

 public static class ZipKinExtensions
{
public static IServiceCollection AddZipKin(this IServiceCollection services)
{
return services.AddSingleton<HttpDiagnosticSourceObserver >();
} public static IApplicationBuilder UseZipKin(this IApplicationBuilder app, IHostApplicationLifetime lifetime, ILoggerFactory loggerFactory, string serviceName, string zipKinUrl)
{
DiagnosticListener.AllListeners.Subscribe(app?.ApplicationServices?.GetService<TraceObserver>());
lifetime.ApplicationStarted.Register(() =>
{
TraceManager.SamplingRate = 1.0f;//记录数据密度,1.0代表全部记录
var logger = new TracingLogger(loggerFactory, "zipkin4net");
var httpSender = new HttpZipkinSender(zipKinUrl, "application/json");
var tracer = new ZipkinTracer(httpSender, new JSONSpanSerializer(), new Statistics());
var consoleTracer = new zipkin4net.Tracers.ConsoleTracer();
TraceManager.RegisterTracer(tracer);
TraceManager.RegisterTracer(consoleTracer);
TraceManager.Start(logger); });
lifetime.ApplicationStopped.Register(() => TraceManager.Stop());
app.UseTracing(serviceName);//这边的名字可自定义
return app;
}
}

Exceptionless帮助类

/// <summary>
/// 日志扩展类
/// </summary>
public static class LogHelper
{
/// <summary>
/// 记录Info日志
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public static void InformationToException(this ILogger logger, string message)
{
var tranceId = Trace.Current?.CurrentSpan.TraceId.ToString("x16");
logger.LogInformation($@"tranceId={tranceId},日志主体为:{message}");
} /// <summary>
/// 记录Debug日志
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public static void DebugToException(this ILogger logger, string message)
{
var tranceId = Trace.Current?.CurrentSpan.TraceId.ToString("x16");
logger.LogDebug($@"tranceId={tranceId},日志主体为:{message}");
} /// <summary>
/// 记录错误日志
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public static void ErrorToException(this ILogger logger, string message)
{
var tranceId = Trace.Current?.CurrentSpan.TraceId.ToString("x16");
logger.LogError($@"tranceId={tranceId},日志主体为:{message}");
} /// <summary>
/// 记录追踪日志
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public static void TraceToException(this ILogger logger, string message)
{
var tranceId = Trace.Current?.CurrentSpan.TraceId.ToString("x16");
logger.LogTrace($@"tranceId={tranceId},日志主体为:{message}");
} /// <summary>
/// 记录警告日志
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public static void WarningToException(this ILogger logger, string message)
{
var tranceId = Trace.Current?.CurrentSpan.TraceId.ToString("x16");
logger.LogWarning($@"tranceId={tranceId},日志主体为:{message}");
}
}

接下来创建SimpleZipkin、WebApi、OrderApi等项目(因为结构一致,所以本文只创建一个),首先引用Exceplesstion

 public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
} public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureLogging((hostContext, configLogging) =>
{
configLogging.AddConfiguration(hostContext.Configuration.GetSection("Logging"));
configLogging.AddConsole();
configLogging.AddDebug();
configLogging.AddExceptionless();
ExceptionlessClient.Default.Configuration.SetDefaultMinLogLevel(Exceptionless.Logging.LogLevel.Debug);
configLogging.SetMinimumLevel(LogLevel.Debug);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}

接下来在Startup中引入ZipKin

 public void ConfigureServices(IServiceCollection services)
{
// 注入Rpc
//AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
//services.AddGrpcClient<HelloServer.HelloServerClient>((p, o) =>
//{
// o.Address = new Uri("http://127.0.0.1:3848");
//});
//.AddHttpMessageHandler(provider => TracingHandler.WithoutInnerHandler("RpcService"));
services.AddControllers();
services.AddZipKin();
services.AddSingleton<IDiagnosticSource, HttpDiagnosticSourceDemo>();
services.AddHttpClient("webApi", client => { client.BaseAddress = new Uri($"http://localhost:5001"); });
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "SimpleZipKin", Version = "v1" });
});
} public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory,
IHostApplicationLifetime lifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "SimpleZipkin v1"));
}
Configuration.GetSection("ExceptionLess").Bind(ExceptionlessClient.Default.Configuration);
ExceptionlessClient.Default.Configuration.SetDefaultMinLogLevel(Exceptionless.Logging.LogLevel.Debug);
app.UseZipKin(lifetime, loggerFactory, "SimpleZip", "http://127.0.0.1:9411");//SimpleZip修改为对应的应用名称,127.0.0.1地址切换为自己的zipkin地址
app.UseRouting(); app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}

接下来创建对应的Controller

   [Route("/api/Home")]
public class HomeController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger; /// <summary>
/// 构造函数
/// </summary>
/// <param name="httpClientFactory"></param>
/// <param name="logger"></param>
public HomeController(IHttpClientFactory httpClientFactory, ILogger<HomeController> logger)
{
_httpClientFactory = httpClientFactory;
//_helloServerClient = helloServerClient;
_logger = logger;
} [HttpGet("GetZipKin")]
public async Task<string> GetZipKin()
{
_logger.InformationToException($@"这里是SimpleZipKinApi");
var httpClient = _httpClientFactory.CreateClient("webApi");
var httpResult = await httpClient.GetAsync($"api/order/getorder");
var result = await httpResult.Content.ReadAsStringAsync();
return result;
}
}

最后在appsettings.json中加入对应的Exceplesstionless配置

  "ExceptionLess": {
"ApiKey": "****************************",
"ServerUrl": "http://127.0.0.1:5000"
}

OrderApi、WebApi如法炮制,修改对应的请求链路信息

接下来我们使用DiagnosticAdapter做链路记载,在公共类库中创建HttpDiagnosticListener类

DiagnosticSource是Runtime层提供,应用层可以通过它与系统集成、事件日志、以及性能计数器进行交互。

DiagnosticSource官方介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.diagnosticsource?view=net-5.0

关于DiagnosticSource设计参考原有yi念之间大佬的文章:https://www.cnblogs.com/wucy/p/13532534.html

public class HttpDiagnosticSourceDemo : IDiagnosticSourceDemo
{
public string DiagnosticName => "HttpDiagnosticSourceDemo"; private ClientTrace _clientTrace;
private readonly IInjector<HttpHeaders> _injector = Propagations.B3String.Injector<HttpHeaders>((carrier, key, value) => carrier.Add(key, value)); [DiagnosticName("System.Net.Http.Request")]
public void HttpRequest(HttpRequestMessage request)
{
_clientTrace = new ClientTrace("simpleZipKin", request.Method.Method);
if (_clientTrace.Trace != null)
{
_injector.Inject(_clientTrace.Trace.CurrentSpan, request.Headers);
}
} [DiagnosticName("System.Net.Http.Response")]
public void HttpResponse(HttpResponseMessage response)
{
if (_clientTrace.Trace != null)
{
_clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_PATH, response.RequestMessage.RequestUri.LocalPath));
_clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_METHOD, response.RequestMessage.Method.Method));
_clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_HOST, response.RequestMessage.RequestUri.Host));
if (!response.IsSuccessStatusCode)
{
_clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_STATUS_CODE, ((int)response.StatusCode).ToString()));
}
}
} [DiagnosticName("System.Net.Http.Exception")]
public void HttpException(HttpRequestMessage request, Exception exception)
{
}
}

IDiagnosticSourceDemo接口信息如下:

  public interface IDiagnosticSourceDemo
{
string DiagnosticName { get; }
}

HttpDiagnosticSourceObserver方法如下:

public class HttpDiagnosticSourceObserver : IObserver<DiagnosticListener>
{
private IEnumerable<IDiagnosticSourceDemo> _diagnosticSourceDemo;
public HttpDiagnosticSourceObserver (IEnumerable<IDiagnosticSourceDemo> diagnosticSourceDemo)
{
_diagnosticSourceDemo= diagnosticSourceDemo;
} public void OnCompleted()
{
} public void OnError(Exception error)
{
} public void OnNext(DiagnosticListener listener)
{
var diagnosticSource= _diagnosticSourceDemo.FirstOrDefault(i => i.DiagnosticName == listener.Name);
if (traceDiagnostic != null)
{
//适配订阅
listener.SubscribeWithAdapter(diagnosticSource);
}
}
}

最终运行结果如下:

Zipkin为:

Exceplesstion日志记录信息为

通过Exceptionless记录的环境信息也能将对应的机器定位

参考

本文引用文章连接如下:

DiagnosticSource官方介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.diagnosticsource?view=net-5.0

DiagnosticSource参考连接:https://www.cnblogs.com/wucy/p/13532534.html

                   https://sudonull.com/post/3671-Using-the-DiagnosticSource-in-NET-Core-Theory

                   https://andrewlock.net/logging-using-diagnosticsource-in-asp-net-core/

docker部署Zipkin:https://www.cnblogs.com/binz/p/12658020.html

docekr部署exceptionless:https://www.cnblogs.com/edisonchou/p/exceptionless_v5_deployment_introduction.html


如有哪里讲得不是很明白或是有错误,欢迎指正

如您喜欢的话不妨点个赞收藏一下吧

使用zipKin构建NetCore分布式链路跟踪的更多相关文章

  1. 分布式链路跟踪系统架构SkyWalking和zipkin和pinpoint

    Net和Java基于zipkin的全链路追踪 https://www.cnblogs.com/zhangs1986/p/8966051.html 在各大厂分布式链路跟踪系统架构对比 中已经介绍了几大框 ...

  2. 跟我学SpringCloud | 第十一篇:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪

    SpringCloud系列教程 | 第十一篇:使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪 Springboot: 2.1.6.RELEASE SpringCloud: ...

  3. Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】

    Spring Cloud(十二):分布式链路跟踪 Sleuth 与 Zipkin[Finchley 版]  发表于 2018-04-24 |  随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请 ...

  4. spring-cloud-sleuth 和 分布式链路跟踪系统

    ==================spring-cloud-sleuth==================spring-cloud-sleuth 可以用来增强 log 的跟踪识别能力, 经常在微服 ...

  5. Zipkin和微服务链路跟踪

    https://cloud.tencent.com/developer/article/1082821 Zipkin和微服务链路跟踪 本期分享的内容是有关zipkin和分布式跟踪的内容. 首先,我们还 ...

  6. 你的Node应用,对接分布式链路跟踪系统了吗?(一) 原创: 金炳 Node全栈进阶 4天前 戳蓝字「Node全栈进阶」关注我们哦

    你的Node应用,对接分布式链路跟踪系统了吗?(一) 原创: 金炳 Node全栈进阶 4天前 戳蓝字「Node全栈进阶」关注我们哦

  7. springcloud(十二):使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪

    随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请求可能最终需要调用很多次后端服务才能完成,当整个请求变慢或不可用时,我们是无法得知该请求是由某个或某些后端服务引起的,这时就需要解决如何快读定位 ...

  8. 分布式链路跟踪 Sleuth 与 Zipkin【Finchley 版】

    原创: dqqzj SpringForAll社区 今天 Spring Cloud Sleuth Span是基本的工作单位. 例如,发送 RPC是一个新的跨度,就像向RPC发送响应一样. 跨度由跨度唯一 ...

  9. 使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪

    原文:http://www.cnblogs.com/ityouknow/p/8403388.html 随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请求可能最终需要调用很多次后端服务才能完成, ...

随机推荐

  1. Android面试6家一线大厂,这个问题是必问!

    年后面了六家大厂,每家都会问的一个问题就是Android的消息机制!可见Android的消息机制是多么重要! 消息机制之所以这么重要是因为Android应用程序是通过消息来驱动的,Android某种意 ...

  2. Typora加七牛云实现实时图片自动上传

    Typora加七牛云实现实时图片自动上传 前言: ​ Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别.重点是免费! ​ 其风格简约 ...

  3. 原生js 以ajax(post)的方式传json至php,并让php解析为数组

    如题. 比如要把一个json,如 json= {name:"John Rambo", time:"3pm"},,通过js ,传到一个php服务器 fwq.php ...

  4. Build a ZenTao Server on Linux

    Download xampp for linux on Apache Friends, file name: xampp-linux-1.8.3-2-installer.run; Intallatio ...

  5. MyBatis学习02(配置解析)

    配置解析 核心配置文件 mybatis-config.xml 系统核心配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息. 能配置的内容如下: configur ...

  6. 入门Kubernetes-StatefulSets

    前言: 前面文中对通过DaemonSet.存储资源对象,实现了在指定节点中运行一个守护进程. 在真实的业务场景中,部署的服务都是有状态的.且有数据需要持久化的:那么如何实现呢? 那么接下来学习一种更加 ...

  7. Linux下基于SQLite3 实现商店商品管理系统

    文章目录 一.SQLite相关C接口 重要接口 打开数据库 插入信息 查询 二.程序要求 三.程序说明 四.实现代码 水果店账单管理系统 一.SQLite相关C接口 如果第一次直接在命令行安装sqli ...

  8. 如何快速方便的生成好看的接口文档(apipost生成文档)

    一键生成文档 我们在"2分钟玩转APIPOST"一讲中,简单介绍了如何生成并分享接口文档: 点击分享文档 复制并打开文档地址就可以看到了完整的接口文档. 本节课主要是讲解一些需要注 ...

  9. @Transactional-同一个类中方法自调,调用方法事物失效

    问题分析 一个类中的方法调用另一个事物传播性为创建事物的方法,调用的方法事物失效? SpringAOP 代理的Service对象调用了其方法,这个方法再去调用这个Service中的其他方法是没有使用A ...

  10. npm : 无法加载文件 C:\Program Files\nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本。