Tutorial: Mengukur performa menggunakan EventCounters di .NET Core

Artikel ini berlaku untuk: ✔️ .NET Core 3.0 SDK dan versi yang lebih baru

Dalam tutorial ini, Anda akan mempelajari bagaimana dapat EventCounter digunakan untuk mengukur performa dengan frekuensi peristiwa yang tinggi. Anda dapat menggunakan penghitung yang tersedia yang diterbitkan oleh berbagai paket .NET Core resmi, penyedia pihak ketiga, atau membuat metrik Anda sendiri untuk pemantauan.

Dalam tutorial ini, Anda akan:

Prasyarat

Tutorial ini menggunakan:

Dapatkan sumbernya

Aplikasi sampel akan digunakan sebagai dasar untuk pemantauan. Sampel ASP.NET repositori Core tersedia dari browser sampel. Anda mengunduh file zip, mengekstraknya setelah diunduh, dan membukanya di IDE favorit Anda. Bangun dan jalankan aplikasi untuk memastikan bahwa aplikasi berfungsi dengan baik, lalu hentikan aplikasi.

Menerapkan EventSource

Untuk peristiwa yang terjadi setiap beberapa milidetik, Anda mungkin ingin overhead per peristiwa rendah (kurang dari milidetik). Jika tidak, dampak pada performa akan signifikan. Mencatat peristiwa berarti Anda akan menulis sesuatu ke disk. Jika disk tidak cukup cepat, Anda akan kehilangan peristiwa. Anda memerlukan solusi selain mencatat peristiwa itu sendiri.

Saat berhadapan dengan sejumlah besar peristiwa, mengetahui ukuran per peristiwa juga tidak berguna. Sebagian besar waktu yang Anda butuhkan hanyalah beberapa statistik dari itu. Jadi Anda bisa mendapatkan statistik dalam proses itu sendiri dan kemudian menulis peristiwa sekali-sekali untuk melaporkan statistik, itulah yang EventCounter akan dilakukan.

Di bawah ini adalah contoh cara mengimplementasikan System.Diagnostics.Tracing.EventSource. Buat file baru bernama MinimalEventCounterSource.cs dan gunakan cuplikan kode sebagai sumbernya:

using System.Diagnostics.Tracing;

[EventSource(Name = "Sample.EventCounter.Minimal")]
public sealed class MinimalEventCounterSource : EventSource
{
    public static readonly MinimalEventCounterSource Log = new MinimalEventCounterSource();

    private EventCounter _requestCounter;

    private MinimalEventCounterSource() =>
        _requestCounter = new EventCounter("request-time", this)
        {
            DisplayName = "Request Processing Time",
            DisplayUnits = "ms"
        };

    public void Request(string url, long elapsedMilliseconds)
    {
        WriteEvent(1, url, elapsedMilliseconds);
        _requestCounter?.WriteMetric(elapsedMilliseconds);
    }

    protected override void Dispose(bool disposing)
    {
        _requestCounter?.Dispose();
        _requestCounter = null;

        base.Dispose(disposing);
    }
}

Baris EventSource.WriteEvent adalah EventSource bagian dan bukan bagian EventCounterdari , itu ditulis untuk menunjukkan bahwa Anda dapat mencatat pesan bersama dengan penghitung peristiwa.

Menambahkan filter tindakan

Kode sumber sampel adalah proyek ASP.NET Core. Anda dapat menambahkan filter tindakan secara global yang akan mencatat total waktu permintaan. Buat file baru bernama LogRequestTimeFilterAttribute.cs, dan gunakan kode berikut:

using System.Diagnostics;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc.Filters;

namespace DiagnosticScenarios
{
    public class LogRequestTimeFilterAttribute : ActionFilterAttribute
    {
        readonly Stopwatch _stopwatch = new Stopwatch();

        public override void OnActionExecuting(ActionExecutingContext context) => _stopwatch.Start();

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            _stopwatch.Stop();

            MinimalEventCounterSource.Log.Request(
                context.HttpContext.Request.GetDisplayUrl(), _stopwatch.ElapsedMilliseconds);
        }
    }
}

Filter tindakan dimulai saat Stopwatch permintaan dimulai, dan berhenti setelah selesai, menangkap waktu yang berlalu. Total milidetik dicatat ke MinimalEventCounterSource instans singleton. Agar filter ini diterapkan, Anda perlu menambahkannya ke koleksi filter. Dalam file Startup.cs , perbarui metode di sertakan ConfigureServices filter ini.

public void ConfigureServices(IServiceCollection services) =>
    services.AddControllers(options => options.Filters.Add<LogRequestTimeFilterAttribute>())
            .AddNewtonsoftJson();

Memantau penghitung peristiwa

