NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

.NET Core 3.0 的新改进:针对分布式应用程序的故障诊断和监控

  • 2019-11-15
  • 本文字数:5225 字

    阅读完需:约 17 分钟

.NET Core 3.0的新改进:针对分布式应用程序的故障诊断和监控

由于分布式应用是由多个组件组成的,且这些组件往往是由不同的团队拥有和操作,所以在与应用程序发生交互时,就会需要跨多个组件执行代码的分布式跟踪。如果用户遇到了问题,想要确定是哪个组件出现了差错,基本就是一件不可能完成的事情。


与单体应用程序相比,分布式应用程序的特点之一就是很难将单个分布式跟踪的遥测(如日志)关联起来。虽然用户可以通过查看日志了解到每个组件是如何处理每个请求的,但是很难知道一个组件中的请求和另一个组件中的请求是否属于同一分布式跟踪。


为了解决这个问题,应用性能管理厂商(APM)会提供从一个组件到另一个组件的分布式跟踪上下文传播功能。但是因为很多环境具有异构性、组件归属不同的团队,并通过不同的工具进行监视,因此对分布式应用程序进行一致的测试仍是难点。


随着 W3C Trace Contex 规范逐步过渡到 Proposed Recommendation maturity 级别,再加上其它供应商和平台对该规范的支持,上下文传播的复杂性正在降低。W3C Trace Contex 定义了分布式跟踪上下文的语义和格式,这使得分布式应用程序中的每个组件都可以理解上下文,并将其传播到调用的组件中。


为了使得分布式应用程序开发更容易,微软做了很多努力,例如 Orleans 框架和 Dapr 项目,而在分布式跟踪上下文传播,微软的服务和平台将采用 W3C Trace Contex 规范。同时,针对分布式跟踪和日志,.NET Core 3.0 做了很多新工作。

分布式跟踪和日志记录

.NET Core 3.0 在分布式跟踪方面的最新改进:


  • 两个开箱即用的.NET Core 3.0 应用程序在整个分布式跟踪中关联日志;

  • .NET Core 3.0 如何设置分布式跟踪上下文,如何自动跨 http 传播;

  • 同一个分布式跟踪标识如何被遥测 SDK 使用,例如 OpenTelemetry 和 ASP.NET Core logs。


为了帮助大家更深刻的理解.NET Core 3.0 的新改进,下面我们做了一个示例。

准备工作

该示例中,我们需要用到三个简单的组件:ClientApp、FrontEndApp 和 BackEndApp。BackEndApp 是一个名为 WeatherApp 的模板 ASP.NET Core 应用程序,可以通过公开的 REST API 来获取天气预报。而 FrontEndApp 会通过控制权将所有传入的请求发送给 BackEndApp。



[ApiController]
[Route("[controller]")]
public class WeatherForecastProxyController : ControllerBase
{
private readonly ILogger<WeatherForecastProxyController> _logger;
private readonly HttpClient _httpClient;
public WeatherForecastProxyController(
ILogger<WeatherForecastProxyController> logger,
HttpClient httpClient)
{
_logger = logger;
_httpClient = httpClient;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
var jsonStream = await
_httpClient.GetStreamAsync("http://localhost:5001/weatherforecast");
var weatherForecast = await
JsonSerializer.DeserializeAsync<IEnumerable<WeatherForecast>>(jsonStream);
return weatherForecast;
}
}

复制代码


ClientApp 是一个 .NET Core 3.0 Windows Forms app,会调用 FrontEndApp 进行天气预报。


private async Task<string> GetWeatherForecast()
{
return await _httpClient.GetStringAsync(
"http://localhost:5000/weatherforecastproxy");
}
复制代码


需要注意的是,在这段示例中,没有安装任何额外的 SDK 和库。

查看日志

我们通过 ClientApp 调用来观察 FrontEndApp 和 BackEndApp 生成的日志。


FrontEndApp :


info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
=> ConnectionId:0HLR1BR0PL1CH
=> RequestPath:/weatherforecastproxy
RequestId:0HLR1BR0PL1CH:00000001,
SpanId:|363a800a-4cf070ad93fe3bd8.,
TraceId:363a800a-4cf070ad93fe3bd8,
ParentId:
Executed endpoint 'FrontEndApp.Controllers.WeatherForecastProxyController.Get (FrontEndApp)'
复制代码


BackEndApp:


