/// <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)]); } }
public async Task <IEndPointHandle> GetOrWaitForNextHost(CancellationToken cancellationToken) { var hostOverride = TracingContext.GetHostOverride(DeploymentIdentifier.ServiceName); if (hostOverride != null) { return(new OverriddenRemoteHost(DeploymentIdentifier.ServiceName, hostOverride.Hostname, hostOverride.Port ?? GetConfig().DefaultPort)); } if (ReachableHosts.Count > 0) { return(GetNextHost()); } lock (_lock) { if (FirstAvailableHostCompletionSource == null) { FirstAvailableHostCompletionSource = new TaskCompletionSource <RemoteHost>(); } cancellationToken.Register(() => FirstAvailableHostCompletionSource?.SetCanceled()); } return(await FirstAvailableHostCompletionSource.Task.ConfigureAwait(false)); }
public async Task <RemoteHost> GetOrWaitForNextHost(CancellationToken cancellationToken) { var hostOverride = TracingContext.GetHostOverride(ServiceDeployment.ServiceName); if (hostOverride != null) { return(new OverriddenRemoteHost(ServiceDeployment.ServiceName, hostOverride, this)); } if (ReachableHosts.Count > 0) { return(GetNextHost()); } lock (_lock) { if (FirstAvailableHostCompletionSource == null) { FirstAvailableHostCompletionSource = new TaskCompletionSource <RemoteHost>(); } cancellationToken.Register(() => FirstAvailableHostCompletionSource?.SetCanceled()); } return(await FirstAvailableHostCompletionSource.Task.ConfigureAwait(false)); }
///<inheritdoc /> public async Task <NodeAndLoadBalancer> GetNode() { _lastUsageTime = DateTime.UtcNow; NodeAndLoadBalancer nodeAndLoadBalancer = null; string preferredEnvironment = TracingContext.GetPreferredEnvironment(); // 1. Use explicit host override if provided in request // TBD: Theoretically if we only ever call a service through host overrides we might not have a health check for the service at all (though it is in use) var hostOverride = TracingContext.GetHostOverride(ServiceName); if (hostOverride != null) { return new NodeAndLoadBalancer { Node = new Node(hostOverride.Host, hostOverride.Port), LoadBalancer = null, PreferredEnvironment = preferredEnvironment ?? Environment.DeploymentEnvironment, } } ; // 2. Otherwise, use preferred environment if provided in request if (preferredEnvironment != null && (nodeAndLoadBalancer = await GetNodeAndLoadBalancer(preferredEnvironment, preferredEnvironment)) != null) { return(nodeAndLoadBalancer); } // 3. Otherwise, try use current environment if ((nodeAndLoadBalancer = await GetNodeAndLoadBalancer(Environment.DeploymentEnvironment, preferredEnvironment)) != null) { _healthStatus = new HealthMessage(Health.Healthy, message: null, suppressMessage: true); // No need for a health message since the load balancer we're returning already provides one return(nodeAndLoadBalancer); } // 4. We're in prod env and service is not deployed, no fallback possible if (Environment.DeploymentEnvironment == MASTER_ENVIRONMENT) { _healthStatus = new HealthMessage(Health.Unhealthy, "Service not deployed"); throw ServiceNotDeployedException(); } // 5. We're not in prod, but fallback to prod is disabled if (GetDiscoveryConfig().EnvironmentFallbackEnabled == false) { _healthStatus = new HealthMessage(Health.Unhealthy, "Service not deployed (and fallback to prod disabled)"); throw ServiceNotDeployedException(); } // 6. Otherwise, try fallback to prod if ((nodeAndLoadBalancer = await GetNodeAndLoadBalancer(MASTER_ENVIRONMENT, preferredEnvironment ?? Environment.DeploymentEnvironment)) != null) { _healthStatus = new HealthMessage(Health.Healthy, "Service not deployed, falling back to prod"); return(nodeAndLoadBalancer); } _healthStatus = new HealthMessage(Health.Unhealthy, "Service not deployed, fallback to prod enabled but service not deployed in prod either"); throw ServiceNotDeployedException(); }
public async Task <object> Invoke(HttpServiceRequest request, Type resultReturnType, JsonSerializerSettings jsonSettings = null) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (resultReturnType == null) { throw new ArgumentNullException(nameof(resultReturnType)); } var hostOverride = TracingContext.GetHostOverride(ServiceName); if (hostOverride != null) { var httpRequest = CreateHttpRequest(request, jsonSettings, hostOverride); } else { } return(null); }