【5min+】保持程式健康的祕訣!AspNetCore的HealthCheck
系列介紹
【五分鐘的dotnet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C#的小細節,AspnetCore,微服務中的.net知識等等。
5min+不是超過5分鐘的意思,"+"是知識的增加。so,它是讓您花費5分鐘以下的時間來提升您的知識儲備量。
正文
在開發AspNet Core應用的時候,我們經常會為該應用公佈一個特殊的檢測接口出來。該介面的目的很簡單,告訴某一些外界程式(比如docker,客戶端等)這個程式現在是可以訪問或者不能訪問的,便於外界做出相應的操作,比如監控報警,頁面通知使用者稍作等待等。
在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”
路徑,根據返回的對應Code來做出相應的反應。
執行狀況檢查
但是在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)。
接下來我們再次進行請求"health"
路徑,就會得到下面的結果:
自定義返回內容對咱們定位錯誤和記錄日誌十分有用。(就像看病的例子,病人更清晰的描述病情,醫生就能夠更容易定位病因。)。
第三方支援
雖然官方為我們提供的執行檢查庫已經足夠輕量和簡單。但是為了避免重複造輪子,我們可以使用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:
預設是沒有任何的檢測配置項的,如果咱們需要視覺化執行狀態,需要在AddHealthChecksUI
中進行配置:
services.AddHealthChecksUI(setupSettings: setup =>
{
setup.AddHealthCheckEndpoint("endpoint1", "http://localhost:5001/health");
});
再次檢視UI介面,就能看到對應的檢查項:
總結
本篇文章主要為大家介紹了 aspnet core 2.2之後所推出的“HealthCheck”,與使用傳統的Controller 公開API進行檢查不同,使用“IHealthCheck”能夠更快速的進行訪問(畢竟不需要進行模型繫結等操作),而且IHealthCheck
介面能夠更好的進行檢查項擴充套件。讓我們能夠更快更清楚的瞭解到應用程式執行的情況。
當然,由於篇幅有限,本來還想繼續拓展IHealthCheckPublisher
介面和健康檢查的實現原理等內容,就放在以後再說吧。如果您對該功能感興趣可以檢視 《ASP.NET Core 中的執行狀況檢查》和第三方HealthChecks擴充套件。
最後,偷偷說一句:創作不易,點個推薦吧.....