Пример #1
0
        /// <summary>
        /// Retrieves the next reachable <see cref="RemoteHost"/>.
        /// </summary>
        /// <param name="affinityToken">
        /// A string to generate a consistent affinity to a specific host within the set of available hosts.
        /// Identical strings will return the same host for a given pool of reachable hosts. A request ID is usually provided.
        /// </param>
        /// <returns>A reachable <see cref="RemoteHost"/>.</returns>
        /// <exception cref="EnvironmentException">Thrown when there is no reachable <see cref="RemoteHost"/> available.</exception>
        public IEndPointHandle GetNextHost(string affinityToken = null)
        {
            LastEndpointRequest = DateTime.UtcNow;

            var hostOverride = TracingContext.GetHostOverride(DeploymentIdentifier.ServiceName);

            if (hostOverride != null)
            {
                return(new OverriddenRemoteHost(DeploymentIdentifier.ServiceName, hostOverride.Hostname, hostOverride.Port ?? GetConfig().DefaultPort));
            }

            lock (_lock)
            {
                Health.Activate();

                if (ReachableHosts.Count == 0)
                {
                    var lastExceptionEndPoint = UnreachableHosts.FirstOrDefault();

                    // TODO: Exception throwing code should be in this class, not in another.
                    throw DiscoverySource.AllEndpointsUnreachable(EndPointsResult, lastExceptionEndPoint?.LastException, lastExceptionEndPoint == null ? null : $"{lastExceptionEndPoint.HostName}:{lastExceptionEndPoint.Port}", string.Join(", ", UnreachableHosts));
                }

                Counter++;

                ulong hostId = affinityToken == null ? Counter : (ulong)affinityToken.GetHashCode();

                return(ReachableHosts[(int)(hostId % (ulong)ReachableHosts.Count)]);
            }
        }
Пример #2
0
        /// <summary>
        /// Loads the specified settings, overwriting existing settings.
        /// </summary>
        /// <param name="updatedEndPointsResult"></param>
        /// this <see cref="RemoteHostPool"/>.
        /// <exception cref="ArgumentNullException">Thrown when </exception>
        /// <exception cref="EnvironmentException"></exception>
        private void ReloadEndpoints(EndPointsResult updatedEndPointsResult)
        {
            lock (_lock)
            {
                try
                {
                    var updatedEndPoints = updatedEndPointsResult.EndPoints;
                    if (updatedEndPoints.Any() == false)
                    {
                        Health.SetHealthFunction(() =>
                        {
                            var config = GetConfig();
                            if (IsHealthCheckSuppressed(config))
                            {
                                return(HealthCheckResult.Healthy($"No endpoints were discovered from source '{config.Source}' but the remote service was not in use for more than {config.SuppressHealthCheckAfterServiceUnused?.TotalSeconds} seconds."));
                            }
                            else
                            {
                                return(HealthCheckResult.Unhealthy($"No endpoints were discovered from source '{config.Source}'."));
                            }
                        });

                        EndPointsResult = updatedEndPointsResult;

                        ReachableHosts   = new List <RemoteHost>();
                        UnreachableHosts = new List <RemoteHost>();
                    }
                    else
                    {
                        if (EndPoints != null)
                        {
                            foreach (var removedEndPoint in EndPoints.Except(updatedEndPoints))
                            {
                                ReachableHosts.SingleOrDefault(h => h.Equals(removedEndPoint))?.StopMonitoring();
                                ReachableHosts.RemoveAll(h => h.Equals(removedEndPoint));
                                UnreachableHosts.RemoveAll(h => h.Equals(removedEndPoint));
                            }
                        }

                        var newHosts = updatedEndPoints
                                       .Except(EndPoints ?? Enumerable.Empty <EndPoint>())
                                       .Select(ep => new RemoteHost(ep.HostName, this, _lock, ep.Port));

                        ReachableHosts.AddRange(newHosts);

                        EndPointsResult = updatedEndPointsResult;
                        Counter         = (ulong)_random.Next(0, ReachableHosts.Count);

                        Health.SetHealthFunction(CheckHealth);
                    }
                }
                catch (Exception ex)
                {
                    Log.Warn("Failed to process newly discovered endpoints", exception: ex);
                    Health.SetHealthFunction(() => HealthCheckResult.Unhealthy("Failed to process newly discovered endpoints: " + HealthMonitor.GetMessages(ex)));
                }
            }
        }
