AIGC 应用、数据分析等企业 10+ 热门专题课,就在极客时间企业版>>> 了解详情
写点什么

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

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

    阅读完需:约 17 分钟

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

AI 大模型超全落地场景&金融应用实践,8 月 16 - 19 日 FCon x AICon 大会联诀来袭、干货翻倍!

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


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


为了解决这个问题,应用性能管理厂商(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:532638

评论

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

2023年国家级重大人才工程引进申报

科兴未来News

创新创业 江苏工博会 博士后 #人才政策# 启明计划

用javascript分类刷leetcode16.set&map(图文视频讲解)

js2030code

JavaScript LeetCode

融云 x 易车:携手八载,打造高效连接用户的汽车服务平台

融云 RongCloud

学习web前端培训怎么样?

小谷哥

前端工程师leetcode算法面试必备-二分搜索算法(上)

js2030code

JavaScript LeetCode

「BY林子」网站资源下载

BY林子

开源即时通讯IM框架 MobileIMSDK v6.3 发布

JackJiang

网络编程 即时通讯IM

新鲜出炉!东莞华为云团泊洼数据中心T1项目成功入选2022年ICT优秀案例

Geek_2d6073

国内外10大主流需求管理工具优缺点对比

易成管理学

项目管理 产品管理 管理工具

必备技巧,PCB设计布局的4项注意(基础篇)

华秋PCB

PCB 布局 PCB设计 布线

火山引擎ByteHouse助力中国地震台网中心,快速构建一站式实时数仓

字节跳动数据平台

大数据 Clickhouse 数据平台

java开发培训机构怎样选择?

小谷哥

react源码分析:实现react时间分片

flyzz177

React

一口气讲完了Redis常用的数据结构及应用场景

小小怪下士

Java redis 程序员

百度工程师带你了解Module Federation

百度Geek说

JavaScript 微前端 企业号 2 月 PK 榜

什么是WMS系统?

SAP虾客

wms

react源码分析:组件的创建和更新

flyzz177

React

一文搞懂Redis

京东科技开发者

redis hash Zset Redis 协议 企业号 2 月 PK 榜

让远程成为本地,微服务后端开发的福音

北京好雨科技有限公司

Kubernetes 微服务 云原生 企业号 2 月 PK 榜

ChatGPT背后的经济账

OneFlow

人工智能 深度学习 大模型

谈谈我对于关键思考的理解

阿里巴巴中间件

阿里云 关键

lua中实现判断是否为局域网IP

ModStart

从入门到实战!阿里内部正式上线“Spring Cloud Alibaba (全彩小册)”

架构师之道

Java 编程 程序员 微服务

java线下培训适合小白学习吗

小谷哥

react源码分析:深度理解React.Context

flyzz177

React

如何实现OpenHarmony的OTA升级

OpenHarmony开发者

OpenHarmony

前端刷完这12道滑动窗口,就可以出山面试了

js2030code

JavaScript LeetCode

文盘Rust -- rust 连接云上数仓 starwift

京东科技开发者

rust Clickhouse 语言 rust语言 企业号 2 月 PK 榜

使用 NineData 快速构建企业容灾备份

NineData

数据库 数据管理工具 数据备份 运维开发 企业容灾

融云「聊天室属性」: 语聊房、直播间有序运行和丝滑体验的绝技

融云 RongCloud

一个简单的案例入门 gRPC

江南一点雨

gRPC

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