prometheus-net.DotNetRuntime 获取 CLR 指标原理解析
prometheus-net.DotNetRuntime 介紹
Intro
前面集成 Prometheus 的文章中簡單提到過,prometheus-net.DotNetRuntime 可以獲取到一些 CLR 的數(shù)據(jù),比如說 GC, ThreadPool, Contention, JIT 等指標(biāo),而這些指標(biāo)可以很大程度上幫助我們解決很多問題,比如應(yīng)用執(zhí)行過程中是否經(jīng)常發(fā)生 GC,GC 等待時(shí)間時(shí)間是否過長,是否有發(fā)生死鎖或競爭鎖時(shí)間過長,是否有發(fā)生線程池餓死等等一系列問題,有了這些指標(biāo)我們就可以清晰的在運(yùn)行時(shí)了解到這些信息。
來看一下官方介紹
A plugin for the prometheus-net package, exposing .NET core runtime metrics including:
Garbage collection collection frequencies and timings by generation/ type, pause timings and GC CPU consumption ratio
Heap size by generation
Bytes allocated by small/ large object heap
JIT compilations and JIT CPU consumption ratio
Thread pool size, scheduling delays and reasons for growing/ shrinking
Lock contention
Exceptions thrown, broken down by type
These metrics are essential for understanding the peformance of any non-trivial application. Even if your application is well instrumented, you're only getting half the story- what the runtime is doing completes the picture.
支持的指標(biāo)
Contention Events
只要運(yùn)行時(shí)使用的 System.Threading.Monitor 鎖或 Native鎖出現(xiàn)爭用情況,就會(huì)引發(fā)爭用事件。
一個(gè)線程等待的鎖被另一線程占有時(shí)將發(fā)生爭用。
| dotnet_contention_seconds_total | 發(fā)生鎖爭用的耗時(shí)(秒)總計(jì) | Counter |
| dotnet_contention_total | 鎖爭用獲得鎖的數(shù)量總計(jì) | Counter |
Thread Pool Events
Worker thread 線程池和 IO thread 線程池信息
| dotnet_threadpool_num_threads | 線程池中活躍的線程數(shù)量 | Gauge |
| dotnet_threadpool_io_num_threads | IO 線程池中活躍線程數(shù)量(WindowsOnly) | Gauge |
| dotnet_threadpool_adjustments_total | 線程池中線程調(diào)整總計(jì) | Counter |
Garbage Collection Events
Captures information pertaining to garbage collection, to help in diagnostics and debugging.
| dotnet_gc_collection_seconds | 執(zhí)行 GC 回收過程耗費(fèi)的時(shí)間(秒) | Histogram |
| dotnet_gc_pause_seconds | GC 回收造成的 Pause 耗費(fèi)的時(shí)間(秒) | Histogram |
| dotnet_gc_collection_reasons_total | 觸發(fā) GC 垃圾回收的原因統(tǒng)計(jì) | Counter |
| dotnet_gc_cpu_ratio | 運(yùn)行垃圾收集所花費(fèi)的進(jìn)程CPU時(shí)間的百分比 | Gauge |
| dotnet_gc_pause_ratio | 進(jìn)程暫停進(jìn)行垃圾收集所花費(fèi)的時(shí)間百分比 | Gauge |
| dotnet_gc_heap_size_bytes | 當(dāng)前各個(gè) GC 堆的大小 (發(fā)生垃圾回收之后才會(huì)更新) | Gauge |
| dotnet_gc_allocated_bytes_total | 大小對(duì)象堆上已分配的字節(jié)總數(shù)(每100 KB分配更新) | Counter |
| dotnet_gc_pinned_objects | pinned 對(duì)象的數(shù)量 | Gauge |
| dotnet_gc_finalization_queue_length | 等待 finalize 的對(duì)象數(shù) | Gauge |
JIT Events
| dotnet_jit_method_total | JIT編譯器編譯的方法總數(shù) | Counter |
| dotnet_jit_method_seconds_total | JIT編譯器中花費(fèi)的總時(shí)間(秒) | Counter |
| dotnet_jit_cpu_ratio | JIT 花費(fèi)的 CPU 時(shí)間 | Gauge |
集成方式
上面的列出來的指標(biāo)是我覺得比較重要的指標(biāo),還有一些 ThreadPool Scheduling 的指標(biāo)和 CLR Exception 的指標(biāo)我覺得意義不是特別大,有需要的可以去源碼里看一看
集成的方式有兩種,一種是作者提供了一個(gè)默認(rèn)的 Collector 會(huì)去收集所有支持的 CLR 指標(biāo)信息,另外一種則是可以自己自定義的要收集的 CLR 指標(biāo)類型,來看示例:
使用默認(rèn)的 Collector 收集 CLR 指標(biāo)
DotNetRuntimeStatsBuilder.Default().StartCollecting();使用自定義的 Collector 收集 CLR 指標(biāo)
DotNetRuntimeStatsBuilder.Customize().WithContentionStats()?//?Contention?event.WithGcStats()?//?GC?指標(biāo).WithThreadPoolStats()?//?ThreadPool?指標(biāo)//?.WithCustomCollector(null)?//?你可以自己實(shí)現(xiàn)一個(gè)自定義的?Collector.StartCollecting();上面提到過默認(rèn)的 Collector 會(huì)收集支持的所有的 CLR 指標(biāo),且看源碼怎么做的
構(gòu)建了一個(gè) Builder 通過建造者模式來構(gòu)建復(fù)雜配置的收集器,類似于 .net core 里的 HostBuilder/LoggingBuilder ...,像極了 Host.CreateDefaultBuilder,做了一些變形
源碼地址:https://github.com/djluck/prometheus-net.DotNetRuntime/blob/master/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs
實(shí)現(xiàn)原理
那它是如何工作的呢,如何實(shí)現(xiàn)捕獲 CLR 的指標(biāo)的呢,下面我們就來解密一下,
在項(xiàng)目 README 里已經(jīng)有了簡單的介紹,是基于 CLR 的 ETW Events 來實(shí)現(xiàn)的,具體的 CLR 支持的 ETW Events 可以參考文檔:https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-events
而 ETW Events 是通過 EventSource 的方式使得我們可以在進(jìn)程外獲取到進(jìn)程的一些運(yùn)行信息,這也是我們可以通過 PerfMonitor/PerfView 等方式進(jìn)程外獲取進(jìn)程 CLR 信息的重要實(shí)現(xiàn)方式,同樣的微軟的新的診斷工具 dotnet diagnostic tools 的實(shí)現(xiàn)方式 EventPipe 也是基于 EventSOurce 的
而 EventSource 的事件不僅僅可以通過進(jìn)程外的這些工具來消費(fèi),我們也可以在應(yīng)用程序中實(shí)現(xiàn) EventListener 來實(shí)現(xiàn)進(jìn)程內(nèi)的 EventSource 事件消費(fèi),而這就是 prometheus-net.DotNetRuntime 這個(gè)庫的實(shí)現(xiàn)本質(zhì)方法
可以參考源碼:https://github.com/djluck/prometheus-net.DotNetRuntime/blob/master/src/prometheus-net.DotNetRuntime/DotNetEventListener.cs
具體的事件處理是在對(duì)應(yīng)的 Collector 中:
https://github.com/djluck/prometheus-net.DotNetRuntime/tree/master/src/prometheus-net.DotNetRuntime/StatsCollectors
Metrics Samples
為了比較直觀的看到這些指標(biāo)可以帶來的效果,分享一下我的應(yīng)用中用到的一些 dashboard 截圖
Lock Contention
GC
從上面的圖可以清晰的看到這個(gè)時(shí)間點(diǎn)發(fā)生了一次垃圾回收,此時(shí) GC Heap 的大小和 GC 垃圾回收的CPU 占用率和耗時(shí)都可以大概看的出來,對(duì)于我們運(yùn)行時(shí)診斷應(yīng)用程序問題會(huì)很有幫助
Thread
Thread 的信息還可以拿到一些 threadpool 線程調(diào)度的數(shù)量以及延遲,這里沒有展示出來,
目前我主要關(guān)注的是線程池中線程的數(shù)量和線程池線程調(diào)整的原因,線程池線程調(diào)整的原因中有一個(gè)是 starvation,這個(gè)指標(biāo)尤其需要關(guān)注一下,應(yīng)避免出現(xiàn) threadpool starvation 的情況,出現(xiàn)這個(gè)的原因通常是因?yàn)橛幸恍┎划?dāng)?shù)挠梅?#xff0c;如:Task.Wait、Task.Result、await Task.Run() 來把一個(gè)同步方法變成異步等不好的用法導(dǎo)致的
DiagnosticSource
除了 EventSource 之外,還有一個(gè) DiagnosticSource 可以幫助我們?cè)\斷應(yīng)用程序的性能問題,目前微軟也是推薦類庫中使用 DiagnosticSource 的方式來讓應(yīng)用診斷類庫中的一些性能問題,這也是目前大多數(shù) APM 實(shí)現(xiàn)的機(jī)制,Skywalking、ElasticAPM、OpenTelemetry 等都使用了 DiagnosticSource 的方式來實(shí)現(xiàn)應(yīng)用程序的性能診斷
如果是進(jìn)程外應(yīng)用程序的性能診斷推薦首選 EventSource,如果是進(jìn)程內(nèi)推薦首選 DiagnosticSource
通常我們都應(yīng)該使用 DiagnosticSource,即使想進(jìn)程外捕獲,也是可以做到的
關(guān)于這二者的使用,可以看一下這個(gè) Comment https://github.com/dotnet/aspnetcore/issues/2312#issuecomment-359514074
More
除了上面列出來的那些指標(biāo)還有一些指標(biāo),比如 exception,threadpool scheduling,還有當(dāng)前 dotnet 的環(huán)境(系統(tǒng)版本,GC 類型,Runtime 版本,程序 TargetFramework,CPU 數(shù)量等),有興趣的可以用一下試一下
exception 指標(biāo)使用下來感覺幫助不大,有一些即使是已經(jīng)處理的或者忽略的 Exception 也會(huì)被統(tǒng)計(jì),這些 Exception 大多并不會(huì)影響應(yīng)用程序的運(yùn)行,如果參考這個(gè)的話可能會(huì)帶來很多的困擾,所以我覺得還是需要應(yīng)用程序來統(tǒng)計(jì) exception 指標(biāo)更為合適一些
prometheus-net.DotNetRuntime 作為 prometheus-net 的一個(gè)插件,依賴于 prometheus-net 去寫 metrics 信息,也就是說 metrics 的信息可以通過 prometheus-net 來獲取
集成 asp.net core 的時(shí)候和之前集成 prometheus-net 是一樣的,metrics path 是同一個(gè),可以參考我這個(gè)項(xiàng)目: https://github.com/OpenReservation/ReservationServer/tree/dev/OpenReservation
注意:作者推薦 .netcore3.0 以上使用,.netcore 2.x 會(huì)有一些 BUG,可以在 Issue 里看到
Reference
https://github.com/djluck/prometheus-net.DotNetRuntime
https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-events
https://github.com/dotnet/aspnetcore/issues/2312#issuecomment-359514074
https://github.com/OpenReservation/ReservationServer
總結(jié)
以上是生活随笔為你收集整理的prometheus-net.DotNetRuntime 获取 CLR 指标原理解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 理解 redis 中的 哈希对象类型
- 下一篇: 这个世界,正在悄悄惩罚那些不注意身体的人