info: BackEndApp.Controllers.WeatherForecastController[0]
=> ConnectionId:0HLR1BMQHFKRL
=> RequestPath:/weatherforecast
RequestId:0HLR1BMQHFKRL:00000002,
SpanId:|363a800a-4cf070ad93fe3bd8.94c1cdba_,
TraceId:363a800a-4cf070ad93fe3bd8,
ParentId:|363a800a-4cf070ad93fe3bd8.
Executed endpoint 'FrontEndApp.Controllers.WeatherForecastController.Get (BackEndApp)'
复制代码


与 magic 一样,来自两个独立应用程序的日志共享相同的 TraceId。ASP.NET Core 3.0 应用程序会初始化分布式跟踪上下文,并将其传递到头文件中。



FrontEndApp 并没有收到任何其它的头文件,出现这种情况的原因是在 ASP.NET Core 应用程序中,分布式跟踪是由 ASP.NET Core 框架自身在每次传入请求时启动的。


启动分布式跟踪

相信很多人都注意到了 Windows 窗体应用 ClientApp 和 ASP.NET Core FrontEndApp 在行为上的差异。ClientApp 未设置任何分布式跟踪上下文,因此 FrontEndApp 也不会收到。设置分布式操作最简单的方法是,使用 DiagnosticSource 包中名为 Activity 的 API。


private async Task<string> GetWeatherForecast()
{
var activity = new Activity("CallToBackend").Start();
try
{
return await _httpClient.GetStringAsync(
"http://localhost:5000/weatherforecastproxy");
}
finally
{
activity.Stop();
}
}
复制代码


启动之后,HttpClient 就知道需要传播分布式跟踪上下文。需要注意的是,ClientApp、FrontEndApp 和 BackEndApp 都共享同一个 TraceId。

W3C Trace Context

上下文使用 Request-Id 进行传播,这是从 ASP 中引入的,是默认生效的。但是,W3C Trace Context 正被广泛采用,因此,我们建议切换到 W3C Trace Context 的上下文传播格式。


在.NET Core 3.0 中,切换到 W3C Trace Context 格式只需要在主方法中添加一行代码:


static void Main()
{
Activity.DefaultIdFormat = ActivityIdFormat.W3C;

Application.Run(new MainForm());
}
复制代码


当 FrontEndApp 收到来自 ClientApp 的请求时,你会在请求中看到一个 traceparent 头文件:



通过这个头文件,.NET Core 应用程序就会明白现在需要通过 W3C Trace Context 格式来进行传播调用。需要注意的是,.NET Core 应用程序可以自动识别 W3C Trace Context 的正确格式,但是将分布式跟踪上下文的默认格式切换到 W3C Trace Context,可以在异构环境中实现更好的互操作性。


所有属性为 TraceId 和 SpanId 的日志:


info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
=> ConnectionId:0HLQV2BC3VP2T
=> RequestPath:/weatherforecast
RequestId:0HLQV2BC3VP2T:00000001,
SpanId:da13aa3c6fd9c146,
TraceId:f11a03e3f078414fa7c0a0ce568c8b5c,
ParentId:5076c17d0a604244
Request starting HTTP/1.1 GET http://localhost:5000/weatherforecast
复制代码

OpenTelemetry 的 Activity 和分布式跟踪

OpenTelemetry 提供了一组 API、库、代理和控制器服务,用于从应用程序捕获分布式跟踪和度量。


在调用时启动 AddOpenTelemetry,就可以在 BackEndApp 上启用 OpenTelemetry:


services.AddOpenTelemetry(b => 
b.UseZipkin(o => {
o.ServiceName="BackEndApp";
o.Endpoint=new Uri("http://zipkin /api/v2/spans");
})
.AddRequestCollector());
复制代码


FrontEndApp 日志中的 TraceId 与 BackEndApp 中的 TraceId 匹配。


info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
=> ConnectionId:0HLR2RC6BIIVO
=> RequestPath:/weatherforecastproxy
RequestId:0HLR2RC6BIIVO:00000001,
SpanId:54e2de7b9428e940,
TraceId:e1a9b61ec50c954d852f645262c7b31a,
ParentId:69dce1f155911a45
=> FrontEndApp.Controllers.WeatherForecastProxyController.Get (FrontEndApp)
Executed action FrontEndApp.Controllers.WeatherForecastProxyController.Get (FrontEndApp) in 3187.3112ms
info: Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker[2]
=> ConnectionId:0HLR2RLEHSKBV
=> RequestPath:/weatherforecast
RequestId:0HLR2RLEHSKBV:00000001,
SpanId:0e783a0867544240,
TraceId:e1a9b61ec50c954d852f645262c7b31a,
ParentId:54e2de7b9428e940
=> BackEndApp.Controllers.WeatherForecastController.Get (BackEndApp)
Executed action BackEndApp.Controllers.WeatherForecastController.Get (BackEndApp) in 3085.9111ms
复制代码


