Example #1
0
 public void Handle(
     HandlingContext handlingContext,
     UpstreamPeer peer)
 {
     //负载均衡完成选择之后,由线程池线程完成接下来的http请求处理
     Task.Run(async() =>
     {
         var request     = handlingContext.Request;
         var tcs         = handlingContext.ResponsSource;
         var originalUri = request.RequestUri;
         //string originalScheme = current.Scheme;
         var response = default(HttpResponseMessage);
         try
         {
             var instance = peer?.Instance;
             var uri      = instance?.Uri;
             if (uri != null)
             {
                 request.RequestUri = new Uri(uri, originalUri.PathAndQuery);
                 lock (peer)
                 {
                     peer.Connections++;
                 }
             }
             var ct   = handlingContext.CancellationToken;
             response = await handlingContext.HandleAsync(request, ct);
             if (!await NextPeerAsync(handlingContext, peer, response, originalUri))
             {
                 tcs.TrySetResult(response);
             }
         }
         catch (TaskCanceledException ex)
         {
             tcs.SetException(ex);
             _logger.LogWarning(ex, "Ensure that this cancelation is not caused by timeout, or you may lost the chance to try another upstream peer.");
             return;
         }
         catch (TimeoutException ex)
         {
             if (!await NextPeerAsync(handlingContext, peer, response, originalUri, true))
             {
                 tcs.SetException(ex);
             }
         }
         catch (HttpRequestException ex)
         {
             if (!await NextPeerAsync(handlingContext, peer, response, originalUri))
             {
                 tcs.TrySetException(ex);
             }
         }
         catch (Exception ex)
         {
             _logger.LogError(ex, ex.Message);
             tcs.SetException(ex);
         }
         finally
         {
             FreePeer(handlingContext, peer, response);
         }
     });
 }
Example #2
0
        /// <returns>是否尝试了“下一个”上游服务端</returns>
        private async Task <bool> NextPeerAsync(
            HandlingContext handlingContext,
            UpstreamPeer peer,
            HttpResponseMessage response,
            Uri originUri,
            bool isTimeout = false)
        {
            if (peer == null)
            {
                return(false);
            }
            if (handlingContext.IsSingle)
            {
                return(false);
            }

            handlingContext.Start = handlingContext.Start ?? DateTime.Now;

            if (NextWhen.Never == (peer.NextWhen & NextWhen.Never))
            {
                return(false);
            }

            var request = handlingContext.Request;

            if (request.Method != HttpMethod.Get &&
                NextWhen.GetOnly == (peer.NextWhen & NextWhen.GetOnly))
            {
                return(false);
            }

            handlingContext.Tries = handlingContext.Tries ?? (peer.NextTries == 0 ? int.MaxValue : peer.NextTries);
            if (handlingContext.Tries == 0)
            {
                return(false);
            }

            var when = NextWhen.None;

            if (isTimeout)
            {
                when |= NextWhen.Timeout;
            }
            else if (response == null)
            {
                when |= NextWhen.Error;
            }
            else
            {
                if (response.IsSuccessStatusCode)
                {
                    return(false);
                }
                switch (response.StatusCode)
                {
                case HttpStatusCode.InternalServerError:
                    when |= NextWhen.Http500;
                    break;

                case HttpStatusCode.BadGateway:
                    when |= NextWhen.Http502;
                    break;

                case HttpStatusCode.ServiceUnavailable:
                    when |= NextWhen.Http503;
                    break;

                case HttpStatusCode.RequestTimeout:
                case HttpStatusCode.GatewayTimeout:
                    when |= NextWhen.Timeout;
                    break;

                case HttpStatusCode.Forbidden:
                    when |= NextWhen.Http403;
                    break;

                case HttpStatusCode.NotFound:
                    when |= NextWhen.Http404;
                    break;

                default:
                    return(false);
                }
            }

#if NETSTANDARD2_0
            if (request.Method == HttpMethod.Post || request.Method.Method == "PATCH")
#elif NETSTANDARD2_1
            if (request.Method == HttpMethod.Post || request.Method == HttpMethod.Patch)
#endif
            {
                when |= NextWhen.NonIdemponent;
            }

            if (when != (when & peer.NextWhen))
            {
                return(false);
            }

            if (DateTime.Now - handlingContext.Start >= peer.NextTimeout)
            {
                return(false);
            }

            handlingContext.Request = request.Clone(originUri);
            //保持上下文重试
            var next = await _dispatcher.AcceptThenDispatchAsync(handlingContext);

            handlingContext.Tries--;
            return(next);
        }
Example #3
0
        private void FreePeer(
            HandlingContext context,
            UpstreamPeer peer,
            HttpResponseMessage response)
        {
            if (peer == null)
            {
                return;
            }
            if (context.IsSingle)
            {
                return;
            }

            PeerState state = PeerState.Successful;

            if (response == null)
            {
                state = PeerState.Failed;
            }
            else
            {
                switch (response.StatusCode)
                {
                case HttpStatusCode.InternalServerError:
                case HttpStatusCode.BadGateway:
                case HttpStatusCode.ServiceUnavailable:
                    state = PeerState.Failed;
                    break;

                case HttpStatusCode.Forbidden:
                case HttpStatusCode.NotFound:
                default:
                    break;
                }
            }
            lock (peer)
            {
                peer.Connections--;
                if (state == PeerState.Successful)
                {
                    //上次失败后已经过了至少一个Check
                    if (peer.LastFailed < peer.Checked)
                    {
                        peer.Fails = 0;
                    }
                    return;
                }
                peer.Fails++;
                //失败刚刚发生,重置对该peer的冻结判定时间(now - Checked <= FailTimeout)
                peer.Checked    = DateTime.Now;
                peer.LastFailed = peer.Checked;
                //随着失败次数逐渐接近MaxFails,权重平滑渐小
                //如果MaxFails为1,则会导致权重立即衰减至0
                if (peer.MaxFails > 0)
                {
                    peer.EffectiveWeight -= peer.Weight / peer.MaxFails;
                    if (peer.Fails >= peer.MaxFails)
                    {
                        _logger.LogDebug($"Upstream peer temporarily freezed.");
                    }
                }
                if (peer.EffectiveWeight < 0)
                {
                    peer.EffectiveWeight = 0;
                }
            }
        }