Closed JiaLinYou closed 9 months ago
For question 1, your strategies are defined in the wrong order. If you swap retry and timeout, like shown in our Getting Started documentation, you'll get the desired behaviour:
TestTimeoutSync Execution timed out after 1 seconds.
OnRetry, Attempt: 0.
TestTimeoutSync Execution timed out after 1 seconds.
TimeoutRejectedException
TestTimeoutSyncV8 Execution Timeout!
For question 2, the fallback strategy requires the pipeline to be of the type of the fallback result. Your code doesn't use the generic type parameter, so the fallback isn't available. The following code will return a fallback:
var pipeline = new ResiliencePipelineBuilder<string>()
.AddFallback(new FallbackStrategyOptions<string>
{
ShouldHandle = new PredicateBuilder<string>().Handle<Exception>(),
FallbackAction = static args =>
{
Console.WriteLine("FallbackAction");
return Outcome.FromResultAsValueTask("Fallback!");
}
})
.AddRetry(new RetryStrategyOptions<string>
{
ShouldHandle = new PredicateBuilder<string>().Handle<Exception>(),
MaxRetryAttempts = 1,
Delay = TimeSpan.Zero,
OnRetry = static args =>
{
Console.WriteLine($"OnRetry, Attempt: {args.AttemptNumber}.");
return default;
}
})
.AddTimeout(new TimeoutStrategyOptions
{
TimeoutGenerator = static args =>
{
return new ValueTask<TimeSpan>(TimeSpan.FromSeconds(1));
},
OnTimeout = static args =>
{
Console.WriteLine($"TestTimeoutSync Execution timed out after {args.Timeout.TotalSeconds} seconds.");
return default;
}
})
.Build();
var result = pipeline.Execute(static token =>
{
var res = TheadSleep(token);
Console.WriteLine(res);
return res;
}, cancellationToken);
TestTimeoutSync Execution timed out after 1 seconds.
OnRetry, Attempt: 0.
TestTimeoutSync Execution timed out after 1 seconds.
FallbackAction
Fallback!
TestTimeoutSyncV8 Execution Timeout!
^^ What Martin says.
You can also review the following table that shows what strategies are available for each builder type:
Thank you for your answers, so if I want to use the AddCircuitBreaker method, the correct order of addition should be like
new ResiliencePipelineBuilder().AddFallback().AddCircuitBreaker().AddRetry().AddTimeout()
right?
Thank you for your answers, so if I want to use the AddCircuitBreaker method, the correct order of addition should be like
new ResiliencePipelineBuilder().AddFallback().AddCircuitBreaker().AddRetry().AddTimeout()
right?
It's rather:
new ResiliencePipelineBuilder().AddFallback().AddRetry().AddCircuitBreaker().AddTimeout()
You want CB to register each individual retry attempt and each timeout.
Thank you for your correction, it has indeed taken effect.
What confuses me is that the order of adding methods is not AddFallback().AddCircuitBreaker().AddRetry().AddTimeout()
But rather AddFallback().AddRetry().AddCircuitBreaker().AddTimeout()
I mistakenly thought that the addition methods order would be the same as the execution methods order, because I thought it would time out, then retry, then circuit breaker, and finally fallback. This seems to be different from the combination strategy in Polly v7.
Sincere thanks for your efforts, which have been of great help to me.
Sorry for that, but I have a new question again ^^.
When I use the SlidingWindRateLimiter object as a parameter to AddRateLimiter, the fourth request within one minute will be subjected to a flow limiting operation successfully.
But when I use the RateLimiterStrategyOptions object as a parameter to AddRateLimiter, it will not be restricted and the statement in the OnRejected method will not be printed out.
I have reviewed the document but still feel confused. Please forgive me as a beginner.
It's much more useful to us, and others who made read this issue in the future, if you provide your code as text in code fences, not as screenshots. Then we can easily copy-paste your code to use, rather than having to type it all out again by reading your screenshots. It's also accessible to tools like screen readers for users of GitHub who need to use such tools.
Thank you for your reminder. Here is my code.
builder.Services.AddResiliencePipeline<string, string>("Test-TimeoutAsync-V8",
pipelineBuilder =>
{
pipelineBuilder.AddFallback(new FallbackStrategyOptions<string>
{
OnFallback = args =>
{
Console.WriteLine($"This is OnFallback");
return default;
},
FallbackAction = args =>
{
Console.WriteLine("This is FallbackAction");
return Outcome.FromResultAsValueTask<string>("Fallback: TestTimeoutAsyncV8 timeout!");
}
})
.AddRetry(new RetryStrategyOptions<string>
{
ShouldHandle = new PredicateBuilder<string>().Handle<TimeoutRejectedException>(),
MaxRetryAttempts = 2,
Delay = TimeSpan.Zero,
OnRetry = retryArguments =>
{
Console.WriteLine($"OnRetry, Attempt: {retryArguments.AttemptNumber}.");
return ValueTask.CompletedTask;
}
})
.AddCircuitBreaker(new CircuitBreakerStrategyOptions<string>
{
ShouldHandle = new PredicateBuilder<string>().Handle<Exception>(),
MinimumThroughput = 3,
FailureRatio = 0.1,
BreakDuration = TimeSpan.FromSeconds(15),
OnOpened = cb =>
{
Console.WriteLine($"OnOpened 开始熔断");
return ValueTask.CompletedTask;
},
OnClosed = cb =>
{
Console.WriteLine($"OnClosed 停止熔断");
return ValueTask.CompletedTask;
},
OnHalfOpened = cb =>
{
Console.WriteLine($"OnHalfOpened 释放一部分请求去服务端");
return ValueTask.CompletedTask;
}
})
.AddTimeout(new TimeoutStrategyOptions
{
TimeoutGenerator = args =>
{
return new ValueTask<TimeSpan>(TimeSpan.FromSeconds(3));
},
OnTimeout = args =>
{
Console.WriteLine($"Execution timed out after {args.Timeout.TotalSeconds} seconds.");
return ValueTask.CompletedTask;
}
})
.AddConcurrencyLimiter(2, 10)
.AddRateLimiter(new RateLimiterStrategyOptions
{
DefaultRateLimiterOptions = new ConcurrencyLimiterOptions
{
QueueLimit = 10,
PermitLimit = 3
},
OnRejected = rateLimiterArguments =>
{
Console.WriteLine($"This is user-ResiliencePipeline: OnRejected {rateLimiterArguments.Lease}");
return default;
}
});
//.AddRateLimiter(new SlidingWindowRateLimiter(
// new SlidingWindowRateLimiterOptions
// {
// SegmentsPerWindow = 1,
// PermitLimit = 3,
// Window = TimeSpan.FromMinutes(1)
// }));
});
@JiaLinYou
When I use the SlidingWindRateLimiter object as a parameter to AddRateLimiter, the fourth request within one minute will be subjected to a flow limiting operation successfully.
Yup, because you are using AddRateLimiter
and providing sliding limiter.
But when I use the RateLimiterStrategyOptions object as a parameter to AddRateLimiter, it will not be restricted and the statement in the OnRejected method will not be printed out.
This is because the default limiter and respective options creates ConcurrencyLimiter
, where the conditions to reject (rate-limit) the execution are not met. (>13 concurrent requests, 3 active, 10 queued).
Thank you for your reminder. I will modify the code to
DefaultRateLimiterOptions = new ConcurrencyLimiterOptions
{
QueueLimit = 0,
PermitLimit = 1
}
Then I used Postman to initiate two requests simultaneously, and the statement in the OnRejected method was indeed printed out.
May I ask does RateLimiterStrategyOptions have a property similar to the Window specified detection time in SlidingWindRateLimiter?
You can use our API documentation, as well as the source code in this repo, to see what options are available on various types.
Thank you for your answer,this is very useful for my learning.
I use AddRateLimiter twice at the same time, so that I can limit the request when I receive four requests within a minute, and also can limit the request when I receive two requests at the same time.
.AddRateLimiter(new RateLimiterStrategyOptions
{
DefaultRateLimiterOptions = new ConcurrencyLimiterOptions
{
QueueLimit = 0,
PermitLimit = 1
},
OnRejected = rateLimiterArguments =>
{
Console.WriteLine($"This is user-ResiliencePipeline: OnRejected {rateLimiterArguments.Lease}");
return default;
}
})
.AddRateLimiter(new SlidingWindowRateLimiter(
new SlidingWindowRateLimiterOptions
{
SegmentsPerWindow = 1,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1)
}));
I suggest consulting the Rate limiting middleware in ASP.NET Core documentation, specifically the bit about how to chain rate limiters together using the PartitionedRateLimiter.CreateChained<TResource>()
method. You could create a single rate limiter that way, and then use it via the RateLimiterStrategyOptions.RateLimiter
property.
Thank you for your reminder. I am currently trying to implements the above functions through 'RateLimiterStrategityOptions.RateLimiter' attribute.
Thanks for your help, I have successfully solved this problem. These codes can be used as a reference for those people in need
var limter = PartitionedRateLimiter.CreateChained(
PartitionedRateLimiter.Create<ResilienceContext, string>(context =>
{
ResiliencePropertyKey<string> UserProperty = new("UserName");
string partitionKey = context.Properties.GetValue(UserProperty, "Default");
return RateLimitPartition.GetSlidingWindowLimiter(partitionKey, _ =>
{
return new SlidingWindowRateLimiterOptions
{
AutoReplenishment = true,
SegmentsPerWindow = 1,
PermitLimit = 3,
Window = TimeSpan.FromMinutes(1)
};
});
}),
PartitionedRateLimiter.Create<ResilienceContext, string>(context =>
{
ResiliencePropertyKey<string> UserProperty = new("UserName");
string partitionKey = context.Properties.GetValue(UserProperty, "Default");
return RateLimitPartition.GetConcurrencyLimiter(partitionKey, _ =>
{
return new ConcurrencyLimiterOptions
{
QueueLimit = 0,
PermitLimit = 1
};
});
})
);
builder.Services.AddResiliencePipeline<string, string>("Test-TimeoutAsync-V8",
pipelineBuilder =>
{
pipelineBuilder.AddRateLimiter(new RateLimiterStrategyOptions
{
RateLimiter = args =>
{
return limter.AcquireAsync(args.Context,1, args.Context.CancellationToken);
},
OnRejected = rateLimiterArguments =>
{
Console.WriteLine($"This is user-ResiliencePipeline: OnRejected {rateLimiterArguments.Lease}");
return default;
}
});
});
How should I make the AddRetry‘s OnRetry method print out statements ?
How to solve the problem of "ResiliencePipelineBuilder does not include the definition of AddFallback" ?
Polly v8.2.0 Q1: Why AddRetry method not effective when use?
Q2: Why prompt does not include the definition of AddFallback method when use the AddFallback method ?
Thanks in advance for your help!
What code or approach do you have so far?
Additional context
No response