日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

利用AppMetrics对Web进行监控教程

發(fā)布時(shí)間:2023/12/4 编程问答 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用AppMetrics对Web进行监控教程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、基礎(chǔ)準(zhǔn)備

1. 安裝依賴

這里可以通過nuget或使用命令行進(jìn)行安裝,具體需要安裝的類庫如下(注意版本):

Install-Package App.Metrics.AspNetCore.Mvc -Version 2.0.0

由于我們需要兼容Prometheus進(jìn)行監(jiān)控,所以我們還需要安裝對(duì)應(yīng)的格式化庫,具體如下:

Install-Package App.Metrics.Formatters.Prometheus -Version 2.0.0

以上就是需要的類庫,接下來我們開始進(jìn)行其他初始化部分。

2. 初始配置

為了保證其能夠正常工作,我們需要根據(jù)不同的環(huán)境設(shè)定對(duì)應(yīng)的appsettings.json文件從而讓度量指標(biāo)可以根據(jù)不同的環(huán)境進(jìn)行輸出,這里考慮到實(shí)際情況尚未存在不同的配置可能性故統(tǒng)一配置即可,打開appsettings.json輸入下配置項(xiàng):

{"MetricsOptions": {"DefaultContextLabel": "MetricsApplication","Enabled": true},"MetricsWebTrackingOptions": {"ApdexTrackingEnabled": true,"ApdexTSeconds": 0.3,"IgnoredHttpStatusCodes": [ 404 ],"IgnoreRoutesRegexPatterns": [],"OAuth2TrackingEnabled": false},"MetricEndpointsOptions": {"MetricsEndpointEnabled": true,"MetricsTextEndpointEnabled": true,"EnvironmentInfoEndpointEnabled": true} }

參數(shù)DefaultContextLabel可以設(shè)定為我們期望其他名稱,這里建議采用項(xiàng)目的簡寫名稱,保證項(xiàng)目之間不存在沖突即可。參數(shù)ApdexTSeconds用于設(shè)定應(yīng)用的響應(yīng)能力標(biāo)準(zhǔn),其采用了當(dāng)前流行的Apdex標(biāo)準(zhǔn),這里使用者可以根據(jù)自身應(yīng)用的實(shí)際情況調(diào)整對(duì)應(yīng)的參數(shù),其他相關(guān)參數(shù)建議默認(rèn)即可。

3. 啟用度量指標(biāo)

因?yàn)槲覀兊臄?shù)據(jù)需要符合Promethues格式,所以后續(xù)教程我們會(huì)替換默認(rèn)的格式采用符合的格式。首先我們需要Program.cs里輸入以下內(nèi)容:

public static IWebHost BuildWebHost(string[] args){Metrics = AppMetrics.CreateDefaultBuilder().OutputMetrics.AsPrometheusPlainText().OutputMetrics.AsPrometheusProtobuf().Build();return WebHost.CreateDefaultBuilder(args).ConfigureMetrics(Metrics).UseMetrics(options =>{options.EndpointOptions = endpointsOptions =>{endpointsOptions.MetricsTextEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusTextOutputFormatter>().First();endpointsOptions.MetricsEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusProtobufOutputFormatter>().First();};}).UseStartup<Startup>().Build();}

其中為了能夠支持其他格式,我們需要手動(dòng)實(shí)例化Metrics對(duì)象完成相關(guān)初始化然后將其注入到asp.net core中,其中相關(guān)格式的代碼主要是由以下這幾部分組成:

OutputMetrics.AsPrometheusPlainText() OutputMetrics.AsPrometheusProtobuf()endpointsOptions.MetricsTextEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusTextOutputFormatter>().First(); endpointsOptions.MetricsEndpointOutputFormatter = Metrics.OutputMetricsFormatters.OfType<MetricsPrometheusProtobufOutputFormatter>().First();

完成以上操作后,我們最后還需要進(jìn)行其他配置,打開Startup.cs文件增加如下內(nèi)容:

