【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

.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:532589

评论

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

Linux system hardening: adding hidepid to /proc mount point

卓丁

Linux linux security proc hidepid

👊 【Spring技术特性】带你看看那些可能你还不知道的特性技巧哦!

洛神灬殇

Java spring Spring特性 10月月更

谈 C++17 里的 Memento 模式

hedzr

设计模式 备忘录模式 Design Patterns c++17 Undo Manager

【设计模式】第四篇 - 简单工厂

Brave

设计模式 工厂模式 10月月更

微博评论高性能高可用计算架构设计

看,有只猪

【Flutter 专题】133 图解自定义 ACEWaterButton 水波纹按钮

阿策小和尚

Flutter 小菜 0 基础学习 Flutter Android 小菜鸟 10月月更

在线字符串哈希/散列工

入门小站

工具

Kafka 生产环境部署指南

Se7en

API网关Kong实战

Fox

kong api 网关 API Gateway

018云原生之基础架构

穿过生命散发芬芳

云原生 10月月更

Prometheus 基础查询(四)修饰符

耳东@Erdong

Prometheus 10月月更

WEB图像优化

devpoint

性能优化 image 图像格式 10月月更

Spring 框架学习

风翱

spring 10月月更

Gas 机制是如何运作的

Rayjun

以太坊

云原生训练营 20211017- docker作业

好吃不贵

linux之awk使用技巧

入门小站

Linux

Win11安装PyTorch

IT蜗壳-Tango

10月月更

Go 扇入 / 扇出

baiyutang

golang 10月月更

官方线索|科大讯飞全球1024开发者节

xcbeyond

1024我在现场

5分钟搞懂Monorepo

俞凡

git 架构

看山聊 Java:IPv4 与 int 之间互相转换

看山

Java 10月月更

02-面向对象设计原则

千羽的编程时光

设计模式

【LeetCode】二叉搜索树中第K小的元素Java题解

Albert

算法 LeetCode 10月月更

linux【redhat&ubuntu】下ffmpeg-3.1安装编译及视频转码

程序员架构进阶

架构 ffmpeg 视频流 10月月更

初始化 Ubuntu 工作环境

看山

ubuntu 10月月更

【产品】论增长黑客思维如何让B端产品爆发式增长

极光一号。

云原生 用户增长 b端产品经理 增长黑客

01-设计模式概述

千羽的编程时光

设计模式

【LeetCode】环形链表Java题解

Albert

算法 LeetCode 10月月更

【架构实战营作业】模块五——微博评论计算架构

聆息

阿里P8面试官:如何设计一个扛住千万级并发的架构?

Java 程序员 架构 面试 高并发

CPU、指令集、微架构概念学习

轻口味

10月月更

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