API網關Ocelot 使用Polly 處理部分失敗問題

在實現API Gateway過程當中,另一個須要考慮的問題就是部分失敗。這個問題發生在分佈式系統中當一個服務調用另一個服務超時或者不可用的狀況。API Gateway不該該被阻斷並處於無限期等待下游服務的狀態。可是,如何處理這種失敗依賴於特定的場景和具體服務。若是是產品信息服務無響應,那麼API Gateway就應該給客戶端返回一個錯誤。html

Ocelot 是一個使用.NET Core平臺上的一個API Gateway,最近我在參與這個項目的開發,開發完成第一個就是使用Polly 處理部分失敗問題。各位同窗可能對Polly這個項目不熟悉,先簡單介紹下,Polly是.NET基金會下的一個開源項目,Polly記錄那些超過預設定的極限值的調用。它實現了 circuit break模 式,使得能夠將客戶端從無響應服務的無盡等待中中止。若是一個服務的錯誤率超過預設值,Polly 將中斷服務,而且在一段時間內全部請求馬上失效,Polly 能夠爲請求失敗定義一個fallback操做,例如讀取緩存或者返回默認值,有時候咱們須要調用其餘API的時候出現暫時鏈接不通超時的狀況,那這時候也能夠經過Polly進行Retry,具體信息參考 http://www.thepollyproject.org/2016/10/25/polly-5-0-a-wider-resilience-framework/git

Ocelot從實現上來講就是一系列的中間件組合,在HTTP請求到達Ocelot,通過一系列的中間件的處理轉發到下游的服務,其中負責調用下游服務的中間件是HttpRequestBuilderMiddleware,經過調用HttpClient請求下游的HTTP服務,咱們這裏就是要給HttpClient 的調用加上熔斷器功能,代碼參看https://github.com/TomPallister/Ocelot/pull/27/files ,主要的一段代碼以下:github

using Ocelot.Logging;
using Polly;
using Polly.CircuitBreaker;
using Polly.Timeout;
using System;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Ocelot.Requester
{
    public class CircuitBreakingDelegatingHandler : DelegatingHandler
    {
        private readonly IOcelotLogger _logger;
        private readonly int _exceptionsAllowedBeforeBreaking;
        private readonly TimeSpan _durationOfBreak;
        private readonly Policy _circuitBreakerPolicy;
        private readonly TimeoutPolicy _timeoutPolicy;

        public CircuitBreakingDelegatingHandler(int exceptionsAllowedBeforeBreaking, TimeSpan durationOfBreak,TimeSpan timeoutValue
            ,TimeoutStrategy timeoutStrategy, IOcelotLogger logger, HttpMessageHandler innerHandler)
            : base(innerHandler)
        {
            this._exceptionsAllowedBeforeBreaking = exceptionsAllowedBeforeBreaking;
            this._durationOfBreak = durationOfBreak;

            _circuitBreakerPolicy = Policy
                .Handle<HttpRequestException>()
                .Or<TimeoutRejectedException>()
                .Or<TimeoutException>()
                .CircuitBreakerAsync(
                    exceptionsAllowedBeforeBreaking: exceptionsAllowedBeforeBreaking,
                    durationOfBreak: durationOfBreak,
                    onBreak: (ex, breakDelay) =>
                    {
                        _logger.LogError(".Breaker logging: Breaking the circuit for " + breakDelay.TotalMilliseconds + "ms!", ex);
                    },
                    onReset: () => _logger.LogDebug(".Breaker logging: Call ok! Closed the circuit again."),
                    onHalfOpen: () => _logger.LogDebug(".Breaker logging: Half-open; next call is a trial.")
                    );
            _timeoutPolicy = Policy.TimeoutAsync(timeoutValue, timeoutStrategy);
            _logger = logger;
        }

        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            Task<HttpResponseMessage> responseTask = null;

            try
            {
                responseTask = Policy.WrapAsync(_circuitBreakerPolicy, _timeoutPolicy).ExecuteAsync<HttpResponseMessage>(() =>
                {
                    return  base.SendAsync(request,cancellationToken);
                });
                return responseTask;
            }
            catch (BrokenCircuitException ex)
            {
                _logger.LogError($"Reached to allowed number of exceptions. Circuit is open. AllowedExceptionCount: {_exceptionsAllowedBeforeBreaking}, DurationOfBreak: {_durationOfBreak}",ex);
                throw;
            }
            catch (HttpRequestException)
            {
                return responseTask;
            }
        }

        private static bool IsTransientFailure(HttpResponseMessage result)
        {
            return result.StatusCode >= HttpStatusCode.InternalServerError;
        }
    }
}

上面代碼咱們使用Policy.WrapAsync組合了熔斷器和重試的兩個策略來解決部分失敗問題,思路很簡單,定義須要處理的異常有哪些,好比 Policy.Handle<HttpRequestException>() .Or<TimeoutRejectedException>() .Or<TimeoutException>(),當異常發生時候須要如何處理,使用熔斷器仍是重試,上面這個代碼固然也是適合調用第三方服務用了。緩存

歡迎你們加入建設.NET Core的微服務開發框架。從給項目Ocelot 點贊和fork代碼開始,一塊兒來建設,春節我已經給項目貢獻了2個特性的代碼,服務發現和本文所講的熔斷器。框架

https://www.cnblogs.com/lwqlun/p/8119856.html  分佈式

相關文章
相關標籤/搜索