.NET 一直在稳定的增加和改善对应用程序进行跨平台的诊断分析,在.NET Core 3.0, 我们看到了 EventCounters[1] 的介绍,用于观察和分析指标测量。

我最近在几个 .NET Core 的应用程序中使用 counters,来跟踪服务一段时间内 http 的请求数量。

.NET 5 一直在进步,我一直在关注 runtime repository [2] 的动态和工作,在 http 发生外部调用时,添加了新的遥测计数器和一些核心组件的事件,包括 HttpClient, Sockets, DNS 和 Security。

在这篇文章中,我将展示如何在 runtime(运行时)消费这些信息,需要注意的是,本文的代码仅仅是简单的实现,如果在生产中使用话,你还需要考虑到性能开销或者其他。

定义 EventListener

.NET 中已经有了 EventListener 抽象类,我们可以在代码中继承这个类,来自定义一个 listener

internal sealed class TelemetryListener : EventListener
{...
}

接下来,我们重写 OnEventSourceCreated 方法,来处理下边的几种特定事件的消息

protected override void OnEventSourceCreated(EventSource eventSource)
{if (eventSource.Name.Equals("System.Net.Sockets") || eventSource.Name.Equals("System.Net.Http")|| eventSource.Name.Equals("System.Net.NameResolution")|| eventSource.Name.Equals("System.Net.Security")){EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All, new Dictionary<string, string>{{"EventCounterIntervalSec", "2"}});}
}

在上面的代码中,我们获取到 eventSource.Name, 然后过滤我们感兴趣的类型的消息,例如, HttpTelemetry 类定义了 EventSource(事件源)的名字叫 System.Net.Http。

[EventSource(Name = "System.Net.Http")]
internal sealed class HttpTelemetry : EventSource
{...
}

在这个例子中,我们感兴趣的 event (事件) 和 counters (计数器)来自四个 event sources (事件源)

•NameResolution Telemetry – DNS lookups•Sockets Telemetry – Underlying network connections to a server•Security Telemetry – Establish TLS•Http Telemetry – HttpClient

当 EventSource 匹配一个我们想要监听的名字时,我们调用 EnableEvents 方法,在这个代码示例中,我们接收所有等级的 event(事件)和关键字,我们可以定义一个字典,可能会有其他额外的参数,当 EventCounters 开始消费时,我们可以设置频率来更新计数器,上面的代码表示我们希望计数器每两秒发送信息。

下边的代码我们重写 OnEventWritten 方法

protected override void OnEventWritten(EventWrittenEventArgs eventData)
{...
}

在这种方法中,我们将添加一些代码,来监听事件计数器,然后更新当前值,并且输出到控制台。

if (eventData.EventName == "EventCounters")
{if (eventData.Payload?.Count <= 0|| eventData.Payload?[0] is not IDictionary<string, object> data|| !data.TryGetValue("CounterType", out var ct)|| !data.TryGetValue("Name", out var n)|| ct is not string counterType|| n is not string name) return;var metricValue = metricType switch{"Sum" when data.TryGetValue("Increment", out var increment) => Convert.ToInt64(increment),"Mean" when data.TryGetValue("Mean", out var mean) => Convert.ToInt64(mean),_ => 0};switch (name){case "dns-lookups-duration":Console.WriteLine($"Event Counter = Avg Duration of Lookup: {metricValue}ms");Console.WriteLine();break;case "dns-lookups-requested":Console.WriteLine($"Event Counter = Name Resolution Lookups: {metricValue}");Console.WriteLine();break;case "total-tls-handshakes":Console.WriteLine($"Event Counter = Total TLS Handshakes: {metricValue}");Console.WriteLine();break;case "requests-started":Console.WriteLine($"Event Counter = HTTP Requests Started: {metricValue}");Console.WriteLine();break;case "requests-failed":Console.WriteLine($"Event Counter = HTTP Requests Failed: {metricValue}");Console.WriteLine();break;case "outgoing-connections-established":Console.WriteLine($"Event Counter = Outgoing Connections Established: {metricValue}");Console.WriteLine();break;case "http11-connections-current-total":Console.WriteLine($"Event Counter = HTTP1.1 Connections Established: {metricValue}");Console.WriteLine();break;case "http20-connections-current-total":Console.WriteLine($"Event Counter = HTTP2.0 Connections Established: {metricValue}");Console.WriteLine();break;case "bytes-sent":Console.WriteLine($"Event Counter = Bytes Sent: {metricValue}");Console.WriteLine();break;case "bytes-received":Console.WriteLine($"Event Counter = Bytes Received: {metricValue}");Console.WriteLine();break;}
}

上面的代码,我通过 eventData 的属性过滤了我感兴趣的日志,你可以注意到,上面我用了一些 C# 9.0 的语法 not 在判断条件中。

