// 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)]);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
    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);
    }
Esempio n. 5
0
        /// <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)]);
        }