Skip to content

Commit 029f15a

Browse files
committed
Adding initial Host load check API support
1 parent 67726d1 commit 029f15a

21 files changed

+408
-34
lines changed

src/WebJobs.Script.WebHost/App_Start/AutofacBootstrap.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Autofac;
55
using Microsoft.Azure.WebJobs.Host;
66
using Microsoft.Azure.WebJobs.Script.Config;
7+
using Microsoft.Azure.WebJobs.Script.Diagnostics;
78
using Microsoft.Azure.WebJobs.Script.WebHost.WebHooks;
89

910
namespace Microsoft.Azure.WebJobs.Script.WebHost
@@ -23,6 +24,7 @@ internal static void Initialize(ScriptSettingsManager settingsManager, Container
2324
builder.Register<ISecretManager>(ct => ct.Resolve<WebHostResolver>().GetSecretManager(settings)).ExternallyOwned();
2425
builder.Register<WebScriptHostManager>(ct => ct.Resolve<WebHostResolver>().GetWebScriptHostManager(settings)).ExternallyOwned();
2526
builder.Register<WebHookReceiverManager>(ct => ct.Resolve<WebHostResolver>().GetWebHookReceiverManager(settings)).ExternallyOwned();
27+
builder.Register<HostPerformanceManager>(ct => ct.Resolve<WebHostResolver>().GetPerformanceManager(settings)).ExternallyOwned();
2628
builder.RegisterInstance(settings);
2729
}
2830
}

src/WebJobs.Script.WebHost/App_Start/WebHostResolver.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Text;
1010
using System.Threading.Tasks;
1111
using Microsoft.Azure.WebJobs.Script.Config;
12+
using Microsoft.Azure.WebJobs.Script.Diagnostics;
1213
using Microsoft.Azure.WebJobs.Script.WebHost.WebHooks;
1314

1415
namespace Microsoft.Azure.WebJobs.Script.WebHost
@@ -54,6 +55,11 @@ public ISecretManager GetSecretManager(WebHostSettings settings)
5455
return GetWebScriptHostManager(settings).SecretManager;
5556
}
5657

58+
public HostPerformanceManager GetPerformanceManager(WebHostSettings settings)
59+
{
60+
return GetWebScriptHostManager(settings).PerformanceManager;
61+
}
62+
5763
public WebScriptHostManager GetWebScriptHostManager(WebHostSettings settings)
5864
{
5965
if (_activeHostManager != null)

src/WebJobs.Script.WebHost/Controllers/AdminController.cs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Threading.Tasks;
1212
using System.Web.Http;
1313
using System.Web.Http.Controllers;
14+
using Microsoft.Azure.WebJobs.Script.Config;
1415
using Microsoft.Azure.WebJobs.Script.Description;
1516
using Microsoft.Azure.WebJobs.Script.WebHost.Filters;
1617
using Microsoft.Azure.WebJobs.Script.WebHost.Models;
@@ -87,18 +88,20 @@ public FunctionStatus GetFunctionStatus(string name)
8788
[HttpGet]
8889
[Route("admin/host/status")]
8990
[AllowAnonymous]
90-
public HostStatus GetHostStatus()
91+
public IHttpActionResult GetHostStatus()
9192
{
92-
var status = new HostStatus();
93-
9493
// based on the authorization level we determine
95-
// the additional level of detail to return
94+
// the level of detail to return
9695
var authorizationLevel = Request.GetAuthorizationLevel();
97-
if (authorizationLevel == AuthorizationLevel.Admin)
96+
if (authorizationLevel == AuthorizationLevel.Admin ||
97+
Request.IsAntaresInternalRequest())
9898
{
99-
status.State = _scriptHostManager.State.ToString();
100-
status.Version = ScriptHost.Version;
101-
status.Id = _scriptHostManager.Instance?.ScriptConfig.HostConfig.HostId;
99+
var status = new HostStatus
100+
{
101+
State = _scriptHostManager.State.ToString(),
102+
Version = ScriptHost.Version,
103+
Id = _scriptHostManager.Instance?.ScriptConfig.HostConfig.HostId
104+
};
102105

103106
var lastError = _scriptHostManager.LastError;
104107
if (lastError != null)
@@ -107,13 +110,22 @@ public HostStatus GetHostStatus()
107110
status.Errors.Add(Utility.FlattenException(lastError));
108111
}
109112

110-
return status;
113+
var parameters = Request.GetQueryParameterDictionary();
114+
string value = null;
115+
if (parameters.TryGetValue(ScriptConstants.CheckLoadQueryParameterName, out value) && value == "1")
116+
{
117+
status.Load = new LoadStatus
118+
{
119+
IsHigh = _scriptHostManager.PerformanceManager.IsUnderHighLoad()
120+
};
121+
}
122+
123+
return Ok(status);
111124
}
112125
else
113126
{
114-
// for Anonymous requests, we don't return any
115-
// detailed info
116-
return status;
127+
// for Anonymous requests, we don't return any info
128+
return Ok();
117129
}
118130
}
119131

