netcore micro fuse and achieve service Polly downgrade mechanism

The basic use of Polly

Polly is a .NET elastic and transient fault handling library that allows us to very smooth and thread-safe way to perform such as row retry, disconnect, time out, fault recovery strategy. For Polly for .NET 4.0, .NET 4.5 and .NET Standard 1.1 and .NET Core implementation of the project has now become a .NET Foundation, the project has been in constant iteration and update the project address https: // github .com / App-vNext / Polly

 

The library implements seven recovery strategy, here I introduce to you one by one.

Retry strategy (Retry)

Preconditions for the retry strategy is a short delay fault and can be self-correcting after a short delay. Allows us to do is to be able to automatically configure the retry mechanism.

Breaker (Circuit-breaker)

Preconditions for policy breaker is when the system is busy, rapid response to failure than to let users have been waiting for better. Fault protection system from overload, Polly can help their recovery.

Timeout (Timeout)

Preconditions for the timeout policy is more than a certain waiting time, you want to get a successful result is not possible, to ensure that the caller does not have to wait for a timeout.

Partition isolation (Bulkhead Isolation)

Preconditions for partition isolation is when a process fails, the failure has been on multiple resources (such as thread / CPU) has been occupied in the host. Downstream system failure may also lead to an upstream failure. Both risk will result in serious consequences. He said a rat feces sub muddy gruel, and Polly will control the operation of a fixed limit of the resource pool, free resources affected by the other.

Cache (Cache)

Preconditions for caching strategy is that the data will not be updated very frequently, in order to avoid overloading the system, the response data cache when you first load data, if present in the cache is read directly from the cache.

Fallback (Fallback)

Operation will still fail, that when this happens we are going to do. That is the definition of failure to return the operation.

Policy Package (PolicyWrap)

Preconditions for policy package is different faults require different strategies, which means using a combination of elastic and flexible.

 

userapi add items Resilience

NuGet>Install-Package Polly

Add interface IHttpClient

public interface IHttpClient
    {
        Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken=null, string requestId = null, string authorizationMethod = null);
        Task<HttpResponseMessage> PostAsync(string uri, Dictionary<string,string> form, string authorizationToken=null, string requestId = null, string authorizationMethod = null);

        Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer");
        Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");

        Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer");
    }

Add Class ResilientHttpClient

public class ResilientHttpClient : IHttpClient
    {
        private readonly HttpClient _client;

        //根据url origin创建policy
        private readonly Func<string, IEnumerable<Policy>> _policyCreator;
        //把policy打包成组合policy wraper,进行本地缓存
        private readonly ConcurrentDictionary<string, PolicyWrap> _policyWrappers;
        private ILogger<ResilientHttpClient> _logger;
        private IHttpContextAccessor _httpContextAccessor;
        public ResilientHttpClient(Func<string, IEnumerable<Policy>> policyCreator, ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor)
        {
            _client = new HttpClient();
            _policyCreator = policyCreator;
            _policyWrappers = new ConcurrentDictionary<string, PolicyWrap>();
            _logger = logger;
            _httpContextAccessor = httpContextAccessor;
        }


        public async  Task<HttpResponseMessage> PostAsync<T>(string uri, T item, string authorizationToken=null, string requestId = null, string authorizationMethod = "Bearer")
        {
            Func<HttpRequestMessage> requestMessage = () => CreateHttpRequestMessage(HttpMethod.Post, uri, item);
            return await DoPostPutAsync(HttpMethod.Post, uri, requestMessage, authorizationToken, requestId, authorizationMethod);
        }
        public async Task<HttpResponseMessage> PostAsync(string uri, Dictionary<string, string> form, string authorizationToken=null, string requestId = null, string authorizationMethod = null)
        {
          Func<HttpRequestMessage> requestMessage =()=>CreateHttpRequestMessage(HttpMethod.Post, uri, form);
            return await DoPostPutAsync(HttpMethod.Post,uri, requestMessage, authorizationToken, requestId, authorizationMethod);
        }

        public Task<HttpResponseMessage> PutAsync<T>(string uri, T item, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
        {
            Func<HttpRequestMessage> requestMessage = () => CreateHttpRequestMessage(HttpMethod.Put, uri, item);
            return DoPostPutAsync(HttpMethod.Put, uri, requestMessage, authorizationToken, requestId, authorizationMethod);
        }

        public Task<string> GetStringAsync(string uri, string authorizationToken = null, string authorizationMethod = "Bearer")
        {
            var origin = GetOriginFromUri(uri);

            return HttpInvoker(origin, async () =>
            {
                var requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);

                if (authorizationToken != null)
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
                }

                var response = await _client.SendAsync(requestMessage);

                return await response.Content.ReadAsStringAsync();
            });
        }
        public Task<HttpResponseMessage> DeleteAsync(string uri, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
        {
            var origin = GetOriginFromUri(uri);

            return HttpInvoker(origin, async () =>
            {
                var requestMessage = new HttpRequestMessage(HttpMethod.Delete, uri);

                if (authorizationToken != null)
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
                }

                if (requestId != null)
                {
                    requestMessage.Headers.Add("x-requestid", requestId);
                }

                return await _client.SendAsync(requestMessage);
            });
        }

        private Task<HttpResponseMessage> DoPostPutAsync(HttpMethod method,string uri,Func<HttpRequestMessage>requestMessageAction, string authorizationToken = null, string requestId = null, string authorizationMethod = "Bearer")
        {
            if (method != HttpMethod.Post && method != HttpMethod.Put)
            {
                throw new ArgumentException("Value must be either post or put.", nameof(method));
            }

            //每次重试都必须创建新的stringcontent
            //每次通讯后都会处理
            var origin = GetOriginFromUri(uri);

            return HttpInvoker(origin, async () =>
            {
                HttpRequestMessage requestMessage = requestMessageAction();
              
                SetAuthorizationHeader(requestMessage);
                if (authorizationToken != null)
                {
                    requestMessage.Headers.Authorization = new AuthenticationHeaderValue(authorizationMethod, authorizationToken);
                }
                if (requestId != null)
                {
                    requestMessage.Headers.Add("x-requestid", requestId);
                }

                var response = await _client.SendAsync(requestMessage);
                // 如果httpresponsecode 500,则引发异常
                // 断路器跟踪故障所需
                if (response.StatusCode == HttpStatusCode.InternalServerError)
                {
                    throw new HttpRequestException();
                }
                return response;
            });
        }

        private async Task<T> HttpInvoker<T>(string origin, Func<Task<T>> action)
        {
            var normalizedOrigin = NormalizeOrigin(origin);

            if (!_policyWrappers.TryGetValue(normalizedOrigin, out PolicyWrap policyWrap))
            {
                policyWrap = Policy.WrapAsync(_policyCreator(normalizedOrigin).ToArray());
                _policyWrappers.TryAdd(normalizedOrigin, policyWrap);
            }

            // 执行应用全部的操作
            // 包装器中定义的策略 
            return await policyWrap.ExecuteAsync(action, new Context(normalizedOrigin));
        }

        private HttpRequestMessage CreateHttpRequestMessage<T>(HttpMethod method,string url, T item)
        {
            return new HttpRequestMessage(method, url)
            {
                Content = new StringContent(JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json")
            };
        }

        private HttpRequestMessage CreateHttpRequestMessage(HttpMethod method, string url, Dictionary<string, string> form)
        {
            return new HttpRequestMessage(method, url) { Content = new FormUrlEncodedContent(form) };
        }

        private static string NormalizeOrigin(string origin)
        {
            return origin?.Trim().ToLower();
        }

        private static string GetOriginFromUri(string uri)
        {
            var url = new Uri(uri);
            var origin = $"{url.Scheme}://{url.DnsSafeHost}:{url.Port}";
            return origin;
        }

        private void SetAuthorizationHeader(HttpRequestMessage requestMessage)
        {
            var authorizationHeader = _httpContextAccessor.HttpContext.Request.Headers["Authorization"];
            if (!string.IsNullOrEmpty(authorizationHeader))
            {
                requestMessage.Headers.Add("Authorization", new List<string>() { authorizationHeader });
            }
        }


    }