if (eventData.Payload?.Count <= 0|| eventData.Payload?[0] is not IDictionary<string, object> data|| !data.TryGetValue("CounterType", out var ct)|| !data.TryGetValue("Name", out var n)|| ct is not string counterType|| n is not string name) return;

下边的代码,我用了 C# 8.0的 switch 表达式来定义指标值,根据指标的类型,EventCounters 主体包含一个自增值或者平均值。

var metricValue = counterType switch{"Sum" when data.TryGetValue("Increment", out var increment) => Convert.ToInt64(increment),"Mean" when data.TryGetValue("Mean", out var mean) => Convert.ToInt64(mean),_ => 0};

下边的例子中,我使用 switch 指定了我们感兴趣的事件来源的名称,然后记录到控制台

switch (name)
{case "dns-lookups-duration":Console.WriteLine($"Event Counter = Avg Duration of Lookup: {metricValue}ms");Console.WriteLine();break;case ...
}

我们需要每过2s把但当前事件计数器的值输出到控制台,你可以选择把这些指标数据放到其他的指标服务,在过去,我把一些事件计数器的值发送到了 Datadog。

下边的这一块代码,我判断了 EventName, 如果不是 EventCounters,为了演示,我把这些信息都输出到了控制台

if (eventData.EventName != "EventCounters")
{Console.WriteLine($"Event = {eventData.EventSource.Name} - {eventData.EventId}:{eventData.EventName}");for (var i = 0; i < eventData.PayloadNames?.Count; i++){Console.WriteLine($" - {eventData.PayloadNames[i]}: {eventData.Payload?[i]?.ToString() ?? string.Empty}");}
}

使用 EventListener

我们在一个简单的控制台应用程序使用 TelemetryListener

