前言:

 前面两篇文章都介绍了.NET Core 性能诊断工具,其中诊断工具都用到了EventCounters来实时的收集服务器性能指标。

 那么收集指标能否自己定义呢?

一、What's EventCounters ?

 EventCounters 是一些 .NET Core API,用于轻量级、跨平台、准实时性能指标收集。 EventCounters 添加为 Windows 上的 .NET 框架的“性能计数器”的跨平台替代。

 EventCounters 作为 EventSource 的一部分实时自动定期推送到侦听器工具。 与 EventSource 上所有其他事件一样,可以通过 EventListener 和 EventPipe 在进程内和进程外使用它们。

 

 EventCounters 分为两类计数器: 某些计数器用于计算“比率”的值,例如异常总数、GC 总数和请求总数。 其他计数器是“快照”值,例如堆使用情况、CPU 使用率和工作集大小。 在这两个类别的计数器中,各有两种类型的计数器,由获取值的方式区分。 轮询计数器通过回调检索其值,非轮询计数器直接在计数器实例上设置其值。  

 计数器由以下实现表示:

  • EventCounter:记录一组值。 EventCounter.WriteMetric 方法将新值添加到集。 在每个间隔中,将计算集的统计摘要,如最小值、最大值和平均值。

    EventCounter 用于描述一组离散的操作。 常见用法包括监视最近 IO 操作的平均大小(以字节为单位)或一组金融交易的平均货币价值。

  • IncrementingEventCounter:记录每个时间间隔的运行总计,使用 Increment 方法添加到总计。

   例如,如果在一段间隔内调用三次 Increment(),其值分别为 12 和 5,则此间隔的计数器值将报告运行总计 8。 用于测量操作发生的频率,例如每秒处理的请求数。

  • IncrementingPollingCounter:使用回调来确定报告的值。 在每个时间间隔中,调用用户提供的回调函数,然后返回值用作计数器值。

   例如获取磁盘上的当前可用字节。 它还可用于报告应用程序可按需计算的自定义统计信息。 示例包括报告最近请求延迟的第 95 个百分位,或缓存的当前命中或错过比率。

  • PollingCounter:使用回调来确定报告的增量值。 对于每个时间间隔,调用回调,然后当前调用与最后一个调用之间的差值是报告的值。

   如果不可在每次发生事件时调用 API,但可以查询事件总数,则此计数器很有用。 例如,可以报告每秒写入文件的字节数,即使每次写入字节时没有通知。

  了解各种计数器后,我们就可以开始实现自己的EventCounters来跟踪各种指标,并在自己的程序中使用

二、自定义实现EventSource

 自定义实现一个获取.NET Core Api项目的请求总数量、获取当前进程内存占用、请求数量、请求平均耗时情况的EventSource。

 1、添加:ApiEventCounterSource.cs 实现EventSource,并添加计数器用于获取对应数据

using System;
using System.Diagnostics.Tracing;
using System.Threading; namespace AuditLogDemo.EventSources
{
/// <summary>
/// Api.EventCounter 事件源
/// </summary>
[EventSource(Name = "Api.EventCounter")]
public sealed class ApiEventCounterSource : EventSource
{
public static readonly ApiEventCounterSource Log = new ApiEventCounterSource(); private EventCounter _requestCounter; private PollingCounter _workingSetCounter; private PollingCounter _totalRequestsCounter; private IncrementingPollingCounter _incrementingPollingCounter; private long _totalRequests; private ApiEventCounterSource()
{
} protected override void OnEventCommand(EventCommandEventArgs command)
{
if (command.Command == EventCommand.Enable)
{
//请求响应耗时
_requestCounter = new EventCounter("request-time", this)
{
DisplayName = "Request Processing Time",
DisplayUnits = "ms"
}; //内存占用
_workingSetCounter = new PollingCounter("working-set", this, () => (double)(Environment.WorkingSet / 1_000_000))
{
DisplayName = "Working Set",
DisplayUnits = "MB"
}; //总请求量
_totalRequestsCounter = new PollingCounter("total-requests", this, () => Volatile.Read(ref _totalRequests))
{
DisplayName = "Total Requests",
DisplayUnits = "次"
}; //单位时间请求速率
_incrementingPollingCounter = new IncrementingPollingCounter("Request Rate", this, () =>
{
return Volatile.Read(ref _totalRequests);
})
{
DisplayName = "Request Rate",
DisplayUnits = "次/s",
//时间间隔1s
DisplayRateTimeScale = new TimeSpan(0, 0, 1)
};
}
} public void Request(string url, float elapsedMilliseconds)
{
//更新请求数量(保证线程安全)
Interlocked.Increment(ref _totalRequests); //写入指标值(请求处理耗时)
_requestCounter?.WriteMetric(elapsedMilliseconds); } protected override void Dispose(bool disposing)
{
_requestCounter?.Dispose();
_requestCounter = null; _workingSetCounter?.Dispose();
_workingSetCounter = null; _totalRequestsCounter?.Dispose();
_totalRequestsCounter = null; _incrementingPollingCounter?.Dispose();
_incrementingPollingCounter = null; base.Dispose(disposing);
}
}
}

 2、添加过滤器:ApiRequestTimeFilter调用ApiEventCounterSource方法