src/WebJobs.Script.WebHost/Models/HostStatus.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Collections.ObjectModel;
5-
using System.Reflection;
65
using Newtonsoft.Json;
76

87
namespace Microsoft.Azure.WebJobs.Script.WebHost.Models
@@ -32,5 +31,11 @@ public class HostStatus
3231
/// </summary>
3332
[JsonProperty(PropertyName = "errors", DefaultValueHandling = DefaultValueHandling.Ignore)]
3433
public Collection<string> Errors { get; set; }
34+
35+
/// <summary>
36+
/// Gets or sets a the <see cref="LoadStatus"/>.
37+
/// </summary>
38+
[JsonProperty(PropertyName = "load", DefaultValueHandling = DefaultValueHandling.Ignore)]
39+
public LoadStatus Load { get; set; }
3540
}
3641
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Newtonsoft.Json;
5+
6+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Models
7+
{
8+
public class LoadStatus
9+
{
10+
/// <summary>
11+
/// Gets or sets a value indicating whether the current host load is high.
12+
/// </summary>
13+
[JsonProperty(PropertyName = "isHigh")]
14+
public bool IsHigh { get; set; }
15+
}
16+
}

src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@
427427
<Compile Include="Models\ApiModel.cs" />
428428
<Compile Include="Models\ApiModelUtility.cs" />
429429
<Compile Include="Models\Link.cs" />
430+
<Compile Include="Models\LoadStatus.cs" />
430431
<Compile Include="OperationResult.cs" />
431432
<Compile Include="Properties\Resources.Designer.cs">
432433
<AutoGen>True</AutoGen>

src/WebJobs.Script.WebHost/WebScriptHostManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class WebScriptHostManager : ScriptHostManager
3232
private static bool? _standbyMode;
3333
private readonly WebHostMetricsLogger _metricsLogger;
3434
private readonly ISecretManager _secretManager;
35+
private readonly HostPerformanceManager _performanceManager;
3536
private readonly WebHostSettings _webHostSettings;
3637
private readonly IWebJobsExceptionHandler _exceptionHandler;
3738
private readonly ScriptHostConfiguration _config;
@@ -61,6 +62,7 @@ public WebScriptHostManager(ScriptHostConfiguration config, ISecretManagerFactor
6162
}
6263

6364
_secretManager = secretManagerFactory.Create(settingsManager, config.TraceWriter, new FileSystemSecretsRepository(webHostSettings.SecretsPath));
65+
_performanceManager = new HostPerformanceManager(settingsManager);
6466
}
6567