User.Identity Add Folder Infrastructure Project 

New ResilienceHttpClientFactory

   public class ResilienceHttpClientFactory
    {
        private ILogger<ResilientHttpClient> _logger;
        private IHttpContextAccessor _httpContextAccessor;
        private int _retryCount;
        private  int _exceptionCountAllowedBeforeBreacking;

        public ResilienceHttpClientFactory(ILogger<ResilientHttpClient> logger, IHttpContextAccessor httpContextAccessor, int retryCount,int exceptionCountAllowedBeforeBreacking)
        {
            _logger = logger;
            _httpContextAccessor = httpContextAccessor;
            _retryCount = retryCount;
            _exceptionCountAllowedBeforeBreacking = exceptionCountAllowedBeforeBreacking;
        }

        public ResilientHttpClient GetResilienceHttpClient()
        {
            return new ResilientHttpClient(origin => CreatePolicy(origin), _logger, _httpContextAccessor);
        }

        private Policy[] CreatePolicy(string origin)
        {
            return new Policy[]{
                Policy.Handle<HttpRequestException>().WaitAndRetryAsync(
                    _retryCount,
                    retryAttempt=>TimeSpan.FromSeconds(Math.Pow(2,retryAttempt)),
                (exception,timeSpan,retryCount,context)=>
                {
                    var msg=$"第{retryCount}implemented with polly's  retryPolicy"+
                    $"of {context.PolicyKey}"+
                    $"at {context.ExecutionKey}"+
                    $"due to:{exception}.";
                    _logger.LogWarning(msg);
                    _logger.LogDebug(msg);
                }
                ),
                Policy.Handle<HttpRequestException>().CircuitBreakerAsync(_exceptionCountAllowedBeforeBreacking,TimeSpan.FromMinutes(1),
                (excpeiton,duration)=>{
                    _logger.LogTrace("熔断器打开");
                },()=>
                {
                   _logger.LogTrace("");
                })

        };
        }
    }

Startup modification

ConfigureServices added service registration

  //注册全局单利 ResilienceHttpClientFactory
            services.AddSingleton(typeof(ResilienceHttpClientFactory), sp =>
            {
                var logger = sp.GetRequiredService<ILogger<ResilientHttpClient>>();
                var httpcontextAccesser = sp.GetRequiredService<IHttpContextAccessor>();
                var retryCount = 5;
                var exceptionCountAllowedBeforeBreacking = 5;
                return new ResilienceHttpClientFactory(logger, httpcontextAccesser, retryCount, exceptionCountAllowedBeforeBreacking);
            });

            //注册全局单利IHttpClient
            services.AddSingleton<IHttpClient>(sp =>
            {
                return sp.GetRequiredService<ResilienceHttpClientFactory>().GetResilienceHttpClient();
            });

  UserService HttpClient modify the project Resilience IHttpClient

 

The transformation is completed run the project

Abnormal counting system default recording allowed, and retry mechanism

normal operation

 

 

When abnormal retry

 

 

git :   https://gitee.com/LIAOKUI/user.api

Reference: https: //github.com/HoussemDellai/ResilientHttpClient

       https://github.com/App-vNext/Polly

      https://blog.csdn.net/guwei9111986/article/details/51649240

 

Guess you like

Origin www.cnblogs.com/liaokui/p/11563208.html