此外,Zipkin 将报告相同的跟踪,因此,可以将分布式跟踪工具收集的分布式跟踪与来自计算机的日志关联起来。当 ClientApp 遇到问题时,可以将此 TraceId 提供给用户,由于用户和应用程序可以共享,因此能够更加容易的跨组件发现相应的日志和分布式跟踪。



另外,用户可以轻松启用对三个组件的监控,并在甘特图上查看:


ASP .NET Core 应用程序与分布式跟踪集成

APM 厂商收集到的遥测数据和 ASP .NET Core 使用的分布式跟踪上下文是相关的。因此,ASP .NET Core 3.0 应用程序非常适合不同团队拥有不同组件的场景。


例如,下图中的两个应用 A 和 C,启用了类似于 OpenTelemetry 的 SDK 遥测采集。如果不使用 ASP .NET Core 3.0,那么应用程序 B 就会“破坏”跟踪,导致分布式跟踪无法起作用。



在大多数部署中,ASP.NET Core 应用程序配置为启用基本日志记录,因此应用程序 B 将传播分布式跟踪上下文。而来自 A 和 C 的分布轨迹将相互关联。在以前的应用程序中,如果 ClientApp 和 BackEndApp 被感知,而 FrontEndApp 没有被感知,仍然可以看到分布式跟踪是相关的:



ASP.NET Core 应用程序非常适合服务网格环境。在服务网格部署中,上图中的 A 和 C 可以表示服务网格。为了让服务网格请求进入和离开组件 B,应用程序必须包含某些头文件。


虽然 Istio 能够自动发送 span,但是仍需要一些提示来连接整个跟踪。应用程序需要使用合适的 HTTP 头文件,以便在发送 span 消息时,能够正确关联到单个跟踪中。如果是采用 W3C Trace Context 格式,ASP.NET Core 应用程序则无需做任何改变。

传递附加上下文

如果你希望能够在分布式应用程序的组件之间共享更多上下文,可以添加以下属性:


private async Task<string> GetWeatherForecast()
{
var activity = new Activity("CallToBackend")
.AddBaggage("appVersion", "v1.0")
.Start();
try
{
return await _httpClient.GetStringAsync(
"http://localhost:5000/weatherforecastproxy");
}
finally
{
activity.Stop();
}
}

复制代码


服务器端,在 FrontEndApp 和 BackEndApp 可以看到一个额外的头文件 Correlation-Context。



使用 Activity.Baggage:


var appVersion =  Activity.Current.Baggage.FirstOrDefault(b => b.Key == "appVersion").Value;
using (_logger.BeginScope($"appVersion={appVersion}"))
{
_logger.LogInformation("this weather forecast is from random source");
}

复制代码


作用域中包含 appVersion:


info: FrontEndApp.Controllers.WeatherForecastController[0]
=> ConnectionId:0HLQV353507UG
=> RequestPath:/weatherforecast
RequestId:0HLQV353507UG:00000001,
SpanId:37a0f7ebf3ecac42,
TraceId:c7e07b7719a7a3489617663753f985e4,
ParentId:f5df77ba38504846
=> FrontEndApp.Controllers.WeatherForecastController.Get (BackEndApp)
=> appVersion=v1.0
this weather forecast is from random source
复制代码

未来发展

随着 ASP.NET Core 3.0 的改进,很多 ASP .NET Core 包含的功能可能就难以使用了,比如开发人员和 DevOps 想要做一个交钥匙遥测解决方案,需要和很多 APM 的厂商来做。但是,我们在 OpenTelemetry 方面会加大投入,使得更多 ASP .NET Core 用户能够在监控和故障排除方面变得更容易。


我们会帮助用户采用 W3C Trace Context,并且在 ASP .NET Core 的未来版本中可能将其作为默认的分布式跟踪上下文传播格式。


另外,我们还会专注于改进分布式上下文传播场景。与 Monolits 相比,分布式应用程序在单个分布式跟踪的生存期缺少公共共享状态,而这种共享状态可以用于基本的日志记录、用于请求的高级路由、实验、A/B 测试、业务上下文传播等。


原文链接:


https://devblogs.microsoft.com/aspnet/improvements-in-net-core-3-0-for-troubleshooting-and-monitoring-distributed-apps/


2019-11-15 08:532605

评论

发布
暂无评论
发现更多内容

详解MOVE PROTOCOL的测试版,让健康运动如影随形

鳄鱼视界

传统企业数字化转型,到底难在哪里?

SoFlu软件机器人

从概念到安全实践:软件供应链基础指南

SEAL安全

DevOps 安全 DevSecOps 软件供应链

【ELT.ZIP】OpenHarmony啃论文俱乐部—硬件加速的快速无损压缩

ELT.ZIP

OpenHarmony 压缩算法 ELT.ZIP 啃论文俱乐部

云原生存储解决方案Rook-Ceph与Rainbond结合的实践

北京好雨科技有限公司

Kubernetes PaaS Ceph rainbond

青藤“基于工业互联网的安全方案”成功入选信通院守卫者计划

青藤云安全

主机安全 互联网安全

主数据管理平台功能模型介绍

agileai

Java 数据治理 数据模型 主数据平台 功能模型

不愧是美团内部“接口自动化测试学习笔记”这细节讲解,神了

伤心的辣条

Python 程序人生 软件测试 自动化测试 接口测试

数字先锋| 助力打造国有资本运营升级版 中国国新构建数字化转型新格局

天翼云开发者社区

Vue-9-计算属性的属性

Python研究所

6月月更

数字先锋 | 牵手中资医疗医药,开创医疗医药应急保障服务新格局

天翼云开发者社区

实战邮件攻击简要分析【网络安全】

网络安全学海

网络安全 安全 渗透测试 WEB安全 漏洞挖掘

【赛事预告】云上开发,高效智能——第二届阿里云ECS CloudBuild开发者大赛即将启动

阿里云弹性计算

开发者大赛 自动化运维 云上运维 机密计算 内存缓存

直播场景音频降噪,传统算法 VS AI 算法对比和实践

融云 RongCloud

手慢无!‘’阿里爸爸‘’6月最新开源新版Spring Cloud Alibaba全体系10w字全彩笔记

Java全栈架构师

Java 程序员 面试题 架构师 SpringCloud

详解MOVE PROTOCOL的测试版,让健康运动如影随形

西柚子

天翼云为欧拉社区贡献首个C++热补丁 加速推进联创技术落地应用

天翼云开发者社区

详解GPU虚拟化技术

Finovy Cloud

人工智能 云渲染 GPU服务器

2022,云上开发新纪元

Heighliner

云原生 #k8s 开发者, 远程开发

What are the uses of LED display?

Dylan

LED LED display

Charles 工具如何做断点测试

伤心的辣条

Python 程序人生 软件测试 自动化测试 接口测试

【ELT.ZIP】OpenHarmony啃论文俱乐部—一种深度神经网压缩算法

ELT.ZIP

OpenHarmony 压缩算法 ELT.ZIP 啃论文俱乐部 深度神经网

小程序容器技术,加速工业互联网平台建设

Geek_99967b

小程序 工业互联网 小程序容器

天人合一物我相融,站点升级渐进式Web应用PWA(Progressive Web Apps)实践

刘悦的技术博客

前端 App 应用 Web JS SDK PWA

从小白到架构师原来是这样修炼出来的

C++后台开发

架构师 C++后台开发 软件架构师 服务器架构师 C++架构师

安心+10000

天翼云开发者社区

EMQ作为首批创始会员单位,加入SAP可持续发展与实践战略联盟

EMQ映云科技

物联网 IoT SAP emq 6月月更

先睹为快 | 卓越示范中心ETB003云原生安全实验测试床

青藤云安全

容器安全 信通院 云原生安全

Elux-从"微前端"到“微模块”

hiisea

前端框架 微前端 微模块 elux

Python接口自动化核心模块 - 数据库操作和日志

伤心的辣条

程序员 程序人生 软件测试 接口测试 Python自动化测试

天翼云电脑打造极致流畅与安全 助企业数字办公升级

天翼云开发者社区

.NET Core 3.0的新改进:针对分布式应用程序的故障诊断和监控_语言 & 开发_Sourabh Shirhatti_InfoQ精选文章