6668
public WebScriptHostManager(ScriptHostConfiguration config, ISecretManagerFactory secretManagerFactory, ScriptSettingsManager settingsManager, WebHostSettings webHostSettings)
@@ -70,6 +72,8 @@ public WebScriptHostManager(ScriptHostConfiguration config, ISecretManagerFactor
7072

7173
public ISecretManager SecretManager => _secretManager;
7274

75+
public HostPerformanceManager PerformanceManager => _performanceManager;
76+
7377
public virtual bool Initialized
7478
{
7579
get

src/WebJobs.Script/Config/ScriptSettingsManager.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public class ScriptSettingsManager
1111
private static ScriptSettingsManager _instance = new ScriptSettingsManager();
1212
private readonly ConcurrentDictionary<string, string> _settingsCache = new ConcurrentDictionary<string, string>();
1313

14-
internal ScriptSettingsManager()
14+
// for testing
15+
public ScriptSettingsManager()
1516
{
1617
}
1718

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
5+
{
6+
public class ApplicationPerformanceCounters
7+
{
8+
public int UserTime { get; set; }
9+
public int KernelTime { get; set; }
10+
public int PageFaults { get; set; }
11+
public int Processes { get; set; }
12+
public int ProcessLimit { get; set; }
13+
public int Threads { get; set; }
14+
public int ThreadLimit { get; set; }
15+
public int Connections { get; set; }
16+
public int ConnectionLimit { get; set; }
17+
public int Sections { get; set; }
18+
public int SectionLimit { get; set; }
19+
public int NamedPipes { get; set; }
20+
public int NamedPipeLimit { get; set; }
21+
public int ReadIoOperations { get; set; }
22+
public int WriteIoOperations { get; set; }
23+
public int OtherIoOperations { get; set; }
24+
public int ReadIoBytes { get; set; }
25+
public int WriteIoBytes { get; set; }
26+
public int OtherIoBytes { get; set; }
27+
public int PrivateBytes { get; set; }
28+
public int Handles { get; set; }
29+
public int ContextSwitches { get; set; }
30+
public int RemoteOpens { get; set; }
31+
}
32+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using Microsoft.Azure.WebJobs.Script.Config;
5+
using Newtonsoft.Json;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.Diagnostics
8+
{
9+
public class HostPerformanceManager
10+
{
11+
internal const float ConnectionThreshold = 0.80F;
12+
internal const float ThreadThreshold = 0.80F;
13+
internal const float ProcessesThreshold = 0.80F;
14+
internal const float NamedPipesThreshold = 0.80F;
15+
16+
private readonly ScriptSettingsManager _settingsManager;
17+
18+
public HostPerformanceManager(ScriptSettingsManager settingsManager)
19+
{
20+
_settingsManager = settingsManager;
21+
}
22+
23+
public bool IsUnderHighLoad()
24+
{
25+
var counters = GetPerformanceCounters();
26+
if (counters != null)
27+
{
28+
return IsUnderHighLoad(counters);
29+
}
30+
31+
return false;
32+
}
33+
34+
internal static bool IsUnderHighLoad(ApplicationPerformanceCounters counters)
35+
{
36+
return
37+
ThresholdExceeded(counters.Connections, counters.ConnectionLimit, ConnectionThreshold) ||
38+
ThresholdExceeded(counters.Threads, counters.ThreadLimit, ThreadThreshold) ||
39+
ThresholdExceeded(counters.Processes, counters.ProcessLimit, ProcessesThreshold) ||
40+
ThresholdExceeded(counters.NamedPipes, counters.NamedPipeLimit, NamedPipesThreshold);
41+
}
42+
43+
internal static bool ThresholdExceeded(int currentValue, int limit, float threshold)
44+
{
45+
float currentUsage = (float)currentValue / limit;
46+
return currentUsage > threshold;
47+
}
48+
49+
internal ApplicationPerformanceCounters GetPerformanceCounters()
50+
{
51+
string json = _settingsManager.GetSetting(EnvironmentSettingNames.AzureWebsiteAppCountersName);
52+
if (!string.IsNullOrEmpty(json))
53+
{
54+
// TEMP: need to parse this specially to work around bug where
55+
// sometimes an extra garbage character occurs after the terminal
56+
// brace
57+
int idx = json.LastIndexOf('}');
58+
if (idx > 0)
59+
{
60+
json = json.Substring(0, idx + 1);
61+
}
62+
63+
return JsonConvert.DeserializeObject<ApplicationPerformanceCounters>(json);
64+
}
65+
66+
return null;
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)