Skip to content

Commit 2d6796d

Browse files
authored
Merge pull request dotnet-architecture#144 from dotnet/healthcheck
Healthcheck
2 parents 5efd0f6 + 3ef2aef commit 2d6796d

113 files changed

Lines changed: 24992 additions & 7 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docker-compose.override.yml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,17 @@ services:
7676
- SA_PASSWORD=Pass@word
7777
- ACCEPT_EULA=Y
7878
ports:
79-
- "5433:1433"
79+
- "5433:1433"
80+
81+
webstatus:
82+
environment:
83+
- ASPNETCORE_ENVIRONMENT=Development
84+
- ASPNETCORE_URLS=http://0.0.0.0:5107
85+
- CatalogUrl=http://catalog.api:5101/hc
86+
- OrderingUrl=http://ordering.api:5102/hc
87+
- BasketUrl=http://basket.api:5103/hc
88+
- IdentityUrl=http://10.0.75.1:5105/hc
89+
- mvc=http://webmvc:5100/hc
90+
- spa=http://webspa:5104/hc
91+
ports:
92+
- "5107:5107"

docker-compose.prod.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,14 @@ services:
7777
- SA_PASSWORD=Pass@word
7878
- ACCEPT_EULA=Y
7979
ports:
80-
- "5433:1433"
80+
- "5433:1433"
81+
82+
webstatus:
83+
environment:
84+
- ASPNETCORE_ENVIRONMENT=Development
85+
- CatalogUrl=http://catalog.api:5101/hc
86+
- OrderingUrl=http://ordering.api:5102/hc
87+
- BasketUrl=http://basket.api:5103/hc
88+
- IdentityUrl=http://10.0.75.1:5105/hc
89+
ports:
90+
- "5107:5107"

docker-compose.vs.debug.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,18 @@ services:
9090
entrypoint: tail -f /dev/null
9191
labels:
9292
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
93+
94+
webstatus:
95+
image: eshop/webstatus:dev
96+
build:
97+
args:
98+
source: ${DOCKER_BUILD_SOURCE}
99+
environment:
100+
- DOTNET_USE_POLLING_FILE_WATCHER=1
101+
volumes:
102+
- ./src/Web/WebStatus:/app
103+
- ~/.nuget/packages:/root/.nuget/packages:ro
104+
- ~/clrdbg:/clrdbg:ro
105+
entrypoint: tail -f /dev/null
106+
labels:
107+
- "com.microsoft.visualstudio.targetoperatingsystem=linux"

docker-compose.vs.release.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,13 @@ services:
6060
entrypoint: tail -f /dev/null
6161
labels:
6262
- "com.microsoft.visualstudio.targetoperatingsystem=linux"
63+
64+
webstatus:
65+
build:
66+
args:
67+
source: ${DOCKER_BUILD_SOURCE}
68+
volumes:
69+
- ~/clrdbg:/clrdbg:ro
70+
entrypoint: tail -f /dev/null
71+
labels:
72+
- "com.microsoft.visualstudio.targetoperatingsystem=linux"

docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,9 @@ services:
6868
image: rabbitmq
6969
ports:
7070
- "5672:5672"
71+
72+
webstatus:
73+
image: eshop/webstatus
74+
build:
75+
context: ./src/Web/WebStatus
76+
dockerfile: Dockerfile

eShopOnContainers-ServicesAndWebApps.sln

