Middlewares
Advanced
Dựa trên thực hành Web API, trong bài viết này chúng ta áp dụng một số middlewares phổ biến trong việc phát triển ứng dụng Microservices.
-
Swashbuckle - mô tả API cung cấp bởi một microservice theo chuẩn OpenAPI. Mô tả này cung cấp thông tin giúp client sử dụng API dễ dàng hơn.
-
Health Check - microservices thông báo trạng thái về một trung tâm giám sát theo chu kì thời gian. Trung tâm tổng hợp báo cáo, gửi cảnh báo khi cần thiết.
-
Prometheus - báo cáo về tần suất, hiệu suất hoạt động của ứng dụng, hoặc một chức năng trong ứng dụng thông qua những tham số đo lường cụ thể - metrics
Nội dung
Swashbuckle
Middleware Swashbuckle
bao gồm ba thành phần:
- SwaggerGen: tạo
SwaggerDocument
từ controllers, routes và models - Swagger: cung cấp JSON endpoint dựa trên
SwaggerDocument
object - SwaggerUI: tạo giao diện UI dựa trên việc biên dịch JSON endpoint
Trong folder RemindersManagement.API
, thực hiện câu lệnh sau để cài đặt package Swashbuckle
:
dotnet add package Swashbuckle.AspNetCore
Cập nhật phương thức trong Startup.cs
:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RemindersManagement.API.Domain.Interfaces;
using RemindersManagement.API.Domain.Services;
using RemindersManagement.API.Infrastructure.Data;
using RemindersManagement.API.Infrastructure.Repositories;
using Serilog;
namespace RemindersManagement.API
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IRemindersRepository, RemindersRepository>();
services.AddScoped<ICategoriesRepository, CategoriesRepository>();
services.AddScoped<IRemindersService, RemindersService>();
services.AddDbContext<RemindersDbContext>(options => {
options.UseSqlite("Data Source=FriendReminders.db");
});
// Register the Swagger generator
services.AddSwaggerGen();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseSerilogRequestLogging();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// Middleware generate JSON endpoint.
app.UseSwagger();
// Middleware generate Swagger-UI
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "RemindersManagement V1");
});
}
}
}
Trong foler gốc của solution thực hiện lệnh:
dotnet run --urls="http://localhost:8000/" --project Services/RemindersManagement/RemindersManagement.API/RemindersManagement.API.csproj
Truy cập http://localhost:8000/swagger/index.html
xác nhận kết quả:
Giao diện Swagger của
RemindersManagement
API
Tip: Để swagger/index.html
được sử dụng mặc định khi truy cập địa chỉ http://localhost:8000/
, chúng ta có thể tạo thêm một HomeController.cs
trong folder Controllers
với nội dụng:
using Microsoft.AspNetCore.Mvc;
namespace RemindersManagement.API.Controllers
{
public class HomeController : ControllerBase
{
[Route(""), HttpGet]
[ApiExplorerSettings(IgnoreApi = true)]
public RedirectResult RedirectToSwaggerUI()
{
return Redirect("/swagger/");
}
}
}
Health Check
Việc kiểm tra Health Check kết hợp hai middlewares:
Microsoft.AspNetCore.Diagnostics.HealthChecks
: cung cấp bởi Microsoft, trả về kết quả Health Check của ứng dụng .NET coreAspNetCore.Diagnostics.HealthChecks
: cung cấp bởi Xabaril, bổ sung thêm khả năng tích hợp và hiển thị Health Check UI
Các bước thực hiện:
- Cài đặt packages trong project
RemindersManagement.API
, với các lệnh:
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.Client
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
dotnet add package Microsoft.AspNetCore.Diagnostics.HealthChecks
Chú ý
Health Check UI sử dụng storage để ghi lại kết quả mỗi khi trạng thái chuyển đổi giữa Healthy và Unhealthy. Do vậy, để hiển thị Health Check UI, chúng ta cần cài đặt một trong các storage providers, đây là lý do chúng ta sử dụng đến package AspNetCore.HealthChecks.UI.InMemory.Storage
trong phần cài đặt.
Khai báo trong appsettings.Development.json
:
{
"HealthChecksUI": {
"HealthChecks": [
{
"Name": "RemindersManagement",
"Uri": "http://localhost:8000/health"
}
],
"EvaluationTimeOnSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
},
"Serilog": {
"Using": [
"Serilog.Sinks.Console"
],
"MinimumLevel": {
"Default": "Information"
},
"WriteTo": [
{
"Name": "Console"
}
],
"Enrich": [
"FromLogContext"
]
},
"AllowedHosts": "*"
}
- Cập nhật các phương thức trong
Startup.cs
:
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RemindersManagement.API.Domain.Interfaces;
using RemindersManagement.API.Domain.Services;
using RemindersManagement.API.Infrastructure.Data;
using RemindersManagement.API.Infrastructure.Repositories;
using Serilog;
namespace RemindersManagement.API
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
// Register the Swagger generator
services.AddSwaggerGen();
// Register health check services
services.AddHealthChecks()
// Register HealthChecks UI and storage provider
services
.AddHealthChecksUI()
.AddInMemoryStorage();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecksUI();
});
// Middleware generate JSON endpoint.
app.UseSwagger();
// Middleware generate Swagger-UI
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "RemindersManagement V1");
});
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
}
}
}
Trong thư mục gốc của solution, thực hiện lệnh dotnet run
:
dotnet run --urls="http://localhost:8000/" --project Services/RemindersManagement/RemindersManagement.API/RemindersManagement.API.csproj
Xác nhận kết quả với địa chỉ http://localhost:8000/healthchecks-ui
:
Giao diện Health Check UI của
RemindersManagement
API
Prometheus
Prometheus là một hệ thống giám sát chức năng của các ứng dụng hoặc cơ sở hạ tầng thông qua những thông số metrics được đo lường theo chuỗi thời gian. Bằng cách cài đặt Node Exporters trên những thành phần hệ thống (virtual machine, database server…) hoặc sử dụng thư viện Prometheus - instrumention bên trong logic ứng dụng, Prometheus tự động thực hiện việc tìm kiếm - service discovery, khai thác - scraping dữ liệu và kết hợp cùng những hệ thống khác như Grafana Dashboard, Alert Management để xây dựng một giải pháp toàn diện cho khả năng giám sát hoạt động của toàn bộ hệ thống ứng dụng.
Kiến trúc hệ thống giám sát Prometheus
Prometheus cung cấp những loại metrics sau:
- Counter - giá trị tăng dần theo cách cộng dồn của một thông số.
- Gause - giá trị biến thiên có khả năng tăng hoặc giảm
- Histogram - giá trị phân phối trong một phạm vi hoặc thời gian
- Summary - tương tự History, bổ sung giá trị phân vị theo thời gian (*)
Trong phần này, chúng ta áp dụng Prometheus middleware trên RemindersManagement
để tạo metric endpoint /metrics
sau đó cài đặt và sử dụng Prometheus thực hiện việc thu thập metrics và hiện thị kết quả với một số câu lệnh truy vấn đơn giản dựa trên ngôn ngữ PromQL
Các bước thực hiện:
Bước 1: Cài đặt Prometheus middleware
Trong folder RemindersManagement.API
, cài đặt Prometheus packages:
dotnet add package prometheus-net.AspNetCore
Thay đổi nội dung Startup.cs
:
using HealthChecks.UI.Client;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using RemindersManagement.API.Domain.Interfaces;
using RemindersManagement.API.Domain.Services;
using RemindersManagement.API.Infrastructure.Data;
using RemindersManagement.API.Infrastructure.Repositories;
using Microsoft.AspNetCore.HttpOverrides;
using Serilog;
using System.Diagnostics.CodeAnalysis;
using Prometheus;
namespace RemindersManagement.API
{
[ExcludeFromCodeCoverage]
public class Startup
{
...
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseForwardedHeaders();
}
else
{
app.UseForwardedHeaders();
app.UseHsts();
}
app.UseSerilogRequestLogging();
app.UseHttpsRedirection();
app.UseRouting();
app.UseHttpMetrics();
app.UseAuthorization();
// ASP.NET Core 2
// app.UseMetricServer();
// Create health check endpoint `/health`.
app.UseHealthChecks("/health", new HealthCheckOptions()
{
Predicate = _ => true,
// Return HealthReport data to show in HealthCheck UI.
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.UseEndpoints(endpoints =>
{
// ASP.NET Core 3 or newer
endpoints.MapMetrics();
endpoints.MapControllers();
});
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "RemindersManagement V1");
});
}
}
}
Các chú ý trong thay đổi trên:
- Sử dụng ASP.NET Core Exporter middleware
endpoints.MapMetrics();
- Cung cấp metrics qua URL endpoint
/metrics
- Cung cấp metrics qua URL endpoint
- Sử dụng library ASP.NET Core HTTP request metrics cung cấp thông tin:
- Số lượng HTTP request đang xử lý
- Số lượng HTTP request nhận được
- Thời gian thực thi HTTP request
Chú ý
- Cách thức cài đặt Prometheus middleware có sự thay đổi giữa ASP.NET Core 2.x và 3.x
- Prometheus cung cấp package tích hợp hoạt động Health Check tương tự như Xabaril
Bước 2: Thực thi và xác nhận kết quả
- Trong solution folder
FriendReminders
, thực thi project với lệnh
dotnet run --urls="http://localhost:8000/" --project Services/RemindersManagement/RemindersManagement.API/RemindersManagement.API.csproj
- Truy cập endpoint
http://localhost:8000/metrics
và xác nhận kết quả
Kết quả hiển thị trên metric endpoint
Dữ liệu Histogram trong kết quả trên được phân loại theo giá trị các tham số Status Code, Method Controller. Mỗi giá trị được biểu diễn theo một trong cách định dạng:
- <basename>_bucket{...,le = "<bound_value>"}
- <basename>_sum
_ <basename>_count
Ví dụ:
- Số lượng HTTP Get request gửi đến endpoint ‘/’ và nhận được 404 Not Found Error trong 1 giây
http_request_duration_seconds_count{code="200",method="GET",controller="",action=""} 2
- Thời gian phản hồi HTTP Get request gửi đến endpoint ‘/’ và nhận được 200 OK trong 1 giây
http_request_duration_seconds_sum{code="200",method="GET",controller="",action=""} 0.2245351
- Với bucket chia theo khoảng thời gian phản hồi: 0.001, 0.002, 0.004, 00.008…những giá trị sau thể hiển số lượng HTTP Get request gửi đến ‘Home’ controller và nhận được 302 Found Redirect.
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.001"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.002"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.004"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.008"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.016"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.032"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.064"} 0
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.128"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.256"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="0.512"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="1.024"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="2.048"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="4.096"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="8.192"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="16.384"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="32.768"} 1
http_request_duration_seconds_bucket{code="302",method="GET",controller="Home",action="RedirectToSwaggerUI",le="+Inf"} 1
Bước 3: Cài đặt Prometheus và truy vấn dữ liệu
- Download và cài đặt Prometheus
Link download: Prometheus
Với hệ điều hành MacOS sử dụng prometheus-2.21.0.darwin-amd64.tar.gz
Giải nén download file:
tar xvfz prometheus-*.tar
rm prometheus-*.tar
- Folder giải nén
prometheus-2.21.0.darwin-amd64
có sẵn file cấu hìnhprometheus.yml
:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
Theo cấu hình trên, Prometheus thực hiện giám sát chính mình qua địa chỉ localhost:9090
- Để giám sát
RemindersManagement
service, bổ sung thêm job_nameremindersmanagement
với cấu hình sau:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'remindersmanagement'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets: ['localhost:8000']
- Thực thi Prometheus với cấu hình cập nhật
# Start Prometheus.
# By default, Prometheus stores its database in ./data (flag --storage.tsdb.path).
./prometheus --config.file=prometheus.yml
Chú ý
-
Khi gặp lỗi “prometheus cannot be opened because the developer cannot be verified.”, có thể sử dụng chức năng Security & Privacy trong MacOS để xác nhận quyền thực hiện câu lệnh.
-
Trường hợp port 9090 đang sử dụng bởi một ứng dụng khác, có thể sử dụng câu lệnh
lsof -i :9090
andsudo kill -9 [PID]
để lấy lại cổng 9090.
- Truy cập địa chỉ
http://localhost:9090/targets
và xác nhận danh sách Target cùng trạng thái hoạt động:
Danh sách Prometheus Targets
- Truy cập địa chỉ
http://localhost:9090/graph
, thực hiện một số câu lênh truy vấn với PromQL. Kết quả trả về có thể hiển thị dưới dạngConsole
hoặcGraphp
.
Kiểm tra trạng thái target
// Up == 1
Up
Kết quả kiểm tra trạng thái của Prometheus Targets
Tổng số HTTP Request gửi đến
http_request_duration_seconds_count
Thống kê số lượng HTTP Requests
Tốc độ thay đổi của tổng số HTTP Request gửi đến trong 1 giây
rate(http_request_duration_seconds_count[1m])
Tốc độ thay đổi số lượng HTTP Request gửi đến trong 1 giây
Bước 4: Kết hợp Prometheus và Alert Management
Prometheus kết hợp PromQL và Alert Rule để tạo ra cảnh báo với những trạng thái bất thường bên trong hệ thống. Những cảnh bảo này được gửi đến Alert Management từ đó tạo ra những message gửi đến người quản trị thông qua hệ thống email, chat etc…
Trong bước 3, truy vấn PromQL Up
cho phép hiển thị Targets tuỳ theo trạng thái hoạt động:
Up == 1
: Targets đang hoạt độngUp == 0
: Targets đang ngừng hoạt động
Dựa theo truy vấn này, tạo một file cấu hình rules.yml
theo nội dung:
groups:
- name: AllTargets
rules:
- alert: TargetDown
# Condition for alerting
expr: up == 0
for: 1m
# Annotation - additional informational labels to store more information
annotations:
title: "Instance down"
description: " of job has been down for more than 1 minute."
# Labels - additional labels to be attached to the alert
labels:
severity: 'critical'
Cập nhật prometheus.yml
để tham chiếu đến qui tắc trên:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
- rules.yml
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'remindersmanagement'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets: ['localhost:8000']
Theo thay đổi này, alert rule được kiểm tra sau mỗi 15 giây, nếu một Target không hoạt động liên tục trong 1 phút thì cảnh báo sẽ được hệ thống Prometheus ghi lại.
Để xác nhận rules check, thực thi Prometheus đồng thời ngừng hoạt động của RemindersManagement
:
./prometheus --config.file=prometheus.yml
Danh sách Targets qua endpoint http://localhost:9090/targets
Danh sách Prometheus Targets với trạng thái Up và Down
Danh sách Alerts qua endpoint http://localhost:9090/alerts
Danh sách Alerts trong hệ thống
Kết luận
Các Middlewares được sử dụng như những thư viện có khả năng liên kết với nhau để bổ sung thêm những chức năng trong một ứng dụng, đồng thời tích hợp ứng dụng với những thành phần hệ thống thông tin khác. Bên cạnh đó, bài viết cũng cung cấp những thông tin khái quát về các thành phần trong một hệ thống Prometheus, giám sát và cảnh báo giúp kiểm soát chất lượng thực thi của một ứng dụng. Để tìm hiểu thêm về cách áp dụng Prometheus trong môi trường Production trên AWS Cloud, chúng ta có thể tham khảo thêm bài viết Swarm P.3.