上一篇介绍了使用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来达到咱们想要的结果。
本文的示例代码: