上一篇介紹了使用Steeltoe來處理服務熔斷,這篇咱們將用Polly來處理服務熔斷。html
不廢話了,直接進正題。git
一樣先定義一個簡單的服務。github
[Route("api/[controller]")] public class ValuesController : Controller { // GET api/values [HttpGet] public string Get() { return "service--a"; } }
再來一個新服務去調用上面的服務。api
定義一個用於訪問服務的Service接口和實現。瀏覽器
public interface IAService { Task<string> GetAsync(); } public class AService : IAService { private PolicyWrap<string> _policyWrap; private ILogger _logger; public AService(ILoggerFactory loggerFactory) { _logger = loggerFactory.CreateLogger<AService>(); //超時 var timeout = Policy .TimeoutAsync(1, Polly.Timeout.TimeoutStrategy.Pessimistic, (context, ts, task) => { _logger.LogInformation("AService timeout"); return Task.CompletedTask; }); //熔斷 var circuitBreaker = Policy .Handle<Exception>() .CircuitBreakerAsync(2, TimeSpan.FromSeconds(5), (ex, ts) => { _logger.LogInformation($"AService OnBreak -- ts = {ts.Seconds}s ,ex.message = {ex.Message}"); }, () => { _logger.LogInformation("AService OnReset"); }); //Fallback + 熔斷 + 超時 _policyWrap = Policy<string> .Handle<Exception>() .FallbackAsync(GetFallback(), (x) => { _logger.LogInformation($"AService Fallback -- {x.Exception.Message}"); return Task.CompletedTask; }) .WrapAsync(circuitBreaker) .WrapAsync(timeout); } //降級處理 private string GetFallback() { return "fallback"; } public async Task<string> GetAsync() { return await _policyWrap.ExecuteAsync(() => { return QueryAsync(); }); } private async Task<string> QueryAsync() { using (var client = new HttpClient()) { var res = await client.GetStringAsync("http://localhost:9001/api/values"); return res; } } }
要注意的有幾個地方。async
Polly沒有既包含熔斷又包含降級又包含超時的,這個須要本身去組合。相對來講,Hystrix在這一方面彷佛好一點點。ui
可是,各有各的好,Polly分離了每個模塊,讓咱們自由組合,也是很靈活的。code
因此能夠看到,咱們定義了3個Policy,再把它們Wrap起來。orm
另外,還在觸發每個Policy的時候,都會輸出相應的日記,方便咱們後面看效果。htm
對於寫日記這一塊,我的認爲對比Steeltoe,Polly的方式要更加方便和簡單。
下面是控制器的使用。
// GET api/values [HttpGet] public async Task<string> A([FromServices]IAService aService) { return await aService.GetAsync(); }
還有一個關鍵的步驟:在Startup註冊咱們的AService。
services.AddSingleton<IAService, AService>();
切記是Singleton!否則熔斷就不會起做用了!!
直接上效果圖
簡單說明一下這張圖,一開始,服務A和調用方都是正常的,後面中斷服務A,使其不可用,這個時候調用方就會走降級處理。
調用方多請求幾回,就能夠看到OnBreak的日記輸出,說明斷路器已經處於Open狀態,不會直接走真正的請求,而是走的Fallback。
最後啓動服務A,能夠看到OnReset的日記輸出,說明斷路器已經處於Closed狀態了,瀏覽器顯示的也是服務A的返回結果。
再來模擬一下超時的情形。
由於上面設置的超時時間是1秒,因此讓其休息1001毫秒就能夠模擬了。
// GET api/values [HttpGet] public string Get() { System.Threading.Thread.Sleep(1001); return "service--a"; }
再來看看效果圖
調用方一直是提示由於超時而降級,而熔斷。從日記也能夠看出,是由於超時而致使熔斷的。
前面還提到一個註冊服務的問題,這裏解釋一下爲何咱們要讓其註冊成Singleton?
咱們先把註冊服務這一塊調整爲不是Singleton,這裏以Scope爲例,Transient也是同樣的。
public void ConfigureServices(IServiceCollection services) { services.AddScoped<IAService, AService>(); //services.AddSingleton<IAService, AService>(); services.AddMvc(); }
效果以下:
能夠看到,日記一直輸出超時!並無提示熔斷相關的信息!這說明咱們設置的熔斷並無起做用!!
這個問題與實例的生命週期有着密不可分的關係!
試想一下,若是每次請求,都建立一個AService的實例,一樣的每次都會從新建立一個新的熔斷器,那熔斷還會生效嗎?
反之,若是熔斷器只有一個,那麼不管發起多少次請求,它都是惟一的,因此它才能統計到有多少次異常,從而去觸發熔斷。
這也是一個咱們須要特別注意的地方。否則一個不當心就入坑了。
Polly用起來仍是比較簡單,比較靈活的,咱們能夠組合多種不一樣的Policy來達到咱們想要的結果。
本文的示例代碼: