때로는 포기하기 전에 여러 번 작업을 다시 시도해야 할 때가 있습니다. 내 코드는 다음과 같습니다
int retries = 3;
while(true) {
try {
DoSomething();
break; // success!
} catch {
if(--retries == 0) throw;
else Thread.Sleep(1000);
}
}
다음과 같은 일반적인 재시도 기능으로 이것을 다시 작성하고 싶습니다.
TryThreeTimes(DoSomething);
C #에서 가능합니까? 이 TryThreeTimes()
방법 의 코드는 무엇입니까 ?
답변
일반적인 예외 처리 메커니즘으로 사용하는 경우 단순히 동일한 호출을 재 시도하는 담요 catch 문은 위험 할 수 있습니다. 말했듯이 다음은 모든 방법으로 사용할 수있는 람다 기반 재시도 래퍼입니다. 재시도 횟수와 재시도 시간 초과를 매개 변수로 고려하여 유연성을 높였습니다.
public static class Retry
{
public static void Do(
Action action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
Do<object>(() =>
{
action();
return null;
}, retryInterval, maxAttemptCount);
}
public static T Do<T>(
Func<T> action,
TimeSpan retryInterval,
int maxAttemptCount = 3)
{
var exceptions = new List<Exception>();
for (int attempted = 0; attempted < maxAttemptCount; attempted++)
{
try
{
if (attempted > 0)
{
Thread.Sleep(retryInterval);
}
return action();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
throw new AggregateException(exceptions);
}
}
이제이 유틸리티 방법을 사용하여 재시도 로직을 수행 할 수 있습니다.
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));
또는:
Retry.Do(SomeFunctionThatCanFail, TimeSpan.FromSeconds(1));
또는:
int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);
또는 async
과부하 를 일으킬 수도 있습니다.
답변
당신은 폴리 를 시도해야 . 개발자가 재시도, 재시도 재시도, 대기 및 재시도 또는 회로 차단기와 같은 일시적인 예외 처리 정책을 유창하게 표현할 수있는 저에 의해 작성된 .NET 라이브러리입니다.
예
Policy
.Handle<SqlException>(ex => ex.Number == 1205)
.Or<ArgumentException>(ex => ex.ParamName == "example")
.WaitAndRetry(3, retryAttempt => TimeSpan.FromSeconds(3))
.Execute(() => DoSomething());
답변
이것은 아마도 나쁜 생각입니다. 첫째, 그것은 “광기의 정의는 같은 일을 두 번하고 매번 다른 결과를 기대하고있다”는 최대의 상징이다. 둘째,이 코딩 패턴은 그 자체로 잘 구성되지 않습니다. 예를 들면 다음과 같습니다.
네트워크 하드웨어 계층이 실패 할 때 패킷을 세 번 다시 전송한다고 가정합시다 (예 : 실패 사이에 1 초).
이제 소프트웨어 계층이 패킷 실패시 3 회 실패에 대한 알림을 다시 보낸다고 가정합니다.
이제 알림 계층이 알림 배달 실패시 알림을 세 번 다시 활성화한다고 가정합니다.
이제 오류보고 계층이 알림 실패시 알림 계층을 세 번 다시 활성화한다고 가정합니다.
이제 웹 서버가 오류 실패시 오류보고를 세 번 다시 활성화한다고 가정합니다.
이제 웹 클라이언트가 서버에서 오류가 발생하면 요청을 세 번 다시 보낸다고 가정합니다.
이제 알림을 관리자에게 라우팅해야하는 네트워크 스위치의 회선이 분리되었다고 가정합니다. 웹 클라이언트 사용자는 언제 마지막으로 오류 메시지를 받습니까? 나는 약 12 분 후에 그것을 만든다.
이것이 단지 어리석은 예라고 생각하지 않도록하십시오 : 우리는 여기에 설명 된 것보다 훨씬 나쁘지만 고객 코드 에서이 버그를 보았습니다. 특정 고객 코드에서 발생하는 오류 상태와 최종 사용자에게보고되는 간격은 몇 주가 지났으므로 많은 계층이 대기 상태에서 자동으로 다시 시도하기 때문입니다. 3 번이 아닌 10 번의 재 시도 가 있다면 어떻게 될지 상상해보십시오 .
일반적으로 오류 조건과 관련된 올바른 작업은 즉시보고하여 사용자가 수행 할 작업을 결정하도록하는 것입니다. 사용자가 자동 재시도 정책을 작성하려면 소프트웨어 추상화에서 적절한 레벨로 해당 정책을 작성하십시오.
답변
public void TryThreeTimes(Action action)
{
var tries = 3;
while (true) {
try {
action();
break; // success!
} catch {
if (--tries == 0)
throw;
Thread.Sleep(1000);
}
}
}
그런 다음 전화하십시오.
TryThreeTimes(DoSomething);
… 또는 대안으로 …
TryThreeTimes(() => DoSomethingElse(withLocalVariable));
보다 유연한 옵션 :
public void DoWithRetry(Action action, TimeSpan sleepPeriod, int tryCount = 3)
{
if (tryCount <= 0)
throw new ArgumentOutOfRangeException(nameof(tryCount));
while (true) {
try {
action();
break; // success!
} catch {
if (--tryCount == 0)
throw;
Thread.Sleep(sleepPeriod);
}
}
}
다음과 같이 사용하십시오.
DoWithRetry(DoSomething, TimeSpan.FromSeconds(2), tryCount: 10);
async / await를 지원하는 최신 버전 :
public async Task DoWithRetryAsync(Func<Task> action, TimeSpan sleepPeriod, int tryCount = 3)
{
if (tryCount <= 0)
throw new ArgumentOutOfRangeException(nameof(tryCount));
while (true) {
try {
await action();
return; // success!
} catch {
if (--tryCount == 0)
throw;
await Task.Delay(sleepPeriod);
}
}
}
다음과 같이 사용하십시오.
await DoWithRetryAsync(DoSomethingAsync, TimeSpan.FromSeconds(2), tryCount: 10);
답변
과도 장애 관리 응용 프로그램 블록 을 포함하여 재시도 전략의 확장 모음을 제공합니다 :
- 증분
- 고정 간격
- 지수 백 오프
또한 클라우드 기반 서비스에 대한 오류 감지 전략 모음이 포함되어 있습니다.
자세한 내용 은 개발자 안내서 의이 장 을 참조하십시오 .
NuGet을 통해 사용 가능합니다 ( ‘ topaz ‘ 검색 ).
답변
기능 허용 및 메시지 재시도
public static T RetryMethod<T>(Func<T> method, int numRetries, int retryTimeout, Action onFailureAction)
{
Guard.IsNotNull(method, "method");
T retval = default(T);
do
{
try
{
retval = method();
return retval;
}
catch
{
onFailureAction();
if (numRetries <= 0) throw; // improved to avoid silent failure
Thread.Sleep(retryTimeout);
}
} while (numRetries-- > 0);
return retval;
}
답변
재 시도하려는 예외 유형 추가를 고려할 수도 있습니다. 예를 들어 다시 시도하려는 시간 초과 예외입니까? 데이터베이스 예외?
RetryForExcpetionType(DoSomething, typeof(TimeoutException), 5, 1000);
public static void RetryForExcpetionType(Action action, Type retryOnExceptionType, int numRetries, int retryTimeout)
{
if (action == null)
throw new ArgumentNullException("action");
if (retryOnExceptionType == null)
throw new ArgumentNullException("retryOnExceptionType");
while (true)
{
try
{
action();
return;
}
catch(Exception e)
{
if (--numRetries <= 0 || !retryOnExceptionType.IsAssignableFrom(e.GetType()))
throw;
if (retryTimeout > 0)
System.Threading.Thread.Sleep(retryTimeout);
}
}
}
다른 모든 예제에는 재시도 == 0 테스트와 비슷한 문제가 있으며 무한대 재시도 또는 음수 값이 주어지면 예외가 발생하지 않습니다. 또한 위의 catch 블록에서 Sleep (-1000)이 실패합니다. 사람들이 얼마나 ‘어리석은’것으로 예상 하느냐에 따라 방어 적 프로그래밍은 결코 아프지 않습니다.