private async Task <DestinationProbingResult> ProbeDestinationAsync(ClusterState cluster, DestinationState destination, TimeSpan timeout) { HttpRequestMessage request; try { request = _probingRequestFactory.CreateRequest(cluster.Model, destination.Model); } catch (Exception ex) { Log.ActiveHealthProbeConstructionFailedOnCluster(_logger, destination.DestinationId, cluster.ClusterId, ex); return(new DestinationProbingResult(destination, null, ex)); } var cts = new CancellationTokenSource(timeout); try { Log.SendingHealthProbeToEndpointOfDestination(_logger, request.RequestUri, destination.DestinationId, cluster.ClusterId); var response = await cluster.Model.HttpClient.SendAsync(request, cts.Token); Log.DestinationProbingCompleted(_logger, destination.DestinationId, cluster.ClusterId, (int)response.StatusCode); return(new DestinationProbingResult(destination, response, null)); } catch (Exception ex) { Log.DestinationProbingFailed(_logger, destination.DestinationId, cluster.ClusterId, ex); return(new DestinationProbingResult(destination, null, ex)); } finally { cts.Dispose(); } }
private async Task ProbeCluster(ClusterInfo cluster) { var clusterConfig = cluster.Config; if (!clusterConfig.HealthCheckOptions.Active.Enabled) { return; } Log.StartingActiveHealthProbingOnCluster(_logger, cluster.ClusterId); // Policy must always be present if the active health check is enabled for a cluster. // It's validated and ensured by a configuration validator. var policy = _policies.GetRequiredServiceById(clusterConfig.HealthCheckOptions.Active.Policy); var allDestinations = cluster.DynamicState.AllDestinations; var probeTasks = new List <(Task <HttpResponseMessage> Task, CancellationTokenSource Cts)>(allDestinations.Count); try { foreach (var destination in allDestinations) { var timeout = clusterConfig.HealthCheckOptions.Active.Timeout ?? _monitorOptions.DefaultTimeout; var cts = new CancellationTokenSource(timeout); try { var request = _probingRequestFactory.CreateRequest(clusterConfig, destination.Config); Log.SendingHealthProbeToEndpointOfDestination(_logger, request.RequestUri, destination.DestinationId, cluster.ClusterId); probeTasks.Add((clusterConfig.HttpClient.SendAsync(request, cts.Token), cts)); } catch (Exception ex) { // Log and suppress an exception to give a chance for all destinations to be probed. Log.ActiveHealthProbeConstructionFailedOnCluster(_logger, destination.DestinationId, cluster.ClusterId, ex); cts.Dispose(); } } var probingResults = new DestinationProbingResult[probeTasks.Count]; for (var i = 0; i < probeTasks.Count; i++) { HttpResponseMessage response = null; ExceptionDispatchInfo edi = null; try { response = await probeTasks[i].Task; Log.DestinationProbingCompleted(_logger, allDestinations[i].DestinationId, cluster.ClusterId, (int)response.StatusCode); } catch (Exception ex) { edi = ExceptionDispatchInfo.Capture(ex); Log.DestinationProbingFailed(_logger, allDestinations[i].DestinationId, cluster.ClusterId, ex); } probingResults[i] = new DestinationProbingResult(allDestinations[i], response, edi?.SourceException); } policy.ProbingCompleted(cluster, probingResults); } catch (Exception ex) { Log.ActiveHealthProbingFailedOnCluster(_logger, cluster.ClusterId, ex); } finally { foreach (var probeTask in probeTasks) { try { try { probeTask.Cts.Cancel(); } catch (Exception ex) { // Suppress exceptions to ensure the task will be awaited. Log.ErrorOccuredDuringActiveHealthProbingShutdownOnCluster(_logger, cluster.ClusterId, ex); } var response = await probeTask.Task; response.Dispose(); } catch (Exception ex) { // Suppress exceptions to ensure all responses get a chance to be disposed. Log.ErrorOccuredDuringActiveHealthProbingShutdownOnCluster(_logger, cluster.ClusterId, ex); } finally { // Dispose CancellationTokenSource even if the response task threw an exception. // Dispose() is not expected to throw here. probeTask.Cts.Dispose(); } } Log.StoppedActiveHealthProbingOnCluster(_logger, cluster.ClusterId); } }