正文
在開發AspNet Core
應用的時候,我們經常會爲該應用公佈一個特殊的檢測接口出來。該接口的目的很簡單,告訴外界程序當前程序現在是可以訪問或者不能訪問的,便於外界做出相應的操作,比如監控報警,頁面通知用戶稍作等待等。
在AspNet Core 2.2
之前,如果我們要實現一個這樣的檢測接口,需要建立一個單獨的controller
,比如HealthController
。然後爲其實現一個簡單的檢測方法:
[Route("working")]
public ActionResult Working()
{
using (var connection = new SqlConnection(_connectionString))
{
try
{
connection.Open();
}
catch (SqlException)
{
return new HttpStatusCodeResult(503, "Generic error");
}
}
return new EmptyResult();
}
該接口目的是檢測應用與數據庫的連接能否成功。如果成功連接,則返回狀態碼爲200
的空內容,如果失敗則返回503
。 外界程序可以通過定時訪問 “\working
” 路徑。
運行狀況檢查
但是在Aspnet Core 2.2
之後,我們有了新的解決方式。只需要簡單的操作就可以進行程序運行狀況的檢查。
我們只需要在Startup.cs
中添加兩句話就OK了:
public void ConfigureServices(IServiceCollection services)
{
//使用該擴展方法
services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseEndpoints(endpoints =>
{
//使用該擴展方法
endpoints.MapHealthChecks("/health");
});
}
默認情況是不需要在額外的引入其它nuget包的,因爲AspNet Core
自帶了這些功能。
此時我們可以訪問 "/health"
路徑,如果程序正常,則返回Http狀態碼爲200,顯示內容爲"Healthy"
的結果。如果程序不正常,則返回Http狀態碼爲503,顯示內容爲"UnHealthy"
的結果。
這就是運行狀況檢查的初步使用。
目的性的檢查
最初我們只是簡單的引入了 AddHealthChecks
。 但是它並沒有任何特定的邏輯在裏面。而現實場景我們是需要對各種指標進行檢查的,所以我們需要實現自定義的檢查功能。
比如咱們現在要實現一個對Sql Server
連接情況的檢查。我們只需要實現 IHealthCheck
接口,實現CheckHealthAsync
方法就可以了:
public class SqlServerHealthCheck : IHealthCheck
{
SqlConnection _connection;
public string Name => "sql";
public SqlServerHealthCheck(SqlConnection connection)
{
_connection = connection;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
try
{
_connection.Open();
}
catch (SqlException)
{
return Task.FromResult(HealthCheckResult.Unhealthy("From Sql Serve"));
}
return Task.FromResult(HealthCheckResult.Healthy());
}
}
然後在Startup.cs
的AddHealthChecks
進行擴展:
services.AddHealthChecks()
.AddCheck<SqlServerHealthCheck>("sql_check");
此時如果咱們再次訪問"/health"
路徑,就會發現應用會執行SqlServerHealthCheck
裏面的檢查邏輯。
但是實際情況,咱們往往都會有許許多多的檢查項,比如增加一個叫做MemoryHealthCheck
的檢查項:
public class MemoryHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
//doing some memory check things.
return Task.FromResult(HealthCheckResult.Healthy());
}
}
// startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck<SqlServerHealthCheck>("sql_check")
.AddCheck<MemoryHealthCheck>("memory_check"); // add this line
}
或許還有許許多多的檢查項:FileSizeHealthCheck,RedisHealthCheck
等等。當我們將它們都添加上之後,則只有當所有的檢查器都返回爲Healthy
的時候,纔會認爲是健康。
但是某些情況我們又只想進行單項檢查怎麼辦呢? 我們可以在 endpoints
的配置中新增另外的路由映射規則:
// startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = s => s.Name.Equals("sql_check"),
ResponseWriter = WriteResponse
});
endpoints.MapHealthChecks("/healthy", new HealthCheckOptions()
{
Predicate = s => s.Name.Equals("memory_check"),
ResponseWriter = WriteResponse
});
});
//指定返回格式
private static Task WriteResponse(HttpContext context, HealthReport result)
{
context.Response.ContentType = "application/json";
var json = new JObject(
new JProperty("status", result.Status.ToString()),
new JProperty("results", new JObject(result.Entries.Select(pair =>
new JProperty(pair.Key, new JObject(
new JProperty("status", pair.Value.Status.ToString()),
new JProperty("description", pair.Value.Description),
new JProperty("data", new JObject(pair.Value.Data.Select(
p => new JProperty(p.Key, p.Value))))))))));
return context.Response.WriteAsync(
json.ToString());
}
我們在原有的基礎上增加了HealthCheckOptions
的參數,該參數指定了關於狀態檢測的匹配規則,返回狀態碼,返回格式等信息。
上面的代碼我們指定了兩個路由。當訪問"health"
路徑的時候,則是對sql連接的檢查(根據檢查器名來匹配:Name.Equals("sql_check")
),而訪問"healthy"
路徑的時候,是對內存的檢查。 最後還爲他們指定了需要返回的內容(WriteResponse
)。
自定義返回內容對咱們定位錯誤和記錄日誌十分有用。
第三方支持
雖然官方爲我們提供的運行檢查庫已經足夠輕量和簡單。但是爲了避免重複造輪子,我們可以使用AspNetCore.Diagnostics.HealthChecks
包,該項目包含了許多情況的檢查,比如 Sql Server、MySql、Elasticsearch、Redis、Kafka
等等。
並且還爲我們提供一個UI界面,可供查看。只需要在原有的基礎上引入對應的代碼就行了:
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks()
.AddCheck<SqlServerHealthCheck>("sql_check")
.AddCheck<MemoryHealthCheck>("memory_check");
// add this line
services.AddHealthChecksUI();
}
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
Predicate = s => s.Name.Equals("sql_check"),
ResponseWriter = WriteResponse
});
//add this line
endpoints.MapHealthChecksUI();
});
當我們訪問"/healthchecks-ui"
路徑時,就可以看到這樣的UI:
默認是沒有任何的檢測配置項的,如果咱們需要可視化運行狀態,需要添加配置:
"HealthChecksUI": {
"HealthChecks": [
{
"Name": "db_check",
"Uri": "http://localhost:5000/db_health"
}
],
"EvaluationTimeinSeconds": 10,
"MinimumSecondsBetweenFailureNotifications": 60
}
再次查看UI界面