Dengan implementasi pada EventSource dan filter tindakan kustom, buat dan luncurkan aplikasi. Anda mencatat metrik ke EventCounter, tetapi kecuali Anda mengakses statistik dari metrik tersebut, metrik tersebut tidak berguna. Untuk mendapatkan statistik, Anda perlu mengaktifkan EventCounter dengan membuat timer yang diaktifkan sesering yang Anda inginkan, serta pendengar untuk menangkap peristiwa. Untuk melakukannya, Anda dapat menggunakan penghitung dotnet.

Gunakan perintah ps penghitung dotnet untuk menampilkan daftar proses .NET yang dapat dipantau.

dotnet-counters ps

Dengan menggunakan pengidentifikasi proses dari output dotnet-counters ps perintah, Anda dapat mulai memantau penghitung peristiwa dengan perintah berikut dotnet-counters monitor :

dotnet-counters monitor --process-id 2196 --counters Sample.EventCounter.Minimal,Microsoft.AspNetCore.Hosting[total-requests,requests-per-second],System.Runtime[cpu-usage]

dotnet-counters monitor Saat perintah berjalan, tahan F5 di browser untuk mulai mengeluarkan permintaan berkelanjutan ke https://localhost:5001/api/values titik akhir. Setelah beberapa detik berhenti dengan menekan q

Press p to pause, r to resume, q to quit.
    Status: Running

[Microsoft.AspNetCore.Hosting]
    Request Rate / 1 sec                               9
    Total Requests                                   134
[System.Runtime]
    CPU Usage (%)                                     13
[Sample.EventCounter.Minimal]
    Request Processing Time (ms)                      34.5

Perintah dotnet-counters monitor ini sangat bagus untuk pemantauan aktif. Namun, Anda mungkin ingin mengumpulkan metrik diagnostik ini untuk pemrosesan dan analisis pasca. Untuk itu, gunakan dotnet-counters collect perintah . Perintah collect switch mirip monitor dengan perintah , tetapi menerima beberapa parameter tambahan. Anda dapat menentukan nama dan format file output yang diinginkan. Untuk file JSON bernama diagnostics.json gunakan perintah berikut:

dotnet-counters collect --process-id 2196 --format json -o diagnostics.json --counters Sample.EventCounter.Minimal,Microsoft.AspNetCore.Hosting[total-requests,requests-per-second],System.Runtime[cpu-usage]

Sekali lagi, saat perintah sedang berjalan, tahan F5 di browser untuk mulai mengeluarkan permintaan berkelanjutan ke https://localhost:5001/api/values titik akhir. Setelah beberapa detik berhenti dengan menekan q. File diagnostics.json ditulis. Namun, file JSON yang ditulis tidak diindentasi; untuk keterbacaan, itu diindentasi di sini.

{
  "TargetProcess": "DiagnosticScenarios",
  "StartTime": "8/5/2020 3:02:45 PM",
  "Events": [
    {
      "timestamp": "2020-08-05 15:02:47Z",
      "provider": "System.Runtime",
      "name": "CPU Usage (%)",
      "counterType": "Metric",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:47Z",
      "provider": "Microsoft.AspNetCore.Hosting",
      "name": "Request Rate / 1 sec",
      "counterType": "Rate",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:47Z",
      "provider": "Microsoft.AspNetCore.Hosting",
      "name": "Total Requests",
      "counterType": "Metric",
      "value": 134
    },
    {
      "timestamp": "2020-08-05 15:02:47Z",
      "provider": "Sample.EventCounter.Minimal",
      "name": "Request Processing Time (ms)",
      "counterType": "Metric",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:47Z",
      "provider": "System.Runtime",
      "name": "CPU Usage (%)",
      "counterType": "Metric",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:48Z",
      "provider": "Microsoft.AspNetCore.Hosting",
      "name": "Request Rate / 1 sec",
      "counterType": "Rate",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:48Z",
      "provider": "Microsoft.AspNetCore.Hosting",
      "name": "Total Requests",
      "counterType": "Metric",
      "value": 134
    },
    {
      "timestamp": "2020-08-05 15:02:48Z",
      "provider": "Sample.EventCounter.Minimal",
      "name": "Request Processing Time (ms)",
      "counterType": "Metric",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:48Z",
      "provider": "System.Runtime",
      "name": "CPU Usage (%)",
      "counterType": "Metric",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:50Z",
      "provider": "Microsoft.AspNetCore.Hosting",
      "name": "Request Rate / 1 sec",
      "counterType": "Rate",
      "value": 0
    },
    {
      "timestamp": "2020-08-05 15:02:50Z",
      "provider": "Microsoft.AspNetCore.Hosting",
      "name": "Total Requests",
      "counterType": "Metric",
      "value": 134
    },
    {
      "timestamp": "2020-08-05 15:02:50Z",
      "provider": "Sample.EventCounter.Minimal",
      "name": "Request Processing Time (ms)",
      "counterType": "Metric",
      "value": 0
    }
  ]
}

Langkah berikutnya