다음을 통해 공유


다시 시도 폭풍 안티패턴

서비스를 사용할 수 없거나 사용량이 많은 경우 클라이언트가 연결을 너무 자주 다시 시도하면 서비스가 복구하는 데 어려움을 겪을 수 있으며 문제를 악화시킬 수 있습니다. 요청은 일반적으로 정의된 기간 동안만 유효하기 때문에 영원히 다시 시도하는 것은 의미가 없습니다.

문제 설명

클라우드에서 서비스는 때때로 문제가 발생하며 클라이언트에서 사용할 수 없게 되거나 클라이언트를 제한하거나 속도를 제한해야 합니다. 클라이언트가 실패한 서비스 연결을 다시 시도하는 것이 좋지만 너무 자주 또는 너무 오랫동안 다시 시도하지 않는 것이 중요합니다. 서비스가 복구되지 않을 가능성이 높기 때문에 짧은 기간 내에 재시도는 성공할 가능성이 낮습니다. 또한 복구를 시도하는 동안 많은 연결 시도가 이루어지면 서비스가 더 많은 스트레스를 받을 수 있으며, 반복된 연결 시도는 서비스에 과부하가 가해지고 근본적인 문제를 악화시킬 수도 있습니다.

다음 예제에서는 클라이언트가 서버 기반 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용) 또는 다시 시도 (JavaScript용)와 같은 라이브러리를 사용하여 재시도 논리를 올바르게 처리하고 코드를 직접 작성하지 않도록 하는 것이 좋습니다.
  • 지원하는 환경에서 실행하는 경우 서비스 메시(또는 다른 추상화 계층)를 사용하여 아웃바운드 호출을 보냅니다. 일반적으로 Dapr과 같은 이러한 도구는 재시도 정책을 지원하고 반복된 시도 후 철회와 같은 모범 사례를 자동으로 따릅니다. 이 방법은 다시 시도 코드를 직접 작성할 필요가 없다는 것을 의미합니다.
  • 요청을 일괄 처리하고 사용 가능한 경우 요청 풀링을 사용하는 것이 좋습니다. 많은 SDK가 사용자를 대신하여 요청 일괄 처리 및 연결 풀링을 처리하므로 애플리케이션이 수행하는 아웃바운드 연결 시도의 총 수를 줄입니다. 이러한 연결을 너무 자주 다시 시도하지 않도록 주의해야 합니다.

또한 서비스는 재시도 폭풍으로부터 자신을 보호해야 합니다.

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

고려 사항

  • 클라이언트는 반환된 오류 유형을 고려해야 합니다. 일부 오류 유형은 서비스의 실패를 나타내지 않고 클라이언트가 잘못된 요청을 보냈음을 나타냅니다. 예를 들어 클라이언트 애플리케이션이 400 Bad Request 오류 응답을 수신하는 경우 서버에서 요청이 유효하지 않다고 말하기 때문에 동일한 요청을 다시 시도하는 것이 도움이 되지 않을 수 있습니다.
  • 클라이언트는 연결을 다시 시도하기에 적합한 시간을 고려해야 합니다. 재시도해야 하는 시간은 비즈니스 요구 사항 및 오류를 사용자 또는 호출자에게 합리적으로 전파할 수 있는지 여부에 따라 결정됩니다. 대부분의 애플리케이션에서 몇 초 또는 몇 분 동안 다시 시도하는 것으로 충분합니다.

문제를 검색하는 방법

클라이언트의 관점에서 이 문제의 증상으로는 매우 긴 응답 시간이나 처리 시간이 있을 수 있으며, 연결을 다시 시도하려는 반복적인 시도를 나타내는 원격 분석 데이터도 포함될 수 있습니다.

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

진단 예제

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

클라이언트 텔레메트리에서 식별

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

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

30분 동안 단일 종속성에 대한 21k 종속성 실패를 보여 주는 Application Insights의 스크린샷

서버 원격 분석에서 식별

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

Log Analytics에 대해 다음 Kusto 쿼리를 실행할 수 있습니다. 마지막 날 내에 애플리케이션에 많은 수의 요청을 보낸 클라이언트 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의 스크린샷