internal class Program
{private static async Task Main(){using var listener = new TelemetryListener();var client = new HttpClient();try{await client.GetAsync("https://www.stevejgordon.co.uk");}catch{// ignore}await Task.Delay(2000);}
}

在这个 main 方法中,我创建了一个 TelemetryListener 实例,开始监听事件信息,我使用了 HttpClient 调用了我的博客主页,然后程序等待2s,这样我们的 listener 有足够的时间触发事件和接受消息。

运行程序后,我们可以在控制台看到这些信息

Event = System.Net.Http - 1:RequestStart- scheme: https- host: www.stevejgordon.co.uk- port: 443- pathAndQuery: /- versionMajor: 1- versionMinor: 1- versionPolicy: 0
Event = System.Net.NameResolution - 1:ResolutionStart- hostNameOrAddress:
Event = System.Net.NameResolution - 2:ResolutionStop
Event = System.Net.NameResolution - 1:ResolutionStart- hostNameOrAddress: www.stevejgordon.co.uk
Event = System.Net.NameResolution - 2:ResolutionStop
Event = System.Net.Sockets - 1:ConnectStart- address: InterNetworkV6:28:{1,187,0,0,0,0,32,1,8,216,16,15,240,0,0,0,0,0,0,0,2,127,0,0,0,0}
Event = System.Net.Sockets - 2:ConnectStop
Event = System.Net.Security - 1:HandshakeStart- isServer: False- targetHost: www.stevejgordon.co.uk
Event = System.Net.Security - 2:HandshakeStop- protocol: 3072
Event = System.Net.Http - 4:ConnectionEstablished- versionMajor: 1- versionMinor: 1
Event = System.Net.Http - 7:RequestHeadersStart
Event = System.Net.Http - 8:RequestHeadersStop
Event = System.Net.Http - 11:ResponseHeadersStart
Event = System.Net.Http - 12:ResponseHeadersStop
Event = System.Net.Http - 13:ResponseContentStart
Event = System.Net.Http - 14:ResponseContentStop
Event = System.Net.Http - 2:RequestStopEvent Counter = HTTP Requests Started: 1
Event Counter = HTTP Requests Failed: 0
Event Counter = HTTP1.1 Connections Established: 1
Event Counter = HTTP2.0 Connections Established: 0
Event Counter = Name Resolution Lookups: 2
Event Counter = Avg Duration of Lookup: 36ms
Event Counter = Outgoing Connections Established: 1
Event Counter = Bytes Received: 68222
Event Counter = Bytes Sent: 354
Event Counter = Total TLS Handshakes: 1

刚开始,我们看到的事件信息来自与我们订阅的4个来源,HttpClient 开始请求我的博客主页,这需要DNS来解析服务器的IP地址,Socket 连接创建,然后TLS握手开始,然后我有了一个TLS 连接,Http 请求发出信息并且接收到了响应,我们可以在控制台看到这些输出信息。

总结

这篇文章特别强调了.NET 的团队正在积极的添加新的遥测事件和事件计数器时, 这些诊断工具对于我们分析应用程序起到很关键的作用,这些事件和计数器可以在运行时进程内收集, 然后把这些信息发送到外部的指标服务,他们也支持跨平台进程跟踪和监视的应用程序行为,在未来的文章中,我希望将深入研究跟踪、可观测性,然后使用这些数据。

原文链接:https://www.stevejgordon.co.uk/additional-http-sockets-dns-and-tls-telemetry-in-dotnet-5

References

[1] EventCounters: https://devblogs.microsoft.com/dotnet/introducing-diagnostics-improvements-in-net-core-3-0/
[2] runtime : https://github.com/dotnet/runtime/

「译」 .NET 5 新增的Http, Sockets, DNS 和 TLS 遥测相关推荐

  1. iOS 9,为前端世界都带来了些什么?「译」

    2015 年 9 月,Apple 重磅发布了全新的 iPhone 6s/6s Plus.iPad Pro 与全新的操作系统 watchOS 2 与 tvOS 9(是的,这货居然是第 9 版),加上已经 ...

  2. jvm 系列(九):如何优化 Java GC 「译」

    本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的"Become a Java GC Expert ...

  3. 变量、中文-「译」javascript 的 12 个怪癖(quirks)-by小雨

    在写这篇文章之前,xxx已经写过了几篇关于改变量.中文-主题的文章,想要懂得的朋友可以去翻一下之前的文章 原文:12 JavaScript quirks 译文:「译」javascript 的 12 个 ...

  4. jvm系列(十):如何优化Java GC「译」

    本文由CrowHawk翻译,地址:如何优化Java GC「译」,是Java GC调优的经典佳作. Sangmin Lee发表在Cubrid上的"Become a Java GC Expert ...

  5. js最小化浏览器_「译」解析、抽象语法树(ast) +如何最小化解析时间的5个技巧...

    前言 该系列课程会在本周陆续更新完毕,主要讲解的都是工作中可能会遇到的真实开发中比较重要的问题以及相应的解决方法.通过本系列的课程学习,希望能对你日常的工作带来些许变化.当然,欢迎大家关注我,我将持续 ...

  6. 「译」一起探讨 JavaScript 的对象

    「译」一起探讨 JavaScript 的对象 原文地址:Let's explore objects in JavaScript 原文作者:Cristi Salcescu 译文出自:阿里云翻译小组 译文 ...

  7. 「译」一个3D网页是如何制作的

    「译」一个3D网页是如何制作的 原文: 本文作者制作了一个3D网页作为自己的个人主页,是一个遥控汽车的游戏页面.页面十分有趣,感兴趣的朋友可以先打开体验一下. 以下为原文的译文,是我个人理解的版本.大 ...

  8. 「译」ECMAScript 提案:类型注解(TypeScript 在未来可能被纳入 ECMAScript 中)

    原文标题:ECMAScript proposal: Type Annotations 原文链接:github.com/tc39/propos- ECMAScript 关于类型注解的提案已进入 stag ...

  9. 「译」更快的 async 函数和 promises

    来源:https://www.yuque.com/es2049/blog 译自:Faster async functions and promises JavaScript 的异步过程一直被认为是不够 ...

最新文章

  1. java处理视频_Java使用FFmpeg处理视频[视频直播三]
  2. utc时间 单位换算_数学基础知识点总结,常用单位换算长度、时间、面积等分类...
  3. HTML5学习笔记(二十七):Ajax
  4. 黑马vue---10-11、Vue实现跑马灯效果
  5. HTML简单的自定义属性制作tab切换
  6. nyoj A+B Problem IV
  7. Tomcat服务器目录结构
  8. PXE 01-PXE介绍
  9. labview求n阶乘的和_求极限方法总结
  10. JSP编程技术2-动态标签
  11. dz3.4后台admin.php修改,Discuz X3.4论坛管理员无法登录后台,修改管理员账号密码的各种解决方法总结...
  12. 局部光照与全局光照的区别
  13. YUI 3 Cookbook 中文版
  14. u盘推荐知乎_u盘系统盘制作 知乎ghost安装教程
  15. Excel提示:您的组织策略阻止我们为您完成此操作
  16. 如何快速插入大量/批量随机数据到数据库(oracle/sqlserver/mysql/postgresql)
  17. 谁将泡泡玛特推上千亿市值神坛?
  18. 分水岭算法java,C++实现分水岭算法(Watershed Algorithm)
  19. 怎样将表格拆分为两个
  20. 利用python对数据进行分组统计

热门文章

  1. linux如何查看所有的用户(user)、用户组(group)、密码(password/passwd)
  2. solidity事件详解
  3. Javascript基础学习20问(二)
  4. python下sqlite增删查改方法(转)
  5. 程序员必须知道的HTML常用代码有哪些?
  6. Vijos p1484 ISBN号码
  7. animate 实现滑动切换效果
  8. Object-C 类,对象,运行时,isa
  9. 码农与UI沟通的日常
  10. Log4j.properties 配置详解