public class ApiRequestTimeFilterAttribute : ActionFilterAttribute
{
readonly Stopwatch _stopwatch = new Stopwatch();
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
//开启耗时记录
_stopwatch.Start();
}
public override void OnResultExecuted(ResultExecutedContext context)
{
//关闭耗时记录
_stopwatch.Stop();
//调用方法记录耗时
     ApiEventCounterSource.Log.Request(context.HttpContext.Request.GetDisplayUrl(), _stopwatch.ElapsedMilliseconds);
}
}

 3、启用过滤器

services.AddControllers(options =>
{
options.Filters.Add(typeof(ApiRequestTimeFilterAttribute));
});

三、使用 EventCounters

 1、进程外使用:

  a) 使用全局诊断工具:

   dotnet-counters、dotnet-trace、dotnet-monitor 使用大体相同

  • 使用 dotnet-counters ps 命令来显示可监视的进程的列表:      
  • 使用命令获取计数器实时结果:通过--counters参数指定计数器范围

     dotnet-counters monitor --process-id 进程id --counters Api.EventCounter,System.Runtime[cpu-usage]

  运行结果如下:

    

  b) 自实现工具:

   1、添加一个:EventPipeProvider 指定名称为:Api.EventCounter

var providers = new List<EventPipeProvider>()
{
new EventPipeProvider("Api.EventCounter",EventLevel.Informational,(long)ClrTraceEventParser.Keywords.None,
new Dictionary<string, string>() {{ "EventCounterIntervalSec", "1" }})
};

   2、运行效果如下:

    

 2、进程内使用:

  实现EventListener,获取计数器值

public class ApiEventListener : EventListener
{
protected override void OnEventSourceCreated(EventSource eventSource)
{
if (!eventSource.Name.Equals("Api.EventCounter"))
{
return;
} EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All, new Dictionary<string, string>()
{
["EventCounterIntervalSec"] = "1"
});
} protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (!eventData.EventName.Equals("Api.EventCounter"))
{
return;
} for (int i = 0; i < eventData.Payload.Count; ++i)
{
if (eventData.Payload[i] is IDictionary<string, object> eventPayload)
{
var (counterName, counterValue) = GetRelevantMetric(eventPayload);
Console.WriteLine($"{counterName} : {counterValue}");
}
}
} /// <summary>
/// 计数器名称和计数器值
/// </summary>
/// <param name="eventPayload"></param>
/// <returns></returns>
private static (string counterName, string counterValue) GetRelevantMetric(IDictionary<string, object> eventPayload)
{
var counterName = "";
var counterValue = ""; if (eventPayload.TryGetValue("DisplayName", out object displayValue))
{
counterName = displayValue.ToString();
}
if (eventPayload.TryGetValue("Mean", out object value) ||
eventPayload.TryGetValue("Increment", out value))
{
counterValue = value.ToString();
} return (counterName, counterValue);
}
}

四、总结

 自定义实现EventSource可用于当前.dotnet 未提供的指标扩展、也可用于业务系统指定指标实现,等着去解锁使用。

 当然dotnet 已经提供了多种性能计数器:供一般情况使用。已知EventCounters

