예제 #1
0
        private async Task <QueryResult <TResponse> > CallConsulAsync <TResponse>(Func <Task <QueryResult <TResponse> > > call,
                                                                                  string monitoringOperation, IDictionary <string, object> monitoringProperties)
        {
            using (var eventContext = new EventContext("ConsulRx.Client", monitoringOperation))
            {
                eventContext.AddValues(monitoringProperties);
                try
                {
                    var result = await call().ConfigureAwait(false);

                    eventContext.IncludeConsulResult(result);
                    if (!HealthyCodes.Contains(result.StatusCode))
                    {
                        //if we got an error that indicates either server or client aren't healthy (e.g. 500 or 403)
                        //then model this as an exception (same as if server can't be contacted). We will figure out what to do below
                        throw new ConsulErrorException(result);
                    }
                    return(result);
                }
                catch (Exception ex)
                {
                    eventContext.IncludeException(ex);
                    throw;
                }
            }
        }
예제 #2
0
        private IObservable <TObservation> LongPoll <TResponse, TObservation>(Func <ulong, Task <QueryResult <TResponse> > > poll, Func <QueryResult <TResponse>, TObservation> toObservation, string monitoringOperation, IDictionary <string, object> monitoringProperties) where TObservation : class
        {
            return(Observable.Create <TObservation>(async(o, cancel) =>
            {
                ulong index = default(ulong);
                var successfullyContactedConsulAtLeastOnce = false;
                while (true)
                {
                    using (var eventContext = new EventContext("ConsulRx.Client", monitoringOperation))
                    {
                        eventContext["RequestIndex"] = index;
                        eventContext.AddValues(monitoringProperties);
                        QueryResult <TResponse> result = null;
                        Exception exception = null;
                        try
                        {
                            result = await poll(index);
                        }
                        catch (Exception ex)
                        {
                            eventContext.IncludeException(ex);
                            exception = ex;
                        }
                        if (cancel.IsCancellationRequested)
                        {
                            eventContext["Cancelled"] = true;
                            o.OnCompleted();
                            return;
                        }
                        if (result != null)
                        {
                            index = result.LastIndex;
                            eventContext.IncludeConsulResult(result);
                            if (HealthyCodes.Contains(result.StatusCode))
                            {
                                //200 or 404 are the only response codes we should expect if consul and client are both configured properly
                                successfullyContactedConsulAtLeastOnce = true;
                            }
                            else
                            {
                                //if we got an error that indicates either server or client aren't healthy (e.g. 500 or 403)
                                //then model this as an exception (same as if server can't be contacted). We will figure out what to do below
                                exception = new ConsulErrorException(result);
                                eventContext.SetLevel(Level.Error);
                            }
                        }

                        if (exception == null)
                        {
                            o.OnNext(toObservation(result));
                        }
                        else
                        {
                            if (successfullyContactedConsulAtLeastOnce)
                            {
                                //if an error occurred, we reset the index so that we can start clean
                                //this is necessary because if the consul cluster was restarted it won't recognize
                                //the old index and will block until the longPollMaxWait expires
                                index = default(ulong);

                                //if we have been successful at contacting consul already, then we will retry under the assumption that
                                //things will eventually get healthy again
                                eventContext["SecondsUntilRetry"] = _retryDelay?.Seconds ?? 0;
                                if (_retryDelay != null)
                                {
                                    await Task.Delay(_retryDelay.Value, cancel);
                                }
                            }
                            else
                            {
                                //if we encountered an error at the very beginning, then we don't have enough confidence that retrying will actually help
                                //so we will stream the exception out and let the consumer figure out what to do
                                o.OnError(exception);
                                return;
                            }
                        }
                    }
                }
            }));
        }