Lines changed: 209 additions & 2 deletions
Large diffs are not rendered by default.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.AspNetCore.Http.Features;
7+
using Microsoft.Extensions.HealthChecks;
8+
using Newtonsoft.Json;
9+
10+
namespace Microsoft.AspNetCore.HealthChecks
11+
{
12+
public class HealthCheckMiddleware
13+
{
14+
private RequestDelegate _next;
15+
private string _path;
16+
private int? _port;
17+
private IHealthCheckService _service;
18+
19+
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, int port)
20+
{
21+
_port = port;
22+
_service = service;
23+
_next = next;
24+
}
25+
26+
public HealthCheckMiddleware(RequestDelegate next, IHealthCheckService service, string path)
27+
{
28+
_path = path;
29+
_service = service;
30+
_next = next;
31+
}
32+
33+
public async Task Invoke(HttpContext context)
34+
{
35+
if (IsHealthCheckRequest(context))
36+
{
37+
var result = await _service.CheckHealthAsync();
38+
var status = result.CheckStatus;
39+
40+
if (status != CheckStatus.Healthy)
41+
context.Response.StatusCode = 503;
42+
43+
context.Response.Headers.Add("content-type", "application/json");
44+
await context.Response.WriteAsync(JsonConvert.SerializeObject(new { status = status.ToString() }));
45+
return;
46+
}
47+
else
48+
{
49+
await _next.Invoke(context);
50+
}
51+
}
52+
53+
private bool IsHealthCheckRequest(HttpContext context)
54+
{
55+
if (_port.HasValue)
56+
{
57+
var connInfo = context.Features.Get<IHttpConnectionFeature>();
58+
if (connInfo.LocalPort == _port)
59+
return true;
60+
}
61+
62+
if (context.Request.Path == _path)
63+
return true;
64+
65+
return false;
66+
}
67+
}
68+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.AspNetCore.Builder;
6+
using Microsoft.AspNetCore.Hosting;
7+
8+
namespace Microsoft.AspNetCore.HealthChecks
9+
{
10+
public class HealthCheckStartupFilter : IStartupFilter
11+
{
12+
private string _path;
13+
private int? _port;
14+
15+
public HealthCheckStartupFilter(int port)
16+
{
17+
_port = port;
18+
}
19+
20+
public HealthCheckStartupFilter(string path)
21+
{
22+
_path = path;
23+
}
24+
25+
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
26+
{
27+
return app =>
28+
{
29+
if (_port.HasValue)
30+
app.UseMiddleware<HealthCheckMiddleware>(_port);
31+
else
32+
app.UseMiddleware<HealthCheckMiddleware>(_path);
33+
34+
next(app);
35+
};
36+
}
37+
}
38+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.HealthChecks;
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
namespace Microsoft.AspNetCore.Hosting
8+
{
9+
public static class HealthCheckWebHostBuilderExtension
10+
{
11+
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, int port)
12+
{
13+
Guard.ArgumentValid(port > 0 && port < 65536, nameof(port), "Port must be a value between 1 and 65535");
14+
15+
builder.ConfigureServices(services =>
16+
{
17+
var existingUrl = builder.GetSetting(WebHostDefaults.ServerUrlsKey);
18+
builder.UseSetting(WebHostDefaults.ServerUrlsKey, $"{existingUrl};http://localhost:{port}");
19+
20+
services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(port));
21+
});
22+
return builder;
23+
}
24+
25+
public static IWebHostBuilder UseHealthChecks(this IWebHostBuilder builder, string path)
26+
{
27+
Guard.ArgumentNotNull(nameof(path), path);
28+
// REVIEW: Is there a better URL path validator somewhere?
29+
Guard.ArgumentValid(!path.Contains("?"), nameof(path), "Path cannot contain query string values");
30+
Guard.ArgumentValid(path.StartsWith("/"), nameof(path), "Path should start with /");
31+
32+
builder.ConfigureServices(services => services.AddSingleton<IStartupFilter>(new HealthCheckStartupFilter(path)));
33+
return builder;
34+
}
35+
}
36+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.HealthChecks;
6+
7+
namespace Microsoft.AspNetCore.Hosting
8+
{
9+
public static class HealthCheckWebHostExtensions
10+
{
11+
private const int DEFAULT_TIMEOUT_SECONDS = 300;
12+
13+
public static void RunWhenHealthy(this IWebHost webHost)
14+
{
15+
webHost.RunWhenHealthy(TimeSpan.FromSeconds(DEFAULT_TIMEOUT_SECONDS));
16+
}
17+
18+
public static void RunWhenHealthy(this IWebHost webHost, TimeSpan timeout)
19+
{
20+
var healthChecks = webHost.Services.GetService(typeof(IHealthCheckService)) as IHealthCheckService;
21+
22+
var loops = 0;
23+
do
24+
{
25+
var checkResult = healthChecks.CheckHealthAsync().Result;
26+
if (checkResult.CheckStatus == CheckStatus.Healthy)
27+
{
28+
webHost.Run();
29+
break;
30+
}
31+
32+
System.Threading.Thread.Sleep(1000);
33+
loops++;
34+
35+
} while (loops < timeout.TotalSeconds);
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)