EventSource的自定义实现的更多相关文章

  1. web前端学习(二)html学习笔记部分(8)--服务器推送事件3

    1.2.22  html5服务器推送事件 1.2.22.1  html5服务器推送事件介绍 服务器推送事件(Server-sent Events)是HTML5规范中的一个组成部分,可以用来从服务器端实 ...

  2. Asp.Net 自定义储存Session方式

    介绍 由于针对于自定义Session存储方式比较少,所以整理了使用自定义Session的方式.用于构建自定义会话存储提供程序代码,而不是使用默认的 SessionStore 介绍 背景 本文使用的是m ...

  3. 服务端事件EventSource揭秘

    服务端推 服务端推,指的是由服务器主动的向客户端发送消息(响应).在应用层的HTTP协议实现中,"请求-响应"是一个round trip,它的起点来自客户端,因此在应用层之上无法实 ...

  4. java自定义事件机制分析

    import java.util.ArrayList; import java.util.EventListener; import java.util.EventObject; import jav ...

  5. Qt 学习之路 2(53):自定义拖放数据

    Qt 学习之路 2(53):自定义拖放数据 豆子  2013年5月26日  Qt 学习之路 2  13条评论上一章中,我们的例子使用系统提供的拖放对象QMimeData进行拖放数据的存储.比如使用QM ...

  6. 【JavaScript】论一个低配版Web实时通信库是如何实现的之二( EventSource篇)

    前情提要 「 话说上回说到!那WebSocket大侠,巧借http之内力,破了敌阵的双工鸳鸯锁,终于突出重围. 然而玄难未了,此时web森林中飞出一只银头红缨枪,划破夜色. "莫非!?&qu ...

  7. EventSource 实时传输数据

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. k8s自定义controller设计与实现

    k8s自定义controller设计与实现 创建CRD 登录可以执行kubectl命令的机器,创建student.yaml apiVersion: apiextensions.k8s.io/v1bet ...

  9. 关于Unity3D自定义编辑器的学习

    被人物编辑器折腾了一个月,最终还是交了点成品上去(还要很多优化都还么做).  刚接手这项工作时觉得没概念,没想法,不知道.后来就去看<<Unity5.X从入门到精通>>中有关于 ...

随机推荐

  1. nodeJS详解2

    Nodejs应用场景 创建应用服务 web开发 接口开发 客户端应用工具  gulp webpack vue脚手架 react脚手架 小程序 NodeJs基于 Commonjs模块化开发的规范,它定义 ...

  2. Vue中的Xss构造

    首发tools:https://www.t00ls.net/thread-59512-1-1.html 存储型XSS 最近做测试的时候碰到了一个前端页面使用了Vue框架的项目 在测试XSS漏洞的过程中 ...

  3. 「HTML+CSS」--自定义加载动画【006】

    前言 Hello!小伙伴! 首先非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出- 哈哈 自我介绍一下 昵称:海轰 标签:程序猿一只|C++选手|学生 简介:因C语言结识编程,随后转入计算机 ...

  4. 极简实用的Asp.NetCore模块化框架决定免费开源了

    背景 在开发这个框架之前,前前后后看过好几款模块化的框架,最后在一段时间内对ABP VNext痛下狠心,研究一段时间后,不得不说 ABP VNext的代码层面很规范,也都是一些最佳实践,开发出一个模块 ...

  5. 热更新解决方案--xlua学习笔记

    一.热更新方案简介 在Unity游戏工程中,C#代码(编译型语言)资源和Resources文件夹下的资源打包后都不可以更改,因此这部分内容不能进行热更新,而lua代码(解释型语言)逻辑不需要进行预编译 ...

  6. 百度开源中国(Java)面经

    一.自我介绍 面试嘛,万年不变还是自我介绍,就说说你是干嘛的(专业是啥),为什么会选择该公司(说一说自己为何向往Java开发),再谈谈自己的优点(兴趣爱好).如果人家叫停了,就别一股脑接着讲了,停下来 ...

  7. Kubernetes部署metrics-server提示健康检测报错500,简单解决方式

    为什么写? 最近有项目要用到HPA(Horizontal Pod Autoscaler)依赖了k8s的 metrics 指标才能做出自动缩扩容的动作,我这边用官方GitHub v0.4.2版本启动不起 ...

  8. Java中的绑定

    1 前言 做了一次笔试题,发现了一题问答题,是关于Java绑定的: 当时做的时候是完全不会的... 于是这里补上一篇Java绑定的文章. 2 绑定 首先来了解一下绑定的概念.绑定是指一个方法的调用与方 ...

  9. (一)LDAP 简介

    一  LDAP  简介 LDAP是一种通讯协议,LDAP支持TCP/IP.协议就是标准,并且是抽象的.在这套标准下,AD(Active Directory)是微软出的一套实现.    AD 暂且把它理 ...

  10. java中的|=符号

    今天看mybaits源码的时候,发现了这个特殊的符号用法,|=. for (ParameterMapping pm : boundSql.getParameterMappings()) { Strin ...