services.AddMvc().AddMetrics();

至此我們就完成了基本的初始化了,通過啟動(dòng)程序并訪問localhost:5000/metrics-text即可查看最終的輸出內(nèi)容。

二、自定義指標(biāo)

由于其內(nèi)部已經(jīng)默認(rèn)提供了若干的指標(biāo),但是并不能符合實(shí)際業(yè)務(wù)的需求故以下將對(duì)常用的度量指標(biāo)類型以及用法進(jìn)行介紹,這里這里大家通過注入IMetrics接口對(duì)象即可訪問,所以下面這部分代碼不在闡述。

1. 儀表盤(Gauge)

最常見的類型,主要是用于直接反應(yīng)當(dāng)前的指標(biāo)情況,比如我們常見的CPU和內(nèi)存基本都是使用這種方式進(jìn)行顯示的,可以直觀的看到當(dāng)前的實(shí)際的狀態(tài)情況。對(duì)于所有的指標(biāo)我們都需要定義對(duì)應(yīng)的Options,當(dāng)然這可以完成攜程靜態(tài)變量供應(yīng)用程序全局使用。

比如下面我們定義一個(gè)表示當(dāng)前發(fā)生錯(cuò)誤次數(shù)的指標(biāo):

GaugeOptions Errors = new GaugeOptions() {Name = "Errors" };

完成指標(biāo)的定義后,我們就可以在需要使用的地方進(jìn)行指標(biāo)數(shù)據(jù)的修改,比如下面我們將錯(cuò)誤數(shù)量設(shè)置為10:

metrics.Measure.Gauge.SetValue(MyMetricsRegistry.Errors, 10);

這樣我們就完成了指標(biāo)的設(shè)定,但是有時(shí)候我們還想?yún)s分具體的Error是那個(gè)層面發(fā)起的,這個(gè)時(shí)候我們需要使用到Tag了,下面我們?cè)谠O(shè)定值的同時(shí)設(shè)定指標(biāo),當(dāng)然也可以在新建指標(biāo)的時(shí)候通過Tags變量,并且通用于其他所有指標(biāo):

var tags = new MetricTags("fromt", "db"); metrics.Measure.Gauge.SetValue(MyMetricsRegistry.Errors, tags, 10);

至此我們就完成了一個(gè)基本的指標(biāo),下面我們繼續(xù)其他類型指標(biāo)。

2. 計(jì)數(shù)值(Counter)

對(duì)于HTTP類型的網(wǎng)站來說,存在非常多的數(shù)量需要統(tǒng)計(jì)記錄,所以計(jì)數(shù)值此時(shí)就特別適合這類情況,比如我們需要統(tǒng)計(jì)請(qǐng)求數(shù)量就可以利用這類指標(biāo)類型,下面我們就以請(qǐng)求數(shù)來定義這個(gè)指標(biāo):

var requestCounter = new CounterOptions() {Name = "httpRequest",MeasurementUnit = Unit.Calls };

以上我們定義了一個(gè)計(jì)數(shù)指標(biāo),其中我們可以看到我們這里使用了一個(gè)新變量MeasurementUnit主要是用于定義指標(biāo)單位的,當(dāng)然這個(gè)只是輔助信息會(huì)一同輸出到結(jié)果,下面我們需要進(jìn)行增加和減少,考慮到大多數(shù)情況都是減1和增1的情況:

metrics.Measure.Counter.Increment(requestCounter);

