Neste artigo retomo a série de dicas úteis para o dia a dia de Desenvolvedores ASP.NET Core. Caso ainda não tenha acessado o primeiro artigo ou até mesmo queira de revê-lo, acesse o link a seguir:
Definindo a versão do .NET Core utilizada pelo comando dotnet new
Com o .NET Core temos a possibilidade de instalação de diferentes versões de SDKs numa mesma máquina. Há, contudo, uma limitação: que versão o .NET Core CLI (Command-Line Interface) utilizará como default quando solicitarmos a criação de um novo projeto?
O comando dotnet new assumirá como padrão a versão mais recente instalada para o .NET Core. Mas e se precisarmos, então, gerar novos projetos via linha de comando, tomando como base um release anterior?
Felizmente podemos solucionar isso através da criação de um arquivo global.json em um diretório a partir do qual serão gerados novos projetos. Já abordei inclusive isso no seguinte artigo:
Na imagem a seguir é possível constatar que o comando dotnet –version retornou primeiramente a versão 3 do .NET Core (ainda em Preview), ao passo que dentro do diretório C:\ASP.NET Core 2.2\, foi assumido o .NET Core 2.2 graças à existência de um arquivo global.json no mesmo:
Health Checks no ASP.NET Core 2.2
A implementação de uma Action customizada que será acessada para fins de monitoramento costuma ser uma prática comum em aplicações ASP.NET. Geralmente isso envolve checagens executadas com o intuito de determinar se o projeto em questão se encontra dentro de seu status normal de funcionamento.
O ASP.NET Core 2.2 conta agora com um novo mecanismo de Health Check que simplifica este tipo de verificação. A ativação deste recurso envolve as seguintes alterações na classe Startup:
- A chamada ao método AddHealthChecks em ConfigureServices;
- A ativação do middleware de Health Check, com a invocação do método UseHealthChecks (informando um caminho para acesso) em Configure.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace APICotacoes
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Adicionado o serviço de Health Check
services.AddHealthChecks();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// Ativando o middlweare de Health Check
app.UseHealthChecks("/status");
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
Um teste acessando o endereço https://localhost:5001/status via Postman trará como retorno o código HTTP 200 (OK), além da mensagem de texto contendo o valor Healthy:
Mais um pouco sobre Health Checks e o ASP.NET Core 2.2: verificando a disponibilidade de um banco de dados
O projeto Xabaril/AspNetCore.Diagnostics.HealthChecks traz uma série de extensões para o ASP.NET Core 2.2, de forma a simplificar a implementação de Health Checks envolvendo verificações de disponibilidade de bancos de dados.
No exemplo descrito nessa seção, foi adicionado o package AspNetCore.HealthChecks.SqlServer ao projeto, com uma alteração acontecendo na classe Startup. O método AddSqlServer foi acionado logo após a chamada a AddHealthChecks, recebendo como parâmetro a Connection String da base a ser testada (BaseCotacoes):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace APICotacoes
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Adicionado o serviço de Health Check
//services.AddHealthChecks();
// Adicionando a verificação de disponibilidade
// do banco de dados
services.AddHealthChecks()
.AddSqlServer(Configuration.GetConnectionString("BaseCotacoes"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// Ativando o middlweare de Health Check
app.UseHealthChecks("/status");
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
Um teste com o servidor de bancos de dados indisponível retornará o código de erro HTTP 503 (Service Unavailable), além da mensagem Unhealthy:
Formatando a resposta dos Health Checks no ASP.NET Core 2.2
Ao invocar o método UseHealthChecks no método Configure da classe Startup, temos ainda a opção de informar como parâmetro uma instância do tipo HealthCheckOptions (namespace Microsoft.AspNetCore.Diagnostics.HealthChecks), a qual permitirá formatar o retorno do middleware de Health Check com informações como o status geral da aplicação e a situação de cada checagem previamente configurada:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Net.Mime;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Newtonsoft.Json;
namespace APICotacoes
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Adicionando a verificação de disponibilidade
// do banco de dados
services.AddHealthChecks()
.AddSqlServer(Configuration.GetConnectionString("BaseCotacoes"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// Ativando o middlweare de Health Check
app.UseHealthChecks("/status",
new HealthCheckOptions()
{
ResponseWriter = async (context, report) =>
{
var result = JsonConvert.SerializeObject(
new
{
statusApplication = report.Status.ToString(),
healthChecks = report.Entries.Select(e => new
{
check = e.Key,
status = Enum.GetName(typeof(HealthStatus), e.Value.Status)
})
});
context.Response.ContentType = MediaTypeNames.Application.Json;
await context.Response.WriteAsync(result);
}
});
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
A seguir temos o retorno para uma situação normal da aplicação:
E a resposta quando ocorre indisponibilidade do banco de dados:
Implementando Load Balancing com Nginx
O Nginx pode ser uma opção extremamente simples e eficiente para a implementação de mecanismos de Load Balancing em aplicações ASP.NET Core de médio e pequeno porte, sobretudo se utilizado em conjunto com tecnologias como Docker e Docker Compose. No artigo abaixo demonstro tudo isso através de um exemplo prático: