public void BulkHeadRejectsExecutionWhenQueueFull() { _processed.Clear(); _rejected.Clear(); bool exceptionThrown = false; var bulkHead = Policy.Bulkhead(1, 5, context => { var id = context["id"]; Console.Out.WriteLine($"Rejected id {id}"); _rejected.Add((int)id); }); // max concurrency of 2, max queue size of 5 Parallel.ForEach(_testData, id => { try { var context = new Polly.Context { ["id"] = id }; bulkHead.Execute((ctx) => SlowFunction(id), context); } catch (BulkheadRejectedException) { // keep demo running exceptionThrown = true; } }); Assert.IsTrue(exceptionThrown); Assert.IsTrue(_processed.Count > 0); Assert.IsTrue(_rejected.Count > 0); Assert.IsTrue(_rejected.Count > _processed.Count); // we will always reject more since method takes 1 second }
public void Should_call_onBulkheadRejected_with_passed_context() { string operationKey = "SomeKey"; Context contextPassedToExecute = new Context(operationKey); Context contextPassedToOnRejected = null; Action <Context> onRejected = ctx => { contextPassedToOnRejected = ctx; }; using (BulkheadPolicy <int> bulkhead = Policy.Bulkhead <int>(1, onRejected)) { TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(); using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) { Task.Run(() => { bulkhead.Execute(() => { tcs.Task.Wait(); return(0); }); }); Within(CohesionTimeLimit, () => Expect(0, () => bulkhead.BulkheadAvailableCount, nameof(bulkhead.BulkheadAvailableCount))); bulkhead.Invoking(b => b.Execute(_ => 1, contextPassedToExecute)).Should().Throw <BulkheadRejectedException>(); cancellationSource.Cancel(); tcs.SetCanceled(); } contextPassedToOnRejected.Should().NotBeNull(); contextPassedToOnRejected.OperationKey.Should().Be(operationKey); contextPassedToOnRejected.Should().BeSameAs(contextPassedToExecute); } }
public PrintPollyBulkhead() { bulkheadPolicy = Policy.Bulkhead(2, 2, onBulkheadRejected: (context) => { Console.WriteLine(context.Count); }); }
public void Should_call_onBulkheadRejected_with_passed_context() { string executionKey = Guid.NewGuid().ToString(); Context contextPassedToExecute = new Context(executionKey); Context contextPassedToOnRejected = null; Action <Context> onRejected = ctx => { contextPassedToOnRejected = ctx; }; BulkheadPolicy <int> bulkhead = Policy.Bulkhead <int>(1, onRejected); TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(); using (CancellationTokenSource cancellationSource = new CancellationTokenSource()) { Task.Run(() => { bulkhead.Execute(() => { tcs.Task.Wait(); return(0); }); }); Within(shimTimeSpan, () => bulkhead.BulkheadAvailableCount.Should().Be(0)); // Time for the other thread to kick up and take the bulkhead. bulkhead.Invoking(b => b.Execute(() => 1, contextPassedToExecute)).ShouldThrow <BulkheadRejectedException>(); cancellationSource.Cancel(); tcs.SetCanceled(); } contextPassedToOnRejected.Should().NotBeNull(); contextPassedToOnRejected.ExecutionKey.Should().Be(executionKey); contextPassedToOnRejected.Should().BeSameAs(contextPassedToExecute); }
private static void _04_隔離() { var bulkheadPolicy = Policy.Bulkhead(1, 1, context => { var msg = $"Reject:{context.PolicyKey}"; Console.WriteLine(msg); }); Console.WriteLine("請求網路資源中..."); Task.Factory .StartNew(() => { bulkheadPolicy.Execute(() => { Console.WriteLine("1.Execute Task,休息一下"); Thread.Sleep(TimeSpan.FromSeconds(5)); }); }); Task.Factory .StartNew(() => { bulkheadPolicy.Execute(() => { Console.WriteLine("2.Execute Task"); }); }); Task.Factory .StartNew(() => { bulkheadPolicy.Execute(() => { Console.WriteLine("3.Execute Task"); }); }); Console.WriteLine("隔離,完成"); }
/// <summary> /// 构造函数 /// </summary> /// <param name="maxParallelization">此Policy可以最多并行执行的action 大于0</param> /// <param name="maxQueuingActions">The maxmimum number of actions that may be queuing, waiting for an execution slot. 大于等于0</param> /// <param name="onBulkheadRejected">当过多的action超出限制时执行的回调操作</param> public BulkheadEx(int maxParallelization, int maxQueuingActions, Action <Context> onBulkheadRejected = null) { _bulkhead = Policy.Bulkhead(maxParallelization, maxQueuingActions, context => { onBulkheadRejected?.Invoke(context); }); }
public void Should_call_onBulkheadRejected_with_passed_context() { string operationKey = "SomeKey"; Context contextPassedToExecute = new Context(operationKey); Context contextPassedToOnRejected = null; Action <Context> onRejected = ctx => { contextPassedToOnRejected = ctx; }; using (BulkheadPolicy bulkhead = Policy.Bulkhead(1, onRejected)) { TaskCompletionSource <object> tcs = new TaskCompletionSource <object>(); Task.Run(() => { bulkhead.Execute(() => { tcs.Task.Wait(); }); }); // Time for the other thread to kick up and take the bulkhead. Within(CohesionTimeLimit, () => Expect(0, () => bulkhead.BulkheadAvailableCount, nameof(bulkhead.BulkheadAvailableCount))); bulkhead.Invoking(b => b.Execute(ctx => { }, contextPassedToExecute)).Should() .Throw <BulkheadRejectedException>(); tcs.SetCanceled(); contextPassedToOnRejected.Should().NotBeNull(); contextPassedToOnRejected.OperationKey.Should().Be(operationKey); contextPassedToOnRejected.Should().BeSameAs(contextPassedToExecute); } }
public override AsyncUnaryCall <TResponse> AsyncUnaryCall <TRequest, TResponse>(TRequest request, ClientInterceptorContext <TRequest, TResponse> context, AsyncUnaryCallContinuation <TRequest, TResponse> continuation) { var pollyRetry = Policy <AsyncUnaryCall <TResponse> > .Handle <Exception>() .Retry(RetryCount); var pollyBulk = Policy.Bulkhead <AsyncUnaryCall <TResponse> >(MaxBulkhead); var pollyFallback = Policy <AsyncUnaryCall <TResponse> > .Handle <Exception>() .Fallback(item => { return(!string.IsNullOrEmpty(FallbackMethod) ? UseNewMethod <TResponse>(new object[] { request }) : null); }); var timeoutPolly = Policy.Timeout <AsyncUnaryCall <TResponse> >(30); var policy = Policy.Wrap(pollyFallback, pollyRetry, timeoutPolly, pollyBulk); var response = policy.Execute(() => { var responseCon = continuation(request, context); var call = new AsyncUnaryCall <TResponse>(responseCon.ResponseAsync, responseCon.ResponseHeadersAsync, responseCon.GetStatus, responseCon.GetTrailers, responseCon.Dispose); return(call); }); return(response); }
public void Bulkhead_Queing_and_Parallel_Execution() { var maximumNumberOfActionsInParallel = 0; var minumumNumberOfSlotsAvailableInQueue = 10; var parallelActionCount = 0; var policy = Policy.Bulkhead(4, 8); Parallel.ForEach(Enumerable.Range(0, 9), p => { policy.Execute(() => { parallelActionCount++; if (policy.QueueAvailableCount < minumumNumberOfSlotsAvailableInQueue) { minumumNumberOfSlotsAvailableInQueue = policy.QueueAvailableCount; } if (parallelActionCount > maximumNumberOfActionsInParallel) { maximumNumberOfActionsInParallel = parallelActionCount; } Thread.Sleep(100); parallelActionCount--; } ); }); Assert.That(FILL.__IN, Is_.Equal_To(maximumNumberOfActionsInParallel + minumumNumberOfSlotsAvailableInQueue)); }
public void Bulkhead_when_Queue_is_Full() { var exceptionCount = 0; var policy = Policy.Bulkhead(4, 2); try { Parallel.ForEach(Enumerable.Range(0, 9), p => { policy.Execute(() => { Thread.Sleep(100); } ); }); } catch (AggregateException ae) { foreach (var ex in ae.InnerExceptions) { if (ex is BulkheadRejectedException) { exceptionCount++; } } } Assert.That(FILL._IN, Is_.Equal_To(exceptionCount)); }
public void Should_throw_when_maxQueuingActions_less_than_zero() { Action policy = () => Policy .Bulkhead <int>(1, -1); policy.ShouldThrow <ArgumentOutOfRangeException>().And .ParamName.Should().Be("maxQueuingActions"); }
public void Should_throw_when_onBulkheadRejected_is_null() { Action policy = () => Policy .Bulkhead <int>(1, 0, null); policy.ShouldThrow <ArgumentNullException>().And .ParamName.Should().Be("onBulkheadRejected"); }
public void Should_throw_when_maxparallelization_less_or_equal_to_zero() { Action policy = () => Policy .Bulkhead <int>(0, 1); policy.ShouldThrow <ArgumentOutOfRangeException>().And .ParamName.Should().Be("maxParallelization"); }
public static Policy CreatePolly() { //超时 var timeout = Policy.Timeout(5);//Handle<TimeoutRejectedException>(); //var retry = Policy.Handle<Exception>().WaitAndRetry(5, (i,e) => //{ // e //}); //重试 var retry = Policy.Handle <Exception>().Retry(5, (e, i) => { Console.WriteLine(e); }); //等待重试 var wait = Policy.Handle <Exception>().WaitAndRetry(5, //重试次数 i => TimeSpan.FromSeconds(3 * i), //间隔3秒*次数 (ex, time) => { Console.WriteLine(ex); }); //降级 var fallback = Policy.Handle <Exception>().Fallback(() => { Console.WriteLine("降级"); }, ex => { Console.WriteLine(ex.Message); }); //5次错误熔断,10秒重试 var _break = Policy.Handle <Exception>().CircuitBreaker(5, TimeSpan.FromSeconds(10), (e, t) =>//OPEN打开熔断 { Console.WriteLine("开始熔断"); }, () => //COLSE 关闭熔断 { Console.WriteLine("关闭熔断"); }, () => //HALF-OPEN 重试半开 { Console.WriteLine("重试半开"); }); //高级熔断器 var breaks = Policy.Handle <Exception>() .AdvancedCircuitBreaker( 0.5, //故障阈值50% TimeSpan.FromSeconds(10), //故障采样时间 6, //最小吞吐量【10秒最少执行6次】 TimeSpan.FromSeconds(15)); //熔断时间 //舱壁隔离【限流】 控制并发 var bulk = Policy.Bulkhead( 10, //最大并发通过量 20); //最大排队数量 return(Policy.Wrap(fallback, _break, retry, timeout)); }
public static string[] TestBulkhead(int maxParallel, int queued) { var policy = Policy.Bulkhead(maxParallel, queued, (context) => { Debug.Write("Slooooooow doooooooown"); }); return(policy.Execute(() => { return new string[] { "value1", "value2" }; })); }
public static void Main() { int maxRetryCount = 6; double circuitBreakDurationSeconds = 0.2 /* experiment with effect of shorter or longer here, eg: change to = 1, and the fallbackForCircuitBreaker is correctly invoked */; int maxExceptionsBeforeBreaking = 4; /* experiment with effect of fewer here, eg change to = 1, and the fallbackForCircuitBreaker is correctly invoked */ int maxParallelizations = 2; int maxQueuingActions = 2; var retryPolicy = Policy.Handle <Exception>(e => (e is HttpRequestException || (/*!(e is BrokenCircuitException) &&*/ e.InnerException is HttpRequestException))) // experiment with introducing the extra (!(e is BrokenCircuitException) && ) clause here, if necessary/desired, depending on goal .WaitAndRetry( retryCount: maxRetryCount, sleepDurationProvider: attempt => TimeSpan.FromMilliseconds(50 * Math.Pow(2, attempt)), onRetry: (ex, calculatedWaitDuration, retryCount, context) => { Console.WriteLine(String.Format("Retry => Count: {0}, Wait duration: {1}, Policy Wrap: {2}, Policy: {3}, Endpoint: {4}, Exception: {5}", retryCount, calculatedWaitDuration, context.PolicyWrapKey, context.PolicyKey, context.OperationKey, ex.Message)); }); var circuitBreaker = Policy.Handle <Exception>(e => (e is HttpRequestException || e.InnerException is HttpRequestException)) .CircuitBreaker(maxExceptionsBeforeBreaking, TimeSpan.FromSeconds(circuitBreakDurationSeconds), onBreak: (ex, breakDuration) => { Console.WriteLine(String.Format("Circuit breaking for {0} ms due to {1}", breakDuration.TotalMilliseconds, ex.Message)); }, onReset: () => { Console.WriteLine("Circuit closed again."); }, onHalfOpen: () => { Console.WriteLine("Half open."); }); var sharedBulkhead = Policy.Bulkhead(maxParallelizations, maxQueuingActions); var fallbackForCircuitBreaker = Policy <bool> .Handle <BrokenCircuitException>() /* .OrInner<BrokenCircuitException>() */ // Consider this if necessary. /* .Or<Exception>(e => circuitBreaker.State != CircuitState.Closed) */ // This check will also detect the circuit in anything but healthy state, regardless of the final exception thrown. .Fallback( fallbackValue: false, onFallback: (b, context) => { Console.WriteLine(String.Format("Operation attempted on broken circuit => Policy Wrap: {0}, Policy: {1}, Endpoint: {2}", context.PolicyWrapKey, context.PolicyKey, context.OperationKey)); } ); var fallbackForAnyException = Policy <bool> .Handle <Exception>() .Fallback <bool>( fallbackAction: (context) => { return(false); }, onFallback: (e, context) => { Console.WriteLine(String.Format("An unexpected error occured => Policy Wrap: {0}, Policy: {1}, Endpoint: {2}, Exception: {3}", context.PolicyWrapKey, context.PolicyKey, context.OperationKey, e.Exception.Message)); } ); var resilienceStrategy = Policy.Wrap(retryPolicy, circuitBreaker, sharedBulkhead); var policyWrap = fallbackForAnyException.Wrap(fallbackForCircuitBreaker.Wrap(resilienceStrategy)); bool outcome = policyWrap.Execute((context) => CallApi("http://www.doesnotexistattimeofwriting.com/"), new Context("some endpoint info")); }
//Polly 可以实现重试、断路、超时、隔离、回退和缓存策略 public static void TestSomething() { //断路(Circuit-breaker) //这句代码设定的策略是,当系统出现两次某个异常时,就停下来,等待 1 分钟后再继续。这是基本的用法,你还可以在断路时定义中断的回调和重启的回调。 Policy.Handle <SomeException>().CircuitBreaker(2, TimeSpan.FromMinutes(1)); //超时(Timeout) //当系统超过一定时间的等待,我们就几乎可以判断不可能会有成功的结果。比如平时一个网络请求瞬间就完成了,如果有一次网络请求超过了 30 秒还没完成,我们就知道这次大概率是不会返回成功的结果了。因此,我们需要设置系统的超时时间,避免系统长时间做无谓的等待。 //这里设置了超时时间不能超过 30 秒,否则就认为是错误的结果,并执行回调。 Policy.Timeout(30, onTimeout: (context, timespan, task) => { // do something }); //隔离(Bulkhead Isolation) //当系统的一处出现故障时,可能促发多个失败的调用,很容易耗尽主机的资源(如 CPU)。 //下游系统出现故障可能导致上游的故障的调用,甚至可能蔓延到导致系统崩溃。所以要将可控的操作限制在一个固定大小的资源池中,以隔离有潜在可能相互影响的操作。 //下面是隔离策略的一个基本用法: //这个策略是最多允许 12 个线程并发执行,如果执行被拒绝,则执行回调。 Policy.Bulkhead(12, context => { // do something }); //回退(Fallback) //有些错误无法避免,就要有备用的方案。这个就像浏览器不支持一些新的 CSS 特性就要额外引用一个 polyfill 一样。 //一般情况,当无法避免的错误发生时,我们要有一个合理的返回来代替失败。 //比如很常见的一个场景是,当用户没有上传头像时,我们就给他一个默认头像,这种策略可以这样定义: //Policy.Handle<SomeException>().Fallback<UserAvatar>(() => UserAvatar.GetRandomAvatar()); Policy.Handle <SomeException>().Fallback(() => { Console.WriteLine("Fallback"); }); //缓存(Cache) //一般我们会把频繁使用且不会怎么变化的资源缓存起来,以提高系统的响应速度。 //如果不对缓存资源的调用进行封装,那么我们调用的时候就要先判断缓存中有没有这个资源,有的话就从缓存返回,否则就从资源存储的地方(比如数据库)获取后缓存起来, //再返回,而且有时还要考虑缓存过期和如何更新缓存的问题。Polly 提供了缓存策略的支持,使得问题变得简单。 //这是官方的一个使用示例用法,它定义了缓存 5 分钟过期的策略,然后把这个策略应用在指定的 Key(即 FooKey)上。 //这一块内容值得用一整篇的内容来讲,下次有机会再详细讲讲 Polly 的缓存策略。 var memoryCacheProvider = new MemoryCacheProvider(); var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMinutes(5)); var result = cachePolicy.Execute(context => getFoo(), new Context("FooKey")); //策略包(Policy Wrap) //一种操作会有多种不同的故障,而不同的故障处理需要不同的策略。这些不同的策略必须包在一起,作为一个策略包,才能应用在同一种操作上。这就是文章开头说的 Polly 的弹性,即各种不同的策略能够灵活地组合起来。 //策略包的基本用法是这样的: //先是把预先定义好的多种不同的策略包在一起,作为一个整体策略,然后应用在同一个操作上。 //var policyWrap = Policy.Wrap(fallback, cache, retry, breaker, timeout, bulkhead); //policyWrap.Execute(); }
public static void Run() { //声明最大并发执行线程数、线程队列数 var policy = Policy.Bulkhead(5, 10); Parallel.For(0, 100, (i) => { var result = policy.Execute <string>(() => { return(GetHtml("http://baidu.com")); }); Console.WriteLine($"成功获取到数据:{result}"); }); Console.ReadKey(); }
public static void Bulkhead() { try { var policy = Policy.Bulkhead(5, 2, context => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId + "---" + context.CorrelationId); }); var arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Parallel.ForEach(arr, (p) => { try { //开始执行 policy.Execute(() => { Console.WriteLine($"{DateTime .Now} 元素:{p} 线程ID:{Thread.CurrentThread.ManagedThreadId}"); }); } catch (Exception exc) { Console.WriteLine("内部:" + exc.Message); } }); //for (int i = 0; i < 10; i++) //{ // new Thread(() => // { // try // { // //开始执行 // policy.Execute(() => // { // Console.WriteLine("开始" + Thread.CurrentThread.ManagedThreadId + "---" + DateTime.Now); // }); // } // catch(Exception exc) // { // Console.WriteLine("内部:"+exc.Message); // } // }).Start(); //} } catch (Exception exc) { Console.WriteLine($"{exc.GetType().FullName} Excepiton:{exc.Message}"); } Console.ReadLine(); }
/// <inheritdoc /> public override void Start() { ThrowIfDisposed(); Guard.IsValid(() => Configuration.MaximumThreads, Configuration.MaximumThreads, i => i > 0, "The Configuration.MaximumThreads must be greater than 0"); if (_smartThreadPool != null) { throw new DotNetWorkQueueException("Start must only be called 1 time"); } _smartThreadPool = Policy.Bulkhead(_configuration.MaximumThreads, _configuration.MaxQueueSize, OnBulkheadRejected); Configuration.SetReadOnly(); }
public void Run() { var policy = Policy.Bulkhead(2, context => { Console.WriteLine("It's Bulkhead, throw BulkheadRejectedException."); }); try { Console.WriteLine(DateTime.Now.ToString() + "-Run BulkheadDemo..."); policy.Execute(Invoke); } catch (Exception ex) { Console.WriteLine(DateTime.Now.ToString() + "-Run BulkheadDemo" + ": Exception->" + ex.Message); } }
public DbRetryPolicy() { var jitterer = new Random(); var retryPolicyWithJitterAsync = Policy.Handle <SqlException>(exception => _sqlExceptions.Contains(exception.Number)). WaitAndRetryAsync( retryCount: RETRY_ATTEMPTS_ON_TRANSIENT_ERROR, sleepDurationProvider: retryNumber => TimeSpan.FromSeconds(Math.Pow(2, retryNumber)) + TimeSpan.FromMilliseconds(jitterer.Next(0, 100)) ); var bulkheadPolicyAsync = Policy.BulkheadAsync(maxParallelization: 3, maxQueuingActions: 25); var circuitBreakerPolicyAsync = Policy .Handle <SqlException>(exception => _sqlExceptions.Contains(exception.Number)). AdvancedCircuitBreakerAsync( failureThreshold: 0.25, // If 25% or more of requests fail samplingDuration: TimeSpan.FromSeconds(60), // in a 60 second period minimumThroughput: 7, // and there have been at least 7 requests in that time durationOfBreak: TimeSpan.FromSeconds(30) // then open the circuit for 30 seconds ); RetryPolicyAsync = Policy.WrapAsync(retryPolicyWithJitterAsync, bulkheadPolicyAsync, circuitBreakerPolicyAsync); var bulkheadPolicy = Policy.Bulkhead(maxParallelization: 3, maxQueuingActions: 25); var retryPolicyWithJitter = Policy.Handle <SqlException>(exception => _sqlExceptions.Contains(exception.Number)). WaitAndRetry( retryCount: RETRY_ATTEMPTS_ON_TRANSIENT_ERROR, sleepDurationProvider: retryNumber => TimeSpan.FromSeconds(Math.Pow(2, retryNumber)) + TimeSpan.FromMilliseconds(jitterer.Next(0, 100)) ); var circuitBreakerPolicy = Policy .Handle <SqlException>(exception => _sqlExceptions.Contains(exception.Number)). AdvancedCircuitBreaker( failureThreshold: 0.25, // If 25% or more of requests fail samplingDuration: TimeSpan.FromSeconds(60), // in a 60 second period minimumThroughput: 7, // and there have been at least 7 requests in that time durationOfBreak: TimeSpan.FromSeconds(30) // then open the circuit for 30 seconds ); RetryPolicy = Policy.Wrap(retryPolicyWithJitter, bulkheadPolicy, circuitBreakerPolicy); }
public static void Test() { var policy = Policy.Bulkhead(5, (context) => { Console.WriteLine("请求量太大"); }); for (int i = 0; i < 1000; i++) { Task.Run(() => { policy.Execute(() => { Thread.Sleep(TimeSpan.FromSeconds(1)); Console.WriteLine("方法执行完成"); }); }); } Console.WriteLine("over"); }
private async Task <UserResponseModel> GetUserListAsync(PriorityType priorityType, int results, int page) { UserResponseModel userResponseList = null; var apiRequestelector = new ApiRequestSelector <IRandomUserEndpoint>(_userEnpoint, priorityType); //Policy.Timeout(20); Policy.Bulkhead(10, 10); userResponseList = await Policy .Handle <ApiException>(exception => exception.StatusCode == HttpStatusCode.ServiceUnavailable) //.CircuitBreakerAsync(2,TimeSpan.FromSeconds(5)) //.AdvancedCircuitBreaker(failureThreshold: 0.5, // samplingDuration: TimeSpan.FromSeconds(10), // minimumThroughput: 1, // durationOfBreak: TimeSpan.FromSeconds(30)) //.FallbackAsync((t) => Task.FromResult(new UserResponseModel())) .WaitAndRetryAsync(5, sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))) .ExecuteAsync(async() => await apiRequestelector.GetApiRequestByPriority()?.GetUserList(results, page)); return(userResponseList); }
/// <summary> /// Adds a new work group. /// </summary> /// <param name="name">The name.</param> /// <param name="concurrencyLevel">The concurrency level.</param> /// <param name="maxQueueSize">Maximum size of the queue. Work groups have a queue that is separate per queue, and is not shared with non work group items</param> /// <returns></returns> /// <exception cref="DotNetWorkQueueException">Start must be called on the scheduler before adding work groups</exception> public override IWorkGroup AddWorkGroup(string name, int concurrencyLevel, int maxQueueSize) { ThrowIfDisposed(); if (_smartThreadPool == null) { throw new DotNetWorkQueueException("Start must be called on the scheduler before adding work groups"); } var group = new WorkGroup(name, concurrencyLevel, maxQueueSize); if (_groups.ContainsKey(group)) { return(_groups[group].GroupInfo); } var groupWithItem = new WorkGroupWithItem(group, Policy.Bulkhead(concurrencyLevel, maxQueueSize, OnBulkheadRejected), _metrics.Counter( $"work group {name}", Units.Items)); _groups.TryAdd(group, groupWithItem); return(groupWithItem.GroupInfo); }
public void Should_control_executions_queuing_and_rejections_per_specification_with_cancellations( int maxParallelization, int maxQueuingActions, int totalActions, bool cancelQueuing, bool cancelExecuting, string scenario) { if (totalActions < 0) { throw new ArgumentOutOfRangeException(nameof(totalActions)); } scenario = String.Format("MaxParallelization {0}; MaxQueuing {1}; TotalActions {2}; CancelQueuing {3}; CancelExecuting {4}: {5}", maxParallelization, maxQueuingActions, totalActions, cancelQueuing, cancelExecuting, scenario); BulkheadPolicy <ResultPrimitive> bulkhead = Policy.Bulkhead <ResultPrimitive>(maxParallelization, maxQueuingActions); // Set up delegates which we can track whether they've started; and control when we allow them to complete (to release their semaphore slot). actions = new TraceableAction[totalActions]; for (int i = 0; i < totalActions; i++) { actions[i] = new TraceableAction(i, statusChanged, testOutputHelper); } // Throw all the delegates at the bulkhead simultaneously. Task[] tasks = new Task[totalActions]; for (int i = 0; i < totalActions; i++) { tasks[i] = actions[i].ExecuteOnBulkhead(bulkhead); } testOutputHelper.WriteLine("Immediately after queueing..."); testOutputHelper.WriteLine("Bulkhead: {0} slots out of {1} available.", bulkhead.BulkheadAvailableCount, maxParallelization); testOutputHelper.WriteLine("Bulkhead queue: {0} slots out of {1} available.", bulkhead.QueueAvailableCount, maxQueuingActions); OutputActionStatuses(); // Assert the expected distributions of executing, queuing, rejected and completed - when all delegates thrown at bulkhead. int expectedCompleted = 0; int expectedCancelled = 0; int expectedExecuting = Math.Min(totalActions, maxParallelization); int expectedRejects = Math.Max(0, totalActions - maxParallelization - maxQueuingActions); int expectedQueuing = Math.Min(maxQueuingActions, Math.Max(0, totalActions - maxParallelization)); int expectedBulkheadFree = maxParallelization - expectedExecuting; int expectedQueueFree = maxQueuingActions - expectedQueuing; try { actions.Count(a => a.Status == TraceableActionStatus.Faulted).Should().Be(0); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.Executing).Should().Be(expectedExecuting, scenario + ", when checking expectedExecuting")); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.QueueingForSemaphore).Should().Be(expectedQueuing, scenario + ", when checking expectedQueuing")); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.Rejected).Should().Be(expectedRejects, scenario + ", when checking expectedRejects")); actions.Count(a => a.Status == TraceableActionStatus.Completed).Should().Be(expectedCompleted, scenario + ", when checking expectedCompleted"); actions.Count(a => a.Status == TraceableActionStatus.Canceled).Should().Be(expectedCancelled, scenario + ", when checking expectedCancelled"); Within(shimTimeSpan, () => bulkhead.BulkheadAvailableCount.Should().Be(expectedBulkheadFree, scenario + ", when checking expectedBulkheadFree")); Within(shimTimeSpan, () => bulkhead.QueueAvailableCount.Should().Be(expectedQueueFree, scenario + ", when checking expectedQueueFree")); } finally { testOutputHelper.WriteLine("Expected initial state verified..."); testOutputHelper.WriteLine("Bulkhead: {0} slots out of {1} available.", bulkhead.BulkheadAvailableCount, maxParallelization); testOutputHelper.WriteLine("Bulkhead queue: {0} slots out of {1} available.", bulkhead.QueueAvailableCount, maxQueuingActions); OutputActionStatuses(); } // Complete or cancel delegates one by one, and expect others to take their place (if a slot released and others remain queueing); until all work is done. while (expectedExecuting > 0) { if (cancelQueuing) { testOutputHelper.WriteLine("Cancelling a queueing task..."); actions.First(a => a.Status == TraceableActionStatus.QueueingForSemaphore).Cancel(); expectedCancelled++; expectedQueuing--; expectedQueueFree++; cancelQueuing = false; } else if (cancelExecuting) { testOutputHelper.WriteLine("Cancelling an executing task..."); actions.First(a => a.Status == TraceableActionStatus.Executing).Cancel(); expectedCancelled++; if (expectedQueuing > 0) { expectedQueuing--; expectedQueueFree++; } else { expectedExecuting--; expectedBulkheadFree++; } cancelExecuting = false; } else // Complete an executing delegate. { testOutputHelper.WriteLine("Completing a task..."); actions.First(a => a.Status == TraceableActionStatus.Executing).AllowCompletion(); expectedCompleted++; if (expectedQueuing > 0) { expectedQueuing--; expectedQueueFree++; } else { expectedExecuting--; expectedBulkheadFree++; } } try { actions.Count(a => a.Status == TraceableActionStatus.Faulted).Should().Be(0); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.Executing).Should().Be(expectedExecuting, scenario + ", when checking expectedExecuting")); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.QueueingForSemaphore).Should().Be(expectedQueuing, scenario + ", when checking expectedQueuing")); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.Completed).Should().Be(expectedCompleted, scenario + ", when checking expectedCompleted")); Within(shimTimeSpan, () => actions.Count(a => a.Status == TraceableActionStatus.Canceled).Should().Be(expectedCancelled, scenario + ", when checking expectedCancelled")); actions.Count(a => a.Status == TraceableActionStatus.Rejected).Should().Be(expectedRejects, scenario + ", when checking expectedRejects"); Within(shimTimeSpan, () => bulkhead.BulkheadAvailableCount.Should().Be(expectedBulkheadFree, scenario + ", when checking expectedBulkheadFree")); Within(shimTimeSpan, () => bulkhead.QueueAvailableCount.Should().Be(expectedQueueFree, scenario + ", when checking expectedQueueFree")); } finally { testOutputHelper.WriteLine("End of next loop iteration..."); testOutputHelper.WriteLine("Bulkhead: {0} slots out of {1} available.", bulkhead.BulkheadAvailableCount, maxParallelization); testOutputHelper.WriteLine("Bulkhead queue: {0} slots out of {1} available.", bulkhead.QueueAvailableCount, maxQueuingActions); OutputActionStatuses(); } } EnsureNoUnbservedTaskExceptions(tasks); testOutputHelper.WriteLine("Verifying all tasks completed..."); Within(shimTimeSpan, () => tasks.All(t => t.IsCompleted).Should().BeTrue()); #endregion }
/// <summary> /// Polly方法使用代码Demo /// </summary> /// <returns></returns> static async Task PollyCodeDemoAsync() { Policy.Handle <Exception>().WaitAndRetry( 3, retryAttempt => TimeSpan.FromSeconds(5), // 处理异常、 (exception, timespan, retryCount, context) => { // doSomething Console.WriteLine($"{DateTime.Now} - 重试 {retryCount} 次 - 抛出{exception.GetType()}-{timespan.TotalMilliseconds}"); }) .Execute(() => { GetServiceResult(); }); Policy.Handle <Exception>() .CircuitBreaker( // 熔断前允许出现几次错误 3, // 熔断时间 TimeSpan.FromSeconds(5), // 熔断时触发 onBreak: (ex, breakDelay) => { Console.WriteLine("断路器:开启状态(熔断时触发)"); }, // 熔断恢复时触发 onReset: () => { Console.WriteLine("断路器:关闭状态(熔断恢复时触发)"); }, // 熔断时间到了之后触发,尝试放行少量(1次)的请求, onHalfOpen: () => { Console.WriteLine("断路器:半开启状态(熔断时间到了之后触发)"); } ); Policy.Handle <Exception>().CircuitBreaker(5, TimeSpan.FromSeconds(10)) .Execute(() => { // do something }); // 单个异常类型 Policy.Handle <Exception>(); // 限定条件的单个异常 Policy.Handle <ArgumentException>(ex => ex.ParamName == "ID"); // 多个异常类型 Policy.Handle <Exception>().Or <ArgumentException>(); // 限定条件的多个异常 Policy.Handle <Exception>(ex => ex.Message == "请求超时") .Or <ArgumentException>(ex => ex.ParamName == "ID"); // Inner Exception 异常里面的异常类型 Policy.HandleInner <Exception>() .OrInner <ArgumentException>(ex => ex.ParamName == "ID"); // 返回结果加限定条件 Policy.HandleResult <HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.NotFound); // 处理多个返回结果 Policy.HandleResult <HttpResponseMessage>(r => r.StatusCode == HttpStatusCode.InternalServerError) .OrResult(r => r.StatusCode == HttpStatusCode.BadGateway); // 处理元类型结果 (用.Equals) Policy.HandleResult(HttpStatusCode.InternalServerError) .OrResult(HttpStatusCode.BadGateway); // 重试1次 Policy.Handle <Exception>().Retry(); // 重试3次 Policy.Handle <Exception>().Retry(3); // 重试3次,加上重试时的action参数 Policy.Handle <Exception>().Retry(3, (exception, retryCount) => { // do Something }); // 不断重试,直到成功 Policy.Handle <Exception>().RetryForever(); // 不断重试,带action参数在每次重试的时候执行 Policy.Handle <Exception>().RetryForever(exception => { // do Something }); // 重试3次,每次等待5s Policy.Handle <Exception>().WaitAndRetry( 3, retryAttempt => TimeSpan.FromSeconds(5), // 处理异常、等待时间、重试第几次 (exception, timespan, retryCount, context) => { // do Something }); // 重试3次,分别等待1、2、3秒。 Policy.Handle <Exception>().WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) }); Policy.Handle <Exception>().CircuitBreaker(2, TimeSpan.FromSeconds(10)); Policy.Handle <Exception>().Fallback(() => { // do something }); // 设置超时时间为30s Policy.Timeout(30, onTimeout: (context, timespan, task, ex) => { // do something }); // 超时分为乐观超时与悲观超时,乐观超时依赖于CancellationToken ,它假设我们的具体执行的任务都支持CancellationToken。 // 那么在进行timeout的时候,它会通知执行线程取消并终止执行线程,避免额外的开销。下面的乐观超时的具体用法 。 HttpResponseMessage httpResponse = await Policy.TimeoutAsync(30) .ExecuteAsync( async ct => await new HttpClient().GetAsync(""), CancellationToken.None ); // 悲观超时与乐观超时的区别在于,如果执行的代码不支持取消CancellationToken, // 它还会继续执行,这会是一个比较大的开销。 Policy.Timeout(30, TimeoutStrategy.Pessimistic); Policy.Bulkhead(12); // 同时,我们还可以控制一个等待处理的队列长度 Policy.Bulkhead(12, 2); // 以及当请求执行操作被拒绝的时候,执行回调 Policy.Bulkhead(12, context => { // do something }); //var policyWrap = Policy.Wrap(fallback, breaker, timeout, retry, bulkhead); //policyWrap.Execute(() => //{ // // do something //}); }
protected override IBulkheadPolicy GetBulkhead(int maxParallelization, int maxQueuingActions) { return(Policy.Bulkhead <ResultPrimitive>(maxParallelization, maxQueuingActions)); }
public static BulkheadPolicy GenerateTotalNetworkRequestsNonAsyncPolicy(PolicySettings settings) { return(Policy.Bulkhead(settings.MaximumSimultaneousNetworkRequests)); }
private static Policy <ResultPrimitive> BulkheadPolicy() { return(Policy .Bulkhead <ResultPrimitive>(1)); }