實(shí)際情況可能我們都是統(tǒng)計(jì)請(qǐng)求但是期望還能單獨(dú)統(tǒng)計(jì)特定接口的請(qǐng)求,這個(gè)時(shí)候我們?cè)谠菊{(diào)用方式基礎(chǔ)上增加額外的參數(shù):

metrics.Measure.Counter.Increment(requestCounter, "api");

如果嫌每次增長1比較慢,我們通過其函數(shù)的重載形式填寫我們希望增長的具體值。

3. 計(jì)量值(Meter)

有點(diǎn)類似于計(jì)數(shù)值但是相比來說,它可以提供更加豐富的信息,比如每1、5、15分鐘的增長率等,所以對(duì)于一些需要通過增長率觀察的數(shù)據(jù)特別時(shí)候,這里我們以請(qǐng)求的反應(yīng)狀態(tài)碼進(jìn)行記錄來體現(xiàn)其用途:

var httpStatusMeter = new MeterOptions() {Name = "Http Status",MeasurementUnit = Unit.Calls };

以上我們完成了一個(gè)指標(biāo)的定義,下面我們開始使用其并且定義不同的狀態(tài)的碼的發(fā)生情況,具體如下:

metrics.Measure.Meter.Mark(httpStatusMeter, "200"); metrics.Measure.Meter.Mark(httpStatusMeter, "500"); metrics.Measure.Meter.Mark(httpStatusMeter, "401");

當(dāng)然如果希望增加的數(shù)量自定控制也可以使用其提供的重載形式進(jìn)行。

4. 柱狀圖(Histogram)

顧名思義,主要反應(yīng)數(shù)據(jù)的分布情況,所以這里不在重復(fù)闡述,大家對(duì)于這種數(shù)據(jù)表現(xiàn)形式還是比較了解的,所以下面就直接以實(shí)際代碼的實(shí)列進(jìn)行介紹,便于大家的理解:

var postAndPutRequestSize = new HistogramOptions() {Name = "Web Request Post & Put Size",MeasurementUnit = Unit.Bytes };

以上我們定義一個(gè)體現(xiàn)Post和Put請(qǐng)求的數(shù)據(jù)尺寸的指標(biāo),下面我們利用隨機(jī)數(shù)來進(jìn)行數(shù)據(jù)的模擬對(duì)其進(jìn)行數(shù)據(jù)填充,便于顯示數(shù)據(jù):

var rnd = new Random();foreach (var i in Enumerable.Range(0, 50)) {var t = rnd.Next(0, 10);metrics.Measure.Histogram.Update(postAndPutRequestSize, t); }

5. 時(shí)間線(Timer)

對(duì)應(yīng)指標(biāo)的監(jiān)控閉然少不了對(duì)于時(shí)間的記錄,特別對(duì)于HTTP來說,直接影響到用戶的體驗(yàn)就是響應(yīng)時(shí)間,素以我們也需要時(shí)刻關(guān)于這類指標(biāo)的變化情況及時(shí)做出反應(yīng),下面我們就以數(shù)據(jù)庫的響應(yīng)時(shí)間的情況作為指標(biāo)進(jìn)行監(jiān)控:

TimerOptions DatabaseTimer = new TimerOptions() {Name = "Database Timer",MeasurementUnit = Unit.Items,DurationUnit = TimeUnit.Milliseconds,RateUnit = TimeUnit.Milliseconds };

上面我們通過特別的屬性指定了改指標(biāo)記錄時(shí)間的單位,下面我們使用其指標(biāo)進(jìn)行數(shù)據(jù)的記錄:

using(metrics.Measure.Timer.Time(DatabaseTimer)) {//to do sonmething }

我們可以看到為了方便的記錄請(qǐng)求的時(shí)間,我們使用using進(jìn)行涵括,并將需要記錄耗時(shí)的請(qǐng)求操作放入其中,在請(qǐng)求完成操作后就可以正確的記錄其需要的時(shí)間。

6. apdex

采用了一種標(biāo)準(zhǔn)的性能指標(biāo)計(jì)算方式,用法類似與上述,這里僅僅列舉用法:

ApdexOptions SampleApdex = new ApdexOptions {Name = "Sample Apdex" };using(metrics.Measure.Apdex.Track(SampleApdex)) {Thread.Sleep(100); }

三、高級(jí)指標(biāo)

1. 平均響應(yīng)

很多時(shí)候我們僅僅依靠一個(gè)指標(biāo)很難完成一個(gè)實(shí)際的需求,是所以我們就需要將多個(gè)指標(biāo)進(jìn)行組合進(jìn)行,比如我們期望得到請(qǐng)求次數(shù),同時(shí)還有請(qǐng)求的總時(shí)間和平均響應(yīng)時(shí)間等,為此我們可以特殊的指標(biāo)將多個(gè)指標(biāo)進(jìn)行組合,具體操作如下:

var cacheHitRatioGauge = new GaugeOptions {Name = "Cache Gauge",MeasurementUnit = Unit.Calls };var cacheHitsMeter = new MeterOptions {Name = "Cache Hits Meter",MeasurementUnit = Unit.Calls };var databaseQueryTimer = new TimerOptions {Name = "Database Query Timer",MeasurementUnit = Unit.Calls,DurationUnit = TimeUnit.Milliseconds,RateUnit = TimeUnit.Milliseconds };var cacheHits = metrics.Provider.Meter.Instance(cacheHitsMeter); var calls = metrics.Provider.Timer.Instance(databaseQueryTimer);var cacheHit = new Random().Next(0, 2) == 0;using(calls.NewContext()) {if (cacheHit){cacheHits.Mark(5);}Thread.Sleep(cacheHit ? 10 : 100); }metrics.Measure.Gauge.SetValue(cacheHitRatioGauge, () => new HitRatioGauge(cacheHits, calls, m => m.OneMinuteRate));

四、利用Promethues和Grafana進(jìn)行監(jiān)控

1. 環(huán)境準(zhǔn)備

這里需要使用到Prometheus和Grafana,為了避免版本導(dǎo)致的區(qū)別這里提供了對(duì)應(yīng)百度云的下載地址,大家可以自行進(jìn)行下載。

Prometheus對(duì)應(yīng)提取碼為2b1r

Grafana對(duì)應(yīng)提取碼為mjym

完成以上下載后需要解壓到對(duì)應(yīng)文件夾下即可。

2. 配置服務(wù)

首先我們需要針對(duì)Prometheus進(jìn)行配置,我們打開prometheus.yml文件新增基于AppMetrics的監(jiān)控指標(biāo)。

- job_name: 'appweb'scrape_interval: 5smetrics_path: '/metrics-text'static_configs:- targets: ['localhost:5000']

完成之后我們可以先打開采集讓其在后臺(tái)持續(xù)采集,后面我們需要針對(duì)AppMetrics暴露的數(shù)據(jù)進(jìn)行調(diào)整。

3. 應(yīng)用指標(biāo)輸出

通過實(shí)際的測(cè)試發(fā)現(xiàn)基于2.0.0版本的Prometheus存在問題,因?yàn)橹笜?biāo)類型被大寫了,導(dǎo)致Prometheus無法正確讀取,所以我們需要將源碼復(fù)制出來進(jìn)行操作,這里直接給出了對(duì)應(yīng)的源碼文件,
主要的工作就是將AsciiFormatter.cs中的HELP和TYPE進(jìn)行了小寫而已,對(duì)應(yīng)文件如下。

