다음을 통해 공유


Azure Functions 안정적인 이벤트 처리

이벤트 처리는 서버리스 아키텍처와 관련된 가장 일반적인 시나리오 중 하나입니다. 이 문서에서는 메시지 손실을 방지하기 위해 Azure Functions를 사용하여 신뢰할 수 있는 메시지 프로세서를 만드는 방법을 설명합니다.

분산 시스템에서 발생하는 이벤트 스트림의 과제

초당 이벤트 100개의 일정한 속도로 이벤트를 전송하는 시스템을 생각해 봅시다. 이 속도에서는 몇 분 이내에 여러 병렬 함수 인스턴스가 초당 수신 이벤트 100개를 사용할 수 있습니다.

그러나 다음과 같이 최적이 아닌 조건이 가능합니다.

  • 이벤트 게시자가 손상된 이벤트를 보낸다면?
  • 함수 인스턴스에 처리되지 않은 예외가 발생한다면?
  • 다운스트림 시스템이 오프라인 상태가 된다면?

애플리케이션의 처리량을 유지하면서 이러한 상황을 처리할 수 있는 방법은 무엇일까요?

큐를 사용하면 신뢰할 수 있는 메시징이 자연스럽게 제공됩니다. 함수 트리거와 페어링된 함수는 큐 메시지에 대한 잠금을 만듭니다. 처리가 실패하면 잠금이 해제되므로 다른 인스턴스에서 다시 처리를 시도할 수 있습니다. 그러면 메시지가 성공적으로 평가되거나 포이즌 큐에 추가될 때까지 처리가 계속됩니다.

단일 큐 메시지가 재시도 주기에 남아 있는 경우에도 다른 병렬 실행은 계속해서 남은 메시지를 큐에서 제거합니다. 결과적으로 전체 처리량은 잘못된 메시지 하나로 인해 거의 영향을 받지 않습니다. 그러나 스토리지 큐는 순서 지정을 보장하지 않으며 Event Hubs에 필요한 처리량이 높은 요구에 맞게 최적화되지 않습니다.

이와 대조적으로 Azure Event Hubs에는 잠금 개념이 포함되지 않습니다. 높은 처리량, 여러 소비자 그룹, 재생 기능과 같은 기능을 허용하기 위해 Event Hubs 이벤트는 비디오 플레이어처럼 동작합니다. 파티션당 스트림의 단일 지점에서 이벤트를 읽습니다. 포인터를 통해 해당 위치를 중심으로 앞으로 또는 뒤로 읽을 수 있지만 이벤트를 처리할 수 있도록 포인터를 이동해야 합니다.

스트림에서 오류가 발생하면 포인터를 동일한 지점에 두기로 결정한 경우 포인터가 앞으로 이동할 때까지 이벤트 처리가 차단됩니다. 즉, 단일 이벤트 처리 문제를 다루기 위해 포인터가 중지된 경우 처리되지 않은 이벤트가 누적되기 시작합니다.

Azure Functions는 성공 또는 실패에 관계 없이 스트림의 포인터를 이동하여 교착 상태를 방지합니다. 포인터는 계속 진행되므로 함수에서 오류를 적절하게 처리해야 합니다.

Azure Functions에서 Event Hubs 이벤트를 사용하는 방법

다음 단계를 진행하는 동안 Azure Functions가 이벤트 허브 이벤트를 사용합니다.

  1. 포인터는 이벤트 허브의 각 파티션에 대한 Azure Storage에 생성되고 유지됩니다.
  2. 새 메시지를 받은 경우(기본적으로 일괄 처리로) 호스트는 메시지 일괄 처리를 사용하여 함수 트리거를 시도합니다.
  3. 예외 발생 유무에 상관 없이 함수가 실행을 완료하면 포인터가 이동하고 검사점이 스토리지 계정에 저장됩니다.
  4. 조건으로 인해 함수 실행을 완료할 수 없는 경우 호스트에서 포인터를 진행시키지 못합니다. 포인터가 이동하지 못하면 추후 검사에서 동일한 메시지가 처리되어 버립니다.
  5. 2-4단계를 반복합니다.

이 동작을 수행하면 다음과 같은 몇 가지 중요한 점이 드러납니다.

  • 처리되지 않은 예외로 인해 메시지가 손실될 수 있습니다. 예외가 발생하는 실행은 계속해서 포인터를 진행하게 됩니다. 재시도 정책을 설정하면 전체 재시도 정책이 평가될 때까지 포인터의 진행이 지연됩니다.
  • Functions는 최소 1회 제공을 보장합니다. 사용 중인 코드와 종속 시스템은 동일한 메시지를 두 번 받을 수 있다는 사실을 고려해야 할 수 있습니다.

예외 처리

일반적으로 모든 함수는 최고 수준의 코드에서 try/catch 블록을 포함해야 합니다. 특히 Event Hubs 이벤트를 사용하는 모든 함수에는 catch 블록이 있어야 합니다. 이렇게 하면 예외가 발생할 때 catch 블록이 포인터가 진행되기 전에 오류를 처리합니다.

재시도 메커니즘 및 정책

일부 예외는 본래 일시적이며 몇 분 후에 작업을 다시 시도하면 나타나지 않습니다. 이러한 이유로 첫 번째 단계는 항상 작업을 다시 시도하는 것입니다. 함수 앱 재시도 정책을 활용하거나 함수 실행 내에서 재시도 논리를 작성할 수 있습니다.

