<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          推薦學(xué)習(xí):Flurl使用Polly實(shí)現(xiàn)重試Policy!

          共 20756字,需瀏覽 42分鐘

           ·

          2021-04-07 11:11

          ?

          在使用Flurl作為HttpClient向Server請(qǐng)求時(shí),由于網(wǎng)絡(luò)或者其它一些原因?qū)е抡?qǐng)求會(huì)有失敗的情況,比如HttpStatusCode.NotFoundHttpStatusCode.ServiceUnavailable、HttpStatusCode.RequestTimeout等;網(wǎng)絡(luò)上有比較多的HttpClientFactory使用Polly來(lái)實(shí)現(xiàn)重試的內(nèi)容,奈何已經(jīng)習(xí)慣使用Flurl的人,要全部換回到IHttpClient的確有不方便的地方,因?yàn)楸疚氖褂肍lurl的Polly來(lái)實(shí)現(xiàn)重試機(jī)制做一個(gè)整理;

          不使用Polly來(lái)測(cè)試

          1. 提供一個(gè)接口以便做請(qǐng)求測(cè)試

            [Route("api/[controller]")]
            [ApiController]
            public class PollyController : ControllerBase
            {
                private readonly ILogger<PollyController> _logger;

                public PollyController(ILogger<PollyController> logger)
                {
                    _logger = logger;
                }

                // GET: api/<PollyController>
                [HttpGet]
                public IActionResult Get()
                {
                    var random = new Random().Next(08);
                    switch (random)
                    {
                        case 0:
                            _logger.LogInformation("About to serve a 404");
                            return StatusCode(StatusCodes.Status404NotFound);

                        case 1:
                            _logger.LogInformation("About to serve a 503");
                            return StatusCode(StatusCodes.Status503ServiceUnavailable);

                        case 2:
                            _logger.LogInformation("Sleeping for 10 seconds then serving a 504");
                            Thread.Sleep(10000);
                            _logger.LogInformation("About to serve a 504");
                            return StatusCode(StatusCodes.Status504GatewayTimeout);

                        default:
                            _logger.LogInformation("About to correctly serve a 200 response");
                            return Ok(new {time = DateTime.Now.ToLocalTime()});
                    }
                }
            }
          2. 創(chuàng)建一個(gè)請(qǐng)求客戶端

            public class HomeController : Controller
            {
                private readonly ILogger<HomeController> _logger;

                public HomeController(ILogger<HomeController> logger)
                {
                    _logger = logger;
                }

                public async Task<IActionResult> Index()
                {
                    try
                    {
                        var time = await "http://127.0.0.1:5000/api/polly"
                            .GetJsonAsync();

                        _logger.LogInformation($"App: success - {time.time}");
                        return View(time.time);
                    }
                    catch (Exception e)
                    {
                        _logger.LogWarning($"App: failed - {e.Message}");
                        throw;
                    }
                }
            }
          3. 嘗試請(qǐng)求,可以發(fā)現(xiàn)有很多請(qǐng)求失敗的地方,這個(gè)情況很不理想,服務(wù)器有較大的機(jī)率不能正常的響應(yīng)

            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to serve a 404
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to correctly serve a 200 response
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to correctly serve a 200 response
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to correctly serve a 200 response
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to serve a 503
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to serve a 503
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to correctly serve a 200 response
            info: SuppertRcsInterfaceTest.Controllers.PollyController[0]
                  About to serve a 404
            ?

            針對(duì)這個(gè)情況有沒(méi)有什么解決的辦法呢,答案是肯定的,粗暴的想法就是失敗了再重新做請(qǐng)求,直接在Flurl的返回結(jié)果中做這個(gè)邏輯處理會(huì)比較麻煩也不方便統(tǒng)一的管理,如此就找到了Polly

          使用Polly來(lái)測(cè)試

          1. 首先安裝Polly, Install-Package Polly

          2. 下面先給出Polly的簡(jiǎn)單介紹后接著給出Policy的代碼片段

            ?

            Polly的七種策略:重試、斷路、超時(shí)、隔離、回退和緩存策略,本文使用到了重試、超時(shí)策略

            重試(Retry):出現(xiàn)故障自動(dòng)重試,這個(gè)是常見的場(chǎng)景

            斷路(Circuit-breaker):當(dāng)系統(tǒng)遇到嚴(yán)重的問(wèn)題時(shí),快速回饋失敗比讓用戶/調(diào)用者等待要好,限制系統(tǒng)出錯(cuò)的消耗,有助于系統(tǒng)恢復(fù),比如,當(dāng)我們?nèi)フ{(diào)用一個(gè)第三方的API,有很長(zhǎng)一段時(shí)間API都沒(méi)有響應(yīng),可能對(duì)方服務(wù)器癱瘓了,如果我們的系統(tǒng)還不停地重試,不僅會(huì)加重系統(tǒng)的負(fù)擔(dān),還有可能導(dǎo)致系統(tǒng)其它任務(wù)受影響,因此,當(dāng)系統(tǒng)出錯(cuò)的次數(shù)超過(guò)了指定的閾值,就得中斷當(dāng)前線程,等待一段時(shí)間后再繼續(xù);比如: Policy.Handle<SomeException>().CircuitBreaker(2, TimeSpan.FromMinutes(1));表示當(dāng)系統(tǒng)出現(xiàn)兩次某個(gè)異常時(shí)就停下來(lái),等待1分鐘后再繼續(xù),還可以在斷路時(shí)定義中斷的回調(diào)和重啟的回調(diào)。

            超時(shí)(Timeout):當(dāng)系統(tǒng)超過(guò)一定時(shí)間的等待,就可以判斷不可能會(huì)有成功的結(jié)果;比如平時(shí)一個(gè)網(wǎng)絡(luò)請(qǐng)求瞬間就完成了,如果有一次網(wǎng)絡(luò)請(qǐng)求超過(guò)了30秒還沒(méi)有完成,我們就可以判定不可能會(huì)返回成功的結(jié)果了,因此,我們需要設(shè)置系統(tǒng)的超時(shí)時(shí)間,避免系統(tǒng)長(zhǎng)時(shí)間無(wú)謂的等待;比如:Policy.Timeout(30, (context, span, task) => {// do something});表示設(shè)置了超時(shí)時(shí)間不能超過(guò)30秒,否則就認(rèn)為是錯(cuò)誤的結(jié)果,并執(zhí)行回調(diào)。

            隔離(Bulkhead Isolation):當(dāng)系統(tǒng)的一處出現(xiàn)故障時(shí),可能觸發(fā)多個(gè)失敗的調(diào)用,對(duì)資源有較大的消耗,下游系統(tǒng)出現(xiàn)故障可能導(dǎo)致上游的故障的調(diào)用,甚至可能蔓延到導(dǎo)致系統(tǒng)崩潰,所以要將可控的操作限制在一個(gè)固定大小的資源池中,以隔離有潛在可能相互影響的操作;比如:Policy.Bulkhead(12, context => {// do something});表示最多允許12個(gè)線程并發(fā)執(zhí)行,如果執(zhí)行被拒絕,則執(zhí)行回調(diào)。

            回退(Fallback):有些錯(cuò)誤無(wú)法避免,就要有備用的方案,當(dāng)無(wú)法避免的錯(cuò)誤發(fā)生時(shí),我們要有一個(gè)合理的返回來(lái)代替失敗;比如:Policy.Handle<Whatever>().Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar());表示當(dāng)用戶沒(méi)有上傳頭像時(shí),我們就給他一個(gè)默認(rèn)頭像。

            緩存(Cache):一般我們會(huì)把頻繁使用且不會(huì)怎么變化的資源緩存起來(lái),以提高系統(tǒng)的響應(yīng)速度,如果不對(duì)緩存資源的調(diào)用進(jìn)行封裝,那么我們調(diào)用的時(shí)候就要先判斷緩存中有沒(méi)有這個(gè)資源,有的話就從緩存返回,否則就從資源存儲(chǔ)的地方獲取后緩存起來(lái)再返回,而且有時(shí)還要考慮緩存過(guò)期和如何更新緩存的問(wèn)題;Polly提供了緩存策略的支持,使得問(wèn)題變得簡(jiǎn)單。

            策略包(Policy Wrap):一種操作會(huì)有多種不同的故障,而不同的故障處理需要不同的策略,這些不同的策略必須包在一起,作為一個(gè)策略包,才能應(yīng)用在同一種操作上,這就是Polly的彈性特性,即各種不同的策略能夠靈活地組合起來(lái)

            更多...

            using System;
            using System.Linq;
            using System.Net;
            using System.Net.Http;
            using System.Threading;
            using System.Threading.Tasks;
            using Flurl.Http.Configuration;
            using Microsoft.Extensions.Logging;
            using Polly;
            using Polly.Retry;
            using Polly.Timeout;
            using Polly.Wrap;

            namespace WithPollyClient.Services
            {
                public class Policies
                {
                    private readonly ILogger<Policies> _logger;

                    public Policies(ILogger<Policies> logger)
                    {
                        _logger = logger;
                    }
                    
                    private AsyncTimeoutPolicy<HttpResponseMessage> TimeoutPolicy
                    {
                        get
                        {
                            return Policy.TimeoutAsync<HttpResponseMessage>(3, (context, span, task) =>
                            {
                                _logger.LogInformation($"Policy: Timeout delegate fired after {span.Seconds} seconds");
                                return Task.CompletedTask;
                            });
                        }
                    }

                    private AsyncRetryPolicy<HttpResponseMessage> RetryPolicy
                    {
                        get
                        {
                            HttpStatusCode[] retryStatus =
                            {
                                HttpStatusCode.NotFound,
                                HttpStatusCode.ServiceUnavailable,
                                HttpStatusCode.RequestTimeout
                            };
                            return Policy
                                .HandleResult<HttpResponseMessage>(r => retryStatus.Contains(r.StatusCode))
                                .Or<TimeoutRejectedException>()
                                .WaitAndRetryAsync(new[]
                                {
                                    // 表示重試3次,第一次1秒后重試,第二次2秒后重試,第三次4秒后重試
                                    TimeSpan.FromSeconds(1),
                                    TimeSpan.FromSeconds(2),
                                    TimeSpan.FromSeconds(4)
                                }, (result, span, count, context) =>
                                {
                                    _logger.LogInformation($"Policy: Retry delegate fired, attempt {count}");
                                });
                        }
                    }

                    public AsyncPolicyWrap<HttpResponseMessage> PolicyStrategy =>
                        Policy.WrapAsync(RetryPolicy, TimeoutPolicy);
                }

                public class PolicyHandler : DelegatingHandler
                {
                    private readonly Policies _policies;

                    public PolicyHandler(Policies policies)
                    {
                        _policies = policies;
                    }
                    
                    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
                    {
                        return _policies.PolicyStrategy.ExecuteAsync(ct => base.SendAsync(request, ct), cancellationToken);
                    }
                }

                public class PollyHttpClientFactory : DefaultHttpClientFactory
                {
                    private readonly Policies _policies;

                    public PollyHttpClientFactory(Policies policies)
                    {
                        _policies = policies;
                    }
                    
                    public override HttpMessageHandler CreateMessageHandler()
                    {
                        return new PolicyHandler(_policies)
                        {
                            InnerHandler = base.CreateMessageHandler()
                        };
                    }
                }
            }
          3. 接下來(lái)在Starup中對(duì)Flurl進(jìn)行配置

            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllersWithViews();
                services.AddSingleton<Policies>();
            }

            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                var policies = app.ApplicationServices.GetService<Policies>();
                FlurlHttp.Configure(setting =>
                                    setting.HttpClientFactory = new PollyHttpClientFactory(policies));
                ......
          4. 再次嘗試請(qǐng)求,可以看到結(jié)果非常之理想

            WithPollyClient.Services.Policies: Information: Policy: Retry delegate fired, attempt 1
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:14
            WithPollyClient.Services.Policies: Information: Policy: Retry delegate fired, attempt 1
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:17
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:22
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:23
            WithPollyClient.Services.Policies: Information: Policy: Retry delegate fired, attempt 1
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:25
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:31
            WithPollyClient.Services.Policies: Information: Policy: Retry delegate fired, attempt 1
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:34
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:39
            WithPollyClient.Services.Policies: Information: Policy: Retry delegate fired, attempt 1
            WithPollyClient.Services.Policies: Information: Policy: Timeout delegate fired after 3 seconds
            WithPollyClient.Services.Policies: Information: Policy: Retry delegate fired, attempt 2
            WithPollyClient.Controllers.HomeController: Information: App: success - 2021/3/14 16:50:46

          富客戶端中使用的情況

          ?

          有時(shí)候呢,例如在WPF或者是其它的富客戶端上面也會(huì)經(jīng)常使用到Flurl的情況,如下

          var time = await Policy
              .Handle<FlurlHttpException>()
              .OrResult<IFlurlResponse>(r => !r.ResponseMessage.IsSuccessStatusCode)
              .WaitAndRetryAsync(new[]
                                 {
                                     TimeSpan.FromSeconds(1),
                                     TimeSpan.FromSeconds(2),
                                     TimeSpan.FromSeconds(4)
                                 }, (result, span, count, context) =>
                                 {
                                     _logger.LogInformation(count.ToString());
                                 })
              .ExecuteAsync(() => "http://127.0.0.1:5000/api/polly".WithTimeout(3).GetAsync())
              .ReceiveJson();

          _logger.LogInformation($"App: success - {time.time}");
          return View(time.time);







          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【被刪】學(xué)個(gè)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù) 【2019】獲取2019 .NET 開發(fā)者峰會(huì)資料PPT
          回復(fù) 【加群】加入dotnet微信交流群

          谷歌靈魂插件,98%的程序員都好評(píng)!


          再見Vip,免費(fèi)看網(wǎng)飛影視大片!


          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  2014天堂网 | 亚洲日本欧美视频 | 男女操逼网站国产 | av电影天堂在线 AV黄色在线网站 av麻豆成人电影 | 日本黄色免费网站 |