다시 시도 폭풍 안티패턴

서비스를 사용할 수 없거나 사용 중인 경우 클라이언트에서 연결을 너무 자주 다시 시도하면 서비스가 복구되지 않을 수 있으며 문제가 악화될 수 있습니다. 또한 요청이 일반적으로 정의된 기간 동안만 유효하므로 계속하여 반복적으로 다시 시도하는 것은 바람직하지 않습니다.

문제 설명

클라우드에서 서비스에 문제가 발생하는 경우도 있습니다. 이 경우 클라이언트에서 해당 서비스를 사용할 수 없게 되거나, 클라이언트를 제한하거나, 속도를 제한해야 합니다. 클라이언트에서 서비스에 대한 실패한 연결을 다시 시도하는 것이 좋지만 너무 자주 또는 너무 오래 다시 시도하지 않아야 합니다. 짧은 시간 내에 다시 시도하는 경우 서비스가 복구되지 않을 수 있으므로 성공할 가능성이 낮습니다. 또한 복구를 시도하는 동안 연결을 많이 시도하는 경우에도 더 많은 스트레스를 받을 수 있으며, 반복되는 연결 시도로 인해 서비스가 과부하 상태에 놓여 기본적인 문제가 발생할 수 있습니다.

다음 예제에서는 클라이언트에서 서버 기반 API에 연결하는 시나리오를 보여 줍니다. 요청이 성공하지 못하면 클라이언트에서 즉시 다시 시도하며 계속하여 반복적으로 다시 시도합니다. 이러한 종류의 동작은 이 예제에 비해 다소 복잡한 경우가 많지만, 동일한 원칙이 적용됩니다.

public async Task<string> GetDataFromServer()
{
    while(true)
    {
        var result = await httpClient.GetAsync(string.Format("http://{0}:8080/api/...", hostName));
        if (result.IsSuccessStatusCode) break;
    }

    // ... Process result.
}

문제를 해결하는 방법

다시 시도 폭풍이 발생하지 않도록 클라이언트 애플리케이션에서 몇 가지 모범 사례를 따라야 합니다.

  • 다시 시도 횟수를 제한하고, 다시 시도를 오랫동안 계속하지 않습니다. while(true) 루프를 작성하는 것이 쉽지 않을 수 있지만, 요청이 시작되는 상황이 변경되었을 수 있으므로 실제로 오랫동안 다시 시도할 필요성이 거의 없습니다. 대부분의 애플리케이션에서는 몇 초 또는 몇 분 동안 다시 시도해도 충분합니다.
  • 다시 시도 간에 일시 중지합니다. 서비스를 사용할 수 없는 경우 즉시 다시 시도가 성공할 가능성이 낮습니다. 예를 들어 지수 백오프 전략을 사용하여 시도 간에 대기하는 시간을 점차적으로 늘립니다.
  • 오류를 정상적으로 처리합니다. 서비스에서 응답하지 않는 경우 시도를 중단하고 구성 요소의 사용자 또는 호출자에게 오류를 반환하는 것이 적절한지 여부를 고려합니다. 이러한 오류 시나리오는 애플리케이션을 설계할 때 고려해야 합니다.
  • 다시 시도 폭풍을 방지하기 위해 특별히 설계된 회로 차단기 패턴을 사용하는 것이 좋습니다.
  • 서버에서 retry-after 응답 헤더를 제공하는 경우 지정된 기간이 경과할 때까지 다시 시도를 수행하지 않아야 합니다.
  • Azure 서비스와 통신하는 경우 공식 SDK를 사용합니다. 이러한 SDK에는 일반적으로 기본 제공 다시 시도 정책 및 다시 시도 폭풍이 발생하지 않거나 이에 기여하지 않도록 방지하는 보호 기능이 있습니다. SDK가 없거나 SDK에서 다시 시도 논리를 올바르게 처리하지 않는 서비스와 통신하는 경우 라이브러리(예: Polly(.NET용) 또는 retry(JavaScript용))를 사용하여 다시 시도 논리를 올바르게 처리하고 코드를 직접 작성하지 않는 것이 좋습니다.
  • 이를 지원하는 환경에서 실행하는 경우 서비스 메시(또는 다른 추상화 계층)를 사용하여 아웃바운드 호출을 보냅니다. 일반적으로 Dapr과 같은 이러한 도구는 재시도 정책을 지원하고 반복 시도 후 백오프와 같은 모범 사례를 자동으로 따릅니다. 이 방법은 사용자가 직접 재시도 코드를 작성할 필요가 없다는 것을 의미합니다.
  • 요청을 일괄 처리하고 사용 가능한 경우 요청 풀링을 사용하는 것이 좋습니다. 대부분의 SDK에서 사용자를 대신하여 요청 일괄 처리 및 연결 풀링을 처리하므로 이러한 아웃바운드 연결을 너무 자주 다시 시도하지 않도록 주의해야 하지만 애플리케이션에서 수행하는 이러한 연결 시도의 총 횟수가 줄어듭니다.

