forked from Azure/azure-functions-host
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reenable http throttles (Azure#2945)
- Loading branch information
Showing
22 changed files
with
548 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using System.Threading.Tasks.Dataflow; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Azure.WebJobs.Extensions.Http; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.Azure.WebJobs.Script.WebHost | ||
{ | ||
/// <summary> | ||
/// Encapsulates an http request queue used for request throttling. See <see cref="HttpThrottleMiddleware"/>. | ||
/// This has been factored as its own service to ensure that it's lifetime stays tied to host the host | ||
/// instance lifetime, not the middleware lifetime which is longer lived. | ||
/// </summary> | ||
internal class HttpRequestQueue | ||
{ | ||
private readonly IOptions<HttpOptions> _httpOptions; | ||
private ActionBlock<HttpRequestItem> _requestQueue; | ||
|
||
public HttpRequestQueue(IOptions<HttpOptions> httpOptions) | ||
{ | ||
_httpOptions = httpOptions; | ||
|
||
if (_httpOptions.Value.MaxOutstandingRequests != DataflowBlockOptions.Unbounded || | ||
_httpOptions.Value.MaxConcurrentRequests != DataflowBlockOptions.Unbounded) | ||
{ | ||
InitializeRequestQueue(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value indicating whether request queueing is enabled. | ||
/// </summary> | ||
public bool Enabled => _requestQueue != null; | ||
|
||
public async Task<bool> Post(HttpContext httpContext, RequestDelegate next) | ||
{ | ||
// enqueue the request workitem | ||
var item = new HttpRequestItem | ||
{ | ||
HttpContext = httpContext, | ||
Next = next, | ||
CompletionSource = new TaskCompletionSource<object>() | ||
}; | ||
|
||
if (_requestQueue.Post(item)) | ||
{ | ||
await item.CompletionSource.Task; | ||
return true; | ||
} | ||
else | ||
{ | ||
// no more requests can be queued at this time | ||
return false; | ||
} | ||
} | ||
|
||
private void InitializeRequestQueue() | ||
{ | ||
// if throttles are enabled, initialize the queue | ||
var blockOptions = new ExecutionDataflowBlockOptions | ||
{ | ||
MaxDegreeOfParallelism = _httpOptions.Value.MaxConcurrentRequests, | ||
BoundedCapacity = _httpOptions.Value.MaxOutstandingRequests | ||
}; | ||
|
||
_requestQueue = new ActionBlock<HttpRequestItem>(async item => | ||
{ | ||
try | ||
{ | ||
await item.Next.Invoke(item.HttpContext); | ||
item.CompletionSource.SetResult(null); | ||
} | ||
catch (Exception ex) | ||
{ | ||
item.CompletionSource.SetException(ex); | ||
} | ||
}, blockOptions); | ||
} | ||
|
||
private class HttpRequestItem | ||
{ | ||
/// <summary> | ||
/// Gets or sets the request context to process. | ||
/// </summary> | ||
public HttpContext HttpContext { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the completion delegate for the request. | ||
/// </summary> | ||
public RequestDelegate Next { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the completion source to use. | ||
/// </summary> | ||
public TaskCompletionSource<object> CompletionSource { get; set; } | ||
} | ||
} | ||
} |
78 changes: 78 additions & 0 deletions
78
src/WebJobs.Script.WebHost/Middleware/HttpThrottleMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.ObjectModel; | ||
using System.Threading.Tasks; | ||
using Microsoft.AspNetCore.Http; | ||
using Microsoft.Azure.WebJobs.Extensions.Http; | ||
using Microsoft.Azure.WebJobs.Script.Diagnostics; | ||
using Microsoft.Azure.WebJobs.Script.Scale; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.Azure.WebJobs.Script.WebHost.Middleware | ||
{ | ||
internal class HttpThrottleMiddleware | ||
{ | ||
private readonly RequestDelegate _next; | ||
private readonly TimeSpan? _performanceCheckInterval; | ||
private readonly ILogger _logger; | ||
private DateTime _lastPerformanceCheck; | ||
private bool _rejectRequests; | ||
|
||
public HttpThrottleMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, TimeSpan? performanceCheckInterval = null) | ||
{ | ||
_next = next; | ||
_performanceCheckInterval = performanceCheckInterval ?? TimeSpan.FromSeconds(15); | ||
_logger = loggerFactory?.CreateLogger("Host.Extensions.Http.HttpThrottleMiddleware"); | ||
} | ||
|
||
public async Task Invoke(HttpContext httpContext, IOptions<HttpOptions> httpOptions, HttpRequestQueue requestQueue, HostPerformanceManager performanceManager, IMetricsLogger metricsLogger) | ||
{ | ||
if (httpOptions.Value.DynamicThrottlesEnabled && | ||
((DateTime.UtcNow - _lastPerformanceCheck) > _performanceCheckInterval)) | ||
{ | ||
// only check host status periodically | ||
Collection<string> exceededCounters = new Collection<string>(); | ||
_rejectRequests = performanceManager.IsUnderHighLoad(exceededCounters); | ||
_lastPerformanceCheck = DateTime.UtcNow; | ||
if (_rejectRequests) | ||
{ | ||
_logger.LogWarning($"Thresholds for the following counters have been exceeded: [{string.Join(", ", exceededCounters)}]"); | ||
} | ||
} | ||
|
||
if (_rejectRequests) | ||
{ | ||
// we're currently in reject mode, so reject the request and | ||
// call the next delegate without calling base | ||
RejectRequest(httpContext, metricsLogger); | ||
return; | ||
} | ||
|
||
if (requestQueue.Enabled) | ||
{ | ||
var success = await requestQueue.Post(httpContext, _next); | ||
if (!success) | ||
{ | ||
_logger?.LogInformation($"Http request queue limit of {httpOptions.Value.MaxOutstandingRequests} has been exceeded."); | ||
RejectRequest(httpContext, metricsLogger); | ||
} | ||
} | ||
else | ||
{ | ||
// queue is not enabled, so just dispatch the request directly | ||
await _next.Invoke(httpContext); | ||
} | ||
} | ||
|
||
private void RejectRequest(HttpContext httpContext, IMetricsLogger metricsLogger) | ||
{ | ||
metricsLogger.LogEvent(MetricEventNames.FunctionInvokeThrottled); | ||
|
||
httpContext.Response.StatusCode = 429; | ||
httpContext.Response.Headers.Add(ScriptConstants.AntaresScaleOutHeaderName, "1"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
src/WebJobs.Script/Config/HostHealthMonitorOptionsSetup.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the MIT License. See License.txt in the project root for license information. | ||
|
||
using Microsoft.Azure.WebJobs.Script.Configuration; | ||
using Microsoft.Extensions.Configuration; | ||
using Microsoft.Extensions.Options; | ||
|
||
namespace Microsoft.Azure.WebJobs.Script.Config | ||
{ | ||
internal class HostHealthMonitorOptionsSetup : IConfigureOptions<HostHealthMonitorOptions> | ||
{ | ||
private readonly IConfiguration _configuration; | ||
|
||
public HostHealthMonitorOptionsSetup(IConfiguration configuration) | ||
{ | ||
_configuration = configuration; | ||
} | ||
|
||
public void Configure(HostHealthMonitorOptions options) | ||
{ | ||
var section = _configuration.GetSection(ConfigurationSectionNames.HealthMonitor); | ||
section.Bind(options); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.