Пример #3
0
 /// <summary>
 /// Resets the state of all hosts as reachable.
 /// </summary>
 public void MarkAllAsReachable()
 {
     lock (_lock)
     {
         foreach (var unreachableHost in UnreachableHosts.ToArray())
         {
             unreachableHost.ReportSuccess();
             MarkReachable(unreachableHost);
         }
     }
 }
Пример #4
0
 private Dictionary <string, string> HealthData()
 {
     lock (_lock)
     {
         return(new Dictionary <string, string>
         {
             { "ReachableHosts", string.Join(",", ReachableHosts.Select(_ => _.HostName)) },
             { "UnreachableHosts", string.Join(",", UnreachableHosts.Select(_ => _.HostName)) }
         });
     }
 }
Пример #5
0
        internal bool MarkUnreachable(RemoteHost remoteHost)
        {
            lock (_lock)
            {
                if (ReachableHosts.Remove(remoteHost))
                {
                    if (ReachableHosts.Count == 0)
                    {
                        ReachabilityBroadcaster.Post(new ServiceReachabilityStatus {
                            IsReachable = false
                        });
                    }
                    UnreachableHosts.Add(remoteHost);
                    return(true);
                }

                return(false);
            }
        }
Пример #6
0
        private ValueTask <HealthCheckResult> CheckHealth()
        {
            var config = GetConfig();

            if (IsHealthCheckSuppressed(config))
            {
                return(new ValueTask <HealthCheckResult>(HealthCheckResult.Healthy($"Health check suppressed because service was not in use for more than {config.SuppressHealthCheckAfterServiceUnused.TotalSeconds} seconds.")));
            }

            int       reachableCount;
            int       unreachableCount;
            Exception exception;

            string[] unreachableHosts;

            lock (_lock)
            {
                reachableCount   = ReachableHosts.Count;
                unreachableHosts = UnreachableHosts.Select(x => $"{x.HostName}:{x.Port}").ToArray();
                unreachableCount = unreachableHosts.Length;
                exception        = UnreachableHosts.FirstOrDefault()?.LastException;
            }

            if (reachableCount == 0)
            {
                return(new ValueTask <HealthCheckResult>(HealthCheckResult.Unhealthy($"All of the {unreachableCount} hosts are unreachable: " +
                                                                                     $"{string.Join(",", unreachableHosts)}. Last exception: {HealthMonitor.GetMessages(exception)}")));
            }
            else
            {
                if (unreachableCount > 0)
                {
                    return(new ValueTask <HealthCheckResult>(HealthCheckResult.Unhealthy($"The following {unreachableCount} hosts " +
                                                                                         $"(out of {unreachableCount + reachableCount}) are unreachable: {string.Join(",", unreachableHosts)}. " +
                                                                                         $"Last exception: {HealthMonitor.GetMessages(exception)}")));
                }
                else
                {
                    return(new ValueTask <HealthCheckResult>(HealthCheckResult.Healthy($"All {reachableCount} hosts are reachable.")));
                }
            }
        }
Пример #7
0
        internal bool MarkReachable(RemoteHost remoteHost)
        {
            lock (_lock)
            {
                if (UnreachableHosts.Remove(remoteHost))
                {
                    ReachableHosts.Add(remoteHost);
                    if (ReachableHosts.Count == 1)
                    {
                        ReachabilityBroadcaster.Post(new ServiceReachabilityStatus {
                            IsReachable = true
                        });
                    }

                    FirstAvailableHostCompletionSource?.SetResult(remoteHost);
                    FirstAvailableHostCompletionSource = null;

                    return(true);
                }

                return(false);
            }
        }