// https://tools.ietf.org/html/rfc7239#section-6.3 private void AppendRandom(StringBuilder builder) { var random = _randomFactory.CreateRandomInstance(); builder.Append('_'); // This length is arbitrary. for (var i = 0; i < 9; i++) { builder.Append(ObfChars[random.Next(ObfChars.Length)]); } }
public DestinationState?PickDestination(HttpContext context, IReadOnlyList <DestinationState> availableDestinations) { if (availableDestinations.Count == 0) { return(null); } var random = _randomFactory.CreateRandomInstance(); return(availableDestinations[random.Next(availableDestinations.Count)]); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { // Pick two, and then return the least busy. This avoids the effort of searching the whole list, but // still avoids overloading a single endpoint. var random1 = _randomFactory.CreateRandomInstance(); var firstEndpoint = availableEndpoints[random1.Next(availableEndpoints.Count)]; var secondEndpoint = availableEndpoints[random1.Next(availableEndpoints.Count)]; return(firstEndpoint.ConcurrencyCounter.Value <= secondEndpoint.ConcurrencyCounter.Value ? firstEndpoint : secondEndpoint); }
public DestinationState?PickDestination(HttpContext context, ClusterState cluster, IReadOnlyList <DestinationState> availableDestinations) { if (availableDestinations.Count == 0) { return(null); } // Pick two, and then return the least busy. This avoids the effort of searching the whole list, but // still avoids overloading a single destination. var destinationCount = availableDestinations.Count; var random = _randomFactory.CreateRandomInstance(); var first = availableDestinations[random.Next(destinationCount)]; var second = availableDestinations[random.Next(destinationCount)]; return((first.ConcurrentRequestCount <= second.ConcurrentRequestCount) ? first : second); }
/// <summary> /// A probe attempt to one endpoint. /// </summary> private async Task ProbeEndpointAsync(EndpointInfo endpoint, AsyncSemaphore semaphore, CancellationToken cancellationToken) { // Conduct a dither for every endpoint probe to optimize concurrency. var randomDither = _randomFactory.CreateRandomInstance(); await _timer.Delay(TimeSpan.FromMilliseconds(randomDither.Next(_ditheringIntervalInMilliseconds)), cancellationToken); var outcome = HealthProbeOutcome.Unknown; string logDetail = null; // Enforce max concurrency. await semaphore.WaitAsync(); _logger.LogInformation($"The backend prober for backend: '{BackendId}', endpoint: `{endpoint.EndpointId}` has started."); try { using (var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(_cts.Token)) { // Set up timeout and start probing. timeoutCts.CancelAfter(_httpTimeoutInterval, _timer); var response = await _operationLogger.ExecuteAsync( "ReverseProxy.Core.Service.HealthProbe", () => _backendProbeHttpClient.GetAsync(new Uri(new Uri(endpoint.Config.Value.Address, UriKind.Absolute), _healthControllerUrl), timeoutCts.Token)); // Collect response status. outcome = response.IsSuccessStatusCode ? HealthProbeOutcome.Success : HealthProbeOutcome.HttpFailure; logDetail = $"Received status code {(int)response.StatusCode}"; } } catch (HttpRequestException ex) { // If there is a error during the http request process. Swallow the error and log error message. outcome = HealthProbeOutcome.TransportFailure; logDetail = ex.Message; } catch (OperationCanceledException) { // If the cancel requested by our StopAsync method. It is a expected graceful shut down. if (_cts.IsCancellationRequested) { outcome = HealthProbeOutcome.Canceled; logDetail = "Operation deliberately canceled"; throw; } else { outcome = HealthProbeOutcome.Timeout; logDetail = $"Health probe timed out after {Config.HealthCheckOptions.Interval.TotalSeconds} second"; } } catch (Exception ex) { throw new Exception($"Prober for '{endpoint.EndpointId}' encounters unexpected exception.", ex); } finally { if (outcome != HealthProbeOutcome.Canceled) { // Update the health state base on the response. var healthState = outcome == HealthProbeOutcome.Success ? EndpointHealth.Healthy : EndpointHealth.Unhealthy; endpoint.DynamicState.Value = new EndpointDynamicState(healthState); _logger.LogInformation($"Health probe result for endpoint '{endpoint.EndpointId}': {outcome}. Details: {logDetail}"); } // The probe operation is done, release the semaphore to allow other probes to proceed. semaphore.Release(); } }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { var random = _randomFactory.CreateRandomInstance(); return(availableEndpoints[random.Next(availableEndpoints.Count)]); }