또한 서비스에서 자체적으로 다시 시도 폭풍으로부터 보호해야 합니다.

  • 인시던트 중에 연결을 종료할 수 있도록 게이트웨이 계층을 추가합니다. 이는 격벽 패턴의 예입니다. Azure는 Front Door, Application GatewayAPI Management를 포함하여 다양한 유형의 솔루션에 다양한 게이트웨이 서비스를 제공합니다.
  • 게이트웨이에서 요청을 제한합니다. 그러면 백 엔드 구성 요소에서 계속 작동할 수 없는 너무 많은 요청을 수락하지 않습니다.
  • 제한하는 경우 클라이언트에서 연결을 다시 시도하는 시기를 이해하는 데 도움이 되도록 retry-after 헤더를 다시 보냅니다.

고려 사항

  • 클라이언트에서 반환되는 오류 유형을 고려해야 합니다. 일부 오류 유형은 서비스 오류가 아니라 클라이언트에서 잘못된 요청을 보낸 것으로 표시합니다. 예를 들어 클라이언트 애플리케이션에서 400 Bad Request 오류 응답을 받는 경우 서버에서 요청이 유효하지 않다고 알려주므로 동일한 요청을 다시 시도해도 도움이 되지 않을 것입니다.
  • 클라이언트에서 연결을 다시 시도하는 데 적합한 시간을 고려해야 합니다. 다시 시도해야 하는 시간은 비즈니스 요구 사항과 사용자 또는 호출자에게 오류를 합리적으로 다시 전파할 수 있는지 여부에 따라 결정됩니다. 대부분의 애플리케이션에서는 몇 초 또는 몇 분 동안 다시 시도해도 충분합니다.

문제를 감지하는 방법

클라이언트의 관점에서 이 문제의 증상에는 연결을 반복적으로 다시 시도했다고 나타내는 원격 분석과 함께 매우 긴 응답 또는 처리 시간이 포함될 수 있습니다.

서비스의 관점에서 이 문제의 증상에는 짧은 시간 내에 한 클라이언트의 많은 요청이 포함되거나 가동 중단에서 복구하는 동안 단일 클라이언트의 많은 요청이 포함될 수 있습니다. 또한 증상에는 서비스를 복구할 때의 어려움이 포함되거나 오류가 복구된 직후 서비스의 지속적인 연속 오류가 포함될 수 있습니다.

예제 진단

다음 섹션에서는 클라이언트 쪽 및 서비스 쪽에서 잠재적인 다시 시도 폭풍을 검색하는 한 가지 방법을 보여 줍니다.

클라이언트 원격 분석에서 식별

Azure Application Insights는 애플리케이션에서 원격 분석을 기록하고 데이터를 쿼리 및 시각화에 사용할 수 있도록 합니다. 아웃바운드 연결은 종속성으로 추적되고, 해당 연결에 대한 정보에 액세스하여 클라이언트에서 동일한 서비스에 대한 많은 수의 아웃바운드 요청을 수행하는 경우를 식별할 수 있습니다.

다음 그래프에서는 Application Insights 포털 내의 [메트릭] 탭에서 가져와서 원격 종속성 이름을 기준으로 분할된 종속성 오류 메트릭을 표시합니다. 이는 종속성에 대한 연결 시도가 짧은 시간 내에 21,000회 이상 실패한 시나리오를 보여 줍니다.

30분 내에 단일 종속성에 대한 21,000회의 종속성 실패를 보여 주는 Application Insights의 스크린샷

서버 원격 분석에서 식별

서버 애플리케이션은 단일 클라이언트에서 많은 수의 연결을 검색할 수 있습니다. 다음 예제에서 Azure Front Door는 애플리케이션에 대한 게이트웨이로 작동하며, 모든 요청을 Log Analytics 작업 영역에 기록하도록 구성되었습니다.

다음 Kusto 쿼리는 Log Analytics에 대해 실행할 수 있습니다. 마지막 날에 많은 수의 요청을 애플리케이션에 보낸 클라이언트 IP 주소를 식별합니다.

AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc

다시 시도 폭풍 중에 이 쿼리를 실행하면 단일 IP 주소에서의 많은 수의 연결 시도가 표시됩니다.

1시간 내에 단일 IP 주소에서 Front Door로 수행된 81,608회의 인바운드 연결을 보여 주는 Log Analytics의 스크린샷