함수에 오류 처리 동작을 도입하면 기본 및 고급 재시도 정책을 모두 정의할 수 있습니다. 예를 들어 다음 규칙에 설명된 워크플로를 따르는 정책을 구현할 수 있습니다.

  • 메시지를 세 번 삽입합니다(재시도 사이에 지연이 있을 수 있음).
  • 모든 재시도의 최종 결과가 실패인 경우 큐에 메시지를 추가해 스트림에서 처리가 계속 진행될 수 있도록 합니다.
  • 그러면 손상되거나 처리되지 않은 메시지가 나중에 처리됩니다.

참고 항목

Polly는 응용 프로그램에 대한 C# 복원력 및 일시적인 오류 처리 라이브러리의 한 가지 예입니다.

예외가 아닌 오류

오류가 없는 경우에도 문제가 일부 발생합니다. 예를 들어 실행 중에 발생하는 오류를 살펴보겠습니다. 이 경우 함수가 실행을 완료하지 못하면 오프셋 포인터는 절대 진행하지 않습니다. 포인터가 이동하지 않으면 실행이 실패한 후 실행되는 모든 인스턴스는 동일한 메시지를 계속 읽습니다. 이 경우 "최소 1회" 보장이 제공됩니다.

모든 메시지가 최소 1회 처리되도록 보장하는 것은 일부 메시지는 두 번 이상 처리될 수 있다는 것을 의미합니다. 함수 앱은 이러한 가능성을 인식해야 하며 멱등성의 원칙을 중심으로 빌드돼야 합니다.

실행 중지 및 다시 시작

몇 가지 오류는 허용이 가능하지만 만약 앱에 심각한 오류가 발생하면 어떻게 할까요? 시스템이 정상 상태에 도달할 때까지 이벤트 트리거를 중지할 수 있습니다. 회로 차단기 패턴을 사용해 처리를 일시 중지할 수도 있습니다. 회로 차단기 패턴을 통해 앱은 이벤트 프로세스의 "회로를 중단"하고 나중에 다시 시작할 수 있습니다.

이벤트 프로세스에서 회로 차단기를 구현하는 데 필요한 두 가지는 다음과 같습니다.

  • 회로 상태의 추적 및 모니터링을 위해 모든 인스턴스에서 상태 공유
  • 회로 상태를 관리할 수 있는 마스터 프로세스(열림 또는 닫힘)

구현 세부 정보는 다를 수 있지만 인스턴스 간에 상태를 공유하려면 스토리지 메커니즘이 필요합니다. Azure Storage, Redis 캐시 또는 함수 컬렉션에서 액세스할 수 있는 다른 계정에 상태를 저장할 수 있습니다.

Azure Logic Apps 또는 지속성 함수는 워크플로 및 회로 상태 관리에 가장 적합합니다. 다른 서비스도 작동할 수 있지만 이 예제에서는 논리 앱이 사용됩니다. 논리 앱을 사용하여 함수의 실행을 일시 중지하고 다시 시작함으로써 회로 차단기 패턴을 구현하는 데 필요한 컨트롤을 가질 수 있습니다.

인스턴스 간 오류 임계값 정의

이벤트들을 동시에 처리하는 여러 인스턴스를 고려하려면 회로 상태를 모니터링하기 위해 공유된 외부 상태를 유지해야 합니다.

구현을 위해 선택할 수 있는 규칙은 다음을 적용할 수 있습니다.

  • 모든 인스턴스에서 30초 이내에 100개가 넘는 최종 오류가 발생한 경우 회로를 중단하고 새 메시지의 트리거를 중지합니다.

구현 세부 정보는 요구 사항에 따라 다를 수 있지만 일반적으로 다음과 같은 시스템을 만들 수 있습니다.

  1. 스토리지 계정(Azure Storage, Redis 등)에 오류를 기록합니다.
  2. 새 오류가 기록될 때 롤링 횟수를 검사하여 임계값이 충족되는지 확인합니다(예: 마지막 30초 동안 100 이상).
  3. 임계값이 충족되면 시스템에 회로를 중단하도록 지시하는 이벤트를 Azure Event Grid로 내보냅니다.

Azure Logic Apps를 통한 회로 상태 관리

다음 설명에서는 Functions 앱이 처리되지 않도록 하는 Azure Logic App을 만드는 한 가지 방법을 보여 줍니다.

Azure Logic Apps는 여러 서비스에 대한 기본 제공 커넥터와 함께 제공되고, 상태 저장 오케스트레이션 기능을 제공하며, 회로 상태 관리에 가장 적합합니다. 회로 중단의 필요성을 감지한 후에는 다음 워크플로를 구현하는 논리 앱을 빌드할 수 있습니다.

  1. Event Grid 워크플로를 트리거하고 Azure Function를 중지합니다(Azure 리소스 커넥터 사용).
  2. 워크플로를 다시 시작하는 옵션이 포함된 알림 이메일 보내기

이메일 수신자는 회로의 상태를 조사하고, 적절한 경우 알림 이메일의 링크를 통해 회로를 다시 시작할 수 있습니다. 워크플로가 함수를 다시 시작할 때 메시지가 마지막 이벤트 허브 검사점에서 처리됩니다.

이 방법을 사용하면 메시지가 손실되지 않고, 모든 메시지가 순서대로 처리되며, 필요에 따라 회로를 중단할 수 있습니다.

리소스

다음 단계

자세한 내용은 다음 리소스를 참조하세요.