PS:考慮到很多基于2.0的所以這里保留了基于HTTP的文本實(shí)現(xiàn)方式發(fā)布了一個(gè)對(duì)應(yīng)的版本庫:

Install-Package Sino.Metrics.Formatters.Prometheus -Version 0.1.2
  • AsciiFormatter.cs

internal static class AsciiFormatter{public static void Format(Stream destination, IEnumerable<MetricFamily> metrics){var metricFamilys = metrics.ToArray();using (var streamWriter = new StreamWriter(destination, Encoding.UTF8)){streamWriter.NewLine = "\n";foreach (var metricFamily in metricFamilys){WriteFamily(streamWriter, metricFamily);}}}internal static string Format(IEnumerable<MetricFamily> metrics, NewLineFormat newLine){var newLineChar = GetNewLineChar(newLine);var metricFamilys = metrics.ToArray();var s = new StringBuilder();foreach (var metricFamily in metricFamilys){s.Append(WriteFamily(metricFamily, newLineChar));}return s.ToString();}private static void WriteFamily(StreamWriter streamWriter, MetricFamily metricFamily){streamWriter.WriteLine("# HELP {0} {1}", metricFamily.name, metricFamily.help.ToLower());streamWriter.WriteLine("# TYPE {0} {1}", metricFamily.name, metricFamily.type.ToString().ToLower());foreach (var metric in metricFamily.metric){WriteMetric(streamWriter, metricFamily, metric);}}private static string WriteFamily(MetricFamily metricFamily, string newLine){var s = new StringBuilder();s.Append(string.Format("# HELP {0} {1}", metricFamily.name, metricFamily.help.ToLower()), newLine);s.Append(string.Format("# TYPE {0} {1}", metricFamily.name, metricFamily.type.ToString().ToLower()), newLine);foreach (var metric in metricFamily.metric){s.Append(WriteMetric(metricFamily, metric, newLine), newLine);}return s.ToString();}private static void WriteMetric(StreamWriter streamWriter, MetricFamily family, Metric metric){var familyName = family.name;if (metric.gauge != null){streamWriter.WriteLine(SimpleValue(familyName, metric.gauge.value, metric.label));}else if (metric.counter != null){streamWriter.WriteLine(SimpleValue(familyName, metric.counter.value, metric.label));}else if (metric.summary != null){streamWriter.WriteLine(SimpleValue(familyName, metric.summary.sample_sum, metric.label, "_sum"));streamWriter.WriteLine(SimpleValue(familyName, metric.summary.sample_count, metric.label, "_count"));foreach (var quantileValuePair in metric.summary.quantile){var quantile = double.IsPositiveInfinity(quantileValuePair.quantile)? "+Inf": quantileValuePair.quantile.ToString(CultureInfo.InvariantCulture);streamWriter.WriteLine(SimpleValue(familyName,quantileValuePair.value,metric.label.Concat(new[] { new LabelPair { name = "quantile", value = quantile } })));}}else if (metric.histogram != null){streamWriter.WriteLine(SimpleValue(familyName, metric.histogram.sample_sum, metric.label, "_sum"));streamWriter.WriteLine(SimpleValue(familyName, metric.histogram.sample_count, metric.label, "_count"));foreach (var bucket in metric.histogram.bucket){var value = double.IsPositiveInfinity(bucket.upper_bound) ? "+Inf" : bucket.upper_bound.ToString(CultureInfo.InvariantCulture);streamWriter.WriteLine(SimpleValue(familyName,bucket.cumulative_count,metric.label.Concat(new[] { new LabelPair { name = "le", value = value } }),"_bucket"));}}else{// not supported}}private static string WriteMetric(MetricFamily family, Metric metric, string newLine){var s = new StringBuilder();var familyName = family.name;if (metric.gauge != null){s.Append(SimpleValue(familyName, metric.gauge.value, metric.label), newLine);}else if (metric.counter != null){s.Append(SimpleValue(familyName, metric.counter.value, metric.label), newLine);}else if (metric.summary != null){s.Append(SimpleValue(familyName, metric.summary.sample_sum, metric.label, "_sum"), newLine);s.Append(SimpleValue(familyName, metric.summary.sample_count, metric.label, "_count"), newLine);foreach (var quantileValuePair in metric.summary.quantile){var quantile = double.IsPositiveInfinity(quantileValuePair.quantile)? "+Inf": quantileValuePair.quantile.ToString(CultureInfo.InvariantCulture);s.Append(SimpleValue(familyName,quantileValuePair.value,metric.label.Concat(new[] { new LabelPair { name = "quantile", value = quantile } })), newLine);}}else if (metric.histogram != null){s.Append(SimpleValue(familyName, metric.histogram.sample_sum, metric.label, "_sum"), newLine);s.Append(SimpleValue(familyName, metric.histogram.sample_count, metric.label, "_count"), newLine);foreach (var bucket in metric.histogram.bucket){var value = double.IsPositiveInfinity(bucket.upper_bound) ? "+Inf" : bucket.upper_bound.ToString(CultureInfo.InvariantCulture);s.Append(SimpleValue(familyName,bucket.cumulative_count,metric.label.Concat(new[] { new LabelPair { name = "le", value = value } }),"_bucket"), newLine);}}else{// not supported}return s.ToString();}private static string WithLabels(string familyName, IEnumerable<LabelPair> labels){var labelPairs = labels as LabelPair[] ?? labels.ToArray();if (labelPairs.Length == 0){return familyName;}return string.Format("{0}{{{1}}}", familyName, string.Join(",", labelPairs.Select(l => string.Format("{0}=\"{1}\"", l.name, l.value))));}private static string SimpleValue(string family, double value, IEnumerable<LabelPair> labels, string namePostfix = null){return string.Format("{0} {1}", WithLabels(family + (namePostfix ?? string.Empty), labels), value.ToString(CultureInfo.InvariantCulture));}private static string GetNewLineChar(NewLineFormat newLine){switch (newLine){case NewLineFormat.Auto:return Environment.NewLine;case NewLineFormat.Windows:return "\r\n";case NewLineFormat.Unix:case NewLineFormat.Default:return "\n";default:throw new ArgumentOutOfRangeException(nameof(newLine), newLine, null);}}private static void Append(this StringBuilder sb, string line, string newLineChar){sb.Append(line + newLineChar);}}
  • MetricsPrometheusTextOutputFormatter.cs

public class MetricsPrometheusTextOutputFormatter : IMetricsOutputFormatter{private readonly MetricsPrometheusOptions _options;public MetricsPrometheusTextOutputFormatter(){_options = new MetricsPrometheusOptions();}public MetricsPrometheusTextOutputFormatter(MetricsPrometheusOptions options) { _options = options ?? throw new ArgumentNullException(nameof(options)); }/// <inheritdoc/>public MetricsMediaTypeValue MediaType => new MetricsMediaTypeValue("text", "vnd.appmetrics.metrics.prometheus", "v1", "plain");/// <inheritdoc/>public async Task WriteAsync(Stream output,MetricsDataValueSource metricsData,CancellationToken cancellationToken = default(CancellationToken)){if (output == null){throw new ArgumentNullException(nameof(output));}using (var streamWriter = new StreamWriter(output)){await streamWriter.WriteAsync(AsciiFormatter.Format(metricsData.GetPrometheusMetricsSnapshot(_options.MetricNameFormatter), _options.NewLineFormat));}}}

新建好以上兩個(gè)文件后我們接著需要修改Program.cs文件,具體內(nèi)容如下:

public static IWebHost BuildWebHost(string[] args){Metrics = AppMetrics.CreateDefaultBuilder().OutputMetrics.AsPrometheusPlainText().Build();return WebHost.CreateDefaultBuilder(args).ConfigureMetrics(Metrics).UseMetrics(options =>{options.EndpointOptions = endpointsOptions =>{endpointsOptions.MetricsTextEndpointOutputFormatter = new MetricsPrometheusTextOutputFormatter();};}).UseStartup<Startup>().Build();}

完成以上操作后我們可以啟用應(yīng)用,此時(shí)可以看到不斷用請(qǐng)求到/metrics-text表示已經(jīng)開始采集指標(biāo)了。

4. 指標(biāo)可視化

此時(shí)我們打開Grafana文件夾,通過其中的bin目錄下的grafana-server.exe啟動(dòng)服務(wù),然后訪問localhost:3000利用初始賬戶密碼進(jìn)行登錄(admin/admin)。
進(jìn)入后添加Prometheus數(shù)據(jù)源。由于AppMetrics已經(jīng)提供了對(duì)應(yīng)的看板所以我們可以通過ID2204直接導(dǎo)入,并選擇正確的數(shù)據(jù)源就可以看到最終的效果了。

總結(jié)

以上是生活随笔為你收集整理的利用AppMetrics对Web进行监控教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。