다음을 통해 공유


Queue-Centric 작업 패턴(Azure를 사용하여 Real-World Cloud Apps 빌드)

작성자 : Rick Anderson, Tom Dykstra

수정 프로젝트 다운로드 또는 전자책 다운로드

Azure 전자책 을 사용하여 Real World Cloud Apps 빌드 는 Scott Guthrie가 개발한 프레젠테이션을 기반으로 합니다. 클라우드용 웹앱을 성공적으로 개발하는 데 도움이 될 수 있는 13개의 패턴과 사례를 설명합니다. 전자책에 대한 자세한 내용은 첫 번째 장을 참조하세요.

앞에서 여러 서비스를 사용하면 앱의 유효 SLA가 개별 SLA의 산물 인 "복합" SLA가 발생할 수 있음을 확인했습니다. 예를 들어 Fix It 앱은 웹 사이트, 스토리지 및 SQL Database 사용합니다. 이러한 서비스 중 하나가 실패하면 앱은 사용자에게 오류를 반환합니다.

캐싱은 읽기 전용 콘텐츠에 대한 일시적인 오류를 처리하는 좋은 방법입니다. 하지만 애플리케이션이 작동해야 하는 경우 어떻게 해야 할까요? 예를 들어 사용자가 새 수정 작업을 제출할 때 앱은 작업을 캐시에 넣을 수 없습니다. 앱은 영구 데이터 저장소에 수정 태스크를 작성하여 처리할 수 있어야 합니다.

이것이 바로 큐 중심 작업 패턴이 들어오는 곳입니다. 이 패턴을 사용하면 웹 계층과 백 엔드 서비스 간에 느슨한 결합이 가능합니다.

패턴의 작동 방식은 다음과 같습니다. 애플리케이션이 요청을 받으면 작업 항목을 큐에 넣고 즉시 응답을 반환합니다. 그런 다음 별도의 백 엔드 프로세스가 큐에서 작업 항목을 끌어와 작업을 수행합니다.

큐 중심 작업 패턴은 다음에 유용합니다.

  • 시간이 많이 걸리는 작업(높은 대기 시간).
  • 항상 사용할 수 없는 외부 서비스가 필요한 작업입니다.
  • 리소스를 많이 사용하는 작업(높은 CPU)
  • 속도 평준화의 이점을 누릴 수 있는 작업(갑작스러운 부하 버스트에 따라).

대기 시간 감소

큐는 시간이 많이 걸리는 작업을 수행할 때마다 유용합니다. 작업이 최종 사용자를 차단하는 대신 몇 초 이상 걸리는 경우 작업 항목을 큐에 넣습니다. 사용자에게 "작업 중"이라고 말한 다음 큐 수신기를 사용하여 백그라운드에서 작업을 처리합니다.

예를 들어 온라인 소매점에서 항목을 구매하면 웹 사이트에서 즉시 주문을 확인합니다. 그렇다고 해서 물건이 이미 트럭에 배달되고 있다는 뜻은 아닙니다. 그들은 큐에 작업을 넣어, 그리고 백그라운드에서 그들은 신용 검사 수행, 배송에 대 한 항목을 준비, 등등.

대기 시간이 짧은 시나리오의 경우 작업을 동기적으로 수행하는 것에 비해 총 엔드 투 엔드 시간이 큐를 사용하는 데 더 길어질 수 있습니다. 그러나 그럼에도 불구하고 다른 이점은 그 단점보다 클 수 있습니다.

안정성 향상

지금까지 살펴본 수정 버전에서는 웹 프런트 엔드가 SQL Database 백 엔드와 긴밀하게 결합되어 있습니다. SQL 데이터베이스 서비스를 사용할 수 없는 경우 사용자에게 오류가 발생합니다. 재시도가 작동하지 않는 경우(즉, 오류가 일시적 이상임) 오류를 표시하고 사용자에게 나중에 다시 시도하도록 요청하는 것뿐입니다.

SQL Database 백 엔드 실패 시 웹 프런트 엔드 실패를 보여 주는 다이어그램

큐를 사용하여 사용자가 수정 작업을 제출할 때 앱은 큐에 메시지를 씁니다. 메시지 페이로드는 작업의 JSON 표현입니다. 메시지가 큐에 기록되는 즉시 앱은 를 반환하고 사용자에게 성공 메시지를 즉시 표시합니다.

SQL 데이터베이스 또는 큐 수신기와 같은 백 엔드 서비스가 오프라인으로 전환되는 경우 사용자는 여전히 새 수정 작업을 제출할 수 있습니다. 메시지는 백 엔드 서비스를 다시 사용할 수 있을 때까지 큐에 대기합니다. 이 시점에서 백 엔드 서비스는 백로그를 따라잡습니다.

SQL Database 오류가 발생할 때 계속 작동하는 웹 프런트 엔드를 보여 주는 다이어그램

또한 이제 프런트 엔드의 복원력에 대해 걱정하지 않고 백 엔드 논리를 더 추가할 수 있습니다. 예를 들어 새 수정이 할당될 때마다 소유자에게 전자 메일 또는 SMS 메시지를 보낼 수 있습니다. 전자 메일 또는 SMS 서비스를 사용할 수 없게 되면 다른 모든 항목을 처리한 다음 전자 메일/SMS 메시지를 보내기 위한 별도의 큐에 메시지를 넣을 수 있습니다.

이전에는 유효한 SLA가 스토리지 × SQL Database = 99.7%Web Apps ×. ( 실패를 살아남기 위한 설계를 참조하세요.)

큐를 사용하도록 앱을 변경하는 경우 웹 프런트 엔드는 99.8%의 복합 SLA에 대해 Web Apps 및 스토리지에만 의존합니다. (큐는 Azure Storage 서비스의 일부이므로 Blob Storage와 동일한 SLA에 포함됩니다.)

99.8%보다 더 나은 값이 필요한 경우 서로 다른 두 지역에 두 개의 큐를 만들 수 있습니다. 하나를 주 복제본으로 지정하고 다른 하나는 보조로 지정합니다. 기본 큐를 사용할 수 없는 경우 앱에서 보조 큐로 장애 조치(failover)합니다. 둘 다 동시에 사용할 수 없을 가능성은 매우 적습니다.

속도 평준화 및 독립적인 크기 조정

큐는 속도 평준화 또는 부하 평준화라는 항목에도 유용합니다.

웹앱은 종종 트래픽의 갑작스런 버스트에 취약합니다. 자동 크기 조정을 사용하여 웹 서버를 자동으로 추가하여 증가하는 웹 트래픽을 처리할 수 있지만 자동 크기 조정은 갑작스러운 부하 급증을 처리할 만큼 빠르게 반응하지 못할 수 있습니다. 웹 서버가 큐에 메시지를 작성하여 수행해야 하는 일부 작업을 오프로드할 수 있는 경우 더 많은 트래픽을 처리할 수 있습니다. 그런 다음 백 엔드 서비스는 큐에서 메시지를 읽고 처리할 수 있습니다. 들어오는 부하가 변화함에 따라 큐의 깊이가 증가하거나 축소됩니다.

시간이 많이 소요되는 작업이 백 엔드 서비스에 오프로드되어 웹 계층은 트래픽의 급격한 급증에 보다 쉽게 대응할 수 있습니다. 또한 지정된 양의 트래픽을 적은 수의 웹 서버에서 처리할 수 있으므로 비용을 절감할 수 있습니다.

웹 계층 및 백 엔드 서비스를 독립적으로 확장할 수 있습니다. 예를 들어 3개의 웹 서버가 필요하지만 큐 메시지를 처리하는 서버는 하나만 필요할 수 있습니다. 또는 백그라운드에서 계산 집약적 작업을 실행하는 경우 더 많은 백 엔드 서버가 필요할 수 있습니다.

큐에서 작업을 처리할 때 크기 조정 계층의 표현을 보여 주는 다이어그램

자동 크기 조정은 백 엔드 서비스뿐만 아니라 웹 계층에서도 작동합니다. 백 엔드 VM의 CPU 사용량에 따라 큐에서 작업을 처리하는 VM 수를 스케일 업하거나 축소할 수 있습니다. 또는 큐에 있는 항목 수에 따라 자동 크기 조정을 수행할 수 있습니다. 예를 들어 자동 크기 조정에 큐에 10개 이하의 항목을 유지하도록 지시할 수 있습니다. 큐에 10개 이상의 항목이 있는 경우 자동 크기 조정은 VM을 추가합니다. 이를 따라잡으면 자동 크기 조정이 추가 VM을 분해합니다.

Fix It 애플리케이션에 큐 추가

큐 패턴을 구현하려면 수정 앱을 두 가지 변경해야 합니다.

  • 사용자가 새 수정 작업을 제출할 때 작업을 데이터베이스에 쓰는 대신 큐에 넣습니다.
  • 큐에서 메시지를 처리하는 백 엔드 서비스를 만듭니다.

큐의 경우 Azure Queue Storage 서비스를 사용합니다. 또 다른 옵션은 Azure Service Bus 사용하는 것입니다.

사용할 큐 서비스를 결정하려면 앱이 큐에서 메시지를 보내고 받는 방법을 고려합니다.

  • 협력 생산자 및 경쟁 소비자가 있는 경우 Azure Queue Storage 서비스를 사용하는 것이 좋습니다. "협력 생산자"는 여러 프로세스가 큐에 메시지를 추가하는 것을 의미합니다. "경쟁 소비자"는 여러 프로세스가 메시지를 큐에서 끌어와 처리하지만 지정된 메시지는 하나의 "소비자"만 처리할 수 있음을 의미합니다. 단일 큐를 사용하여 얻을 수 있는 것보다 더 많은 처리량이 필요한 경우 추가 큐 및/또는 추가 스토리지 계정을 사용합니다.
  • 게시/구독 모델이 필요한 경우 Azure Service Bus 큐를 사용하는 것이 좋습니다.

Fix It 앱은 협력 생산자 및 경쟁 소비자 모델에 적합합니다.

또 다른 고려 사항은 애플리케이션 가용성입니다. Queue Storage 서비스는 Blob Storage에 사용하는 것과 동일한 서비스의 일부이므로 이를 사용하면 SLA에 영향을 주지 않습니다. Azure Service Bus 자체 SLA를 사용하는 별도의 서비스입니다. Service Bus 큐를 사용한 경우 추가 SLA 비율을 고려해야 하며 복합 SLA는 더 낮아질 것입니다. 큐 서비스를 선택할 때 선택한 것이 애플리케이션 가용성에 미치는 영향을 이해해야 합니다. 자세한 내용은 리소스 섹션을 참조 하세요 .

큐 메시지 만들기

큐에 수정 작업을 배치하기 위해 웹 프런트 엔드는 다음 단계를 수행합니다.

  1. CloudQueueClient instance 만듭니다. CloudQueueClient instance 큐 서비스에 대한 요청을 실행하는 데 사용됩니다.
  2. 큐가 아직 없는 경우 만듭니다.
  3. 수정 작업을 직렬화합니다.
  4. CloudQueue.AddMessageAsync를 호출하여 메시지를 큐에 넣습니다.

FixItQueueManager 클래스의 생성자 및 SendMessageAsync 메서드에서 이 작업을 수행합니다.

public class FixItQueueManager : IFixItQueueManager
{
    private CloudQueueClient _queueClient;
    private IFixItTaskRepository _repository;

    private static readonly string fixitQueueName = "fixits";

    public FixItQueueManager(IFixItTaskRepository repository)
    {
        _repository = repository;
        CloudStorageAccount storageAccount = StorageUtils.StorageAccount;
        _queueClient = storageAccount.CreateCloudQueueClient();
    }

    // Puts a serialized fixit onto the queue.
    public async Task SendMessageAsync(FixItTask fixIt)
    {
        CloudQueue queue = _queueClient.GetQueueReference(fixitQueueName);
        await queue.CreateIfNotExistsAsync();

        var fixitJson = JsonConvert.SerializeObject(fixIt);
        CloudQueueMessage message = new CloudQueueMessage(fixitJson);

        await queue.AddMessageAsync(message);
    }

    // Processes any messages on the queue.
    public async Task ProcessMessagesAsync()
    {
        CloudQueue queue = _queueClient.GetQueueReference(fixitQueueName);
        await queue.CreateIfNotExistsAsync();

        while (true)
        {
            CloudQueueMessage message = await queue.GetMessageAsync();
            if (message == null)
            {
                break;
            }
            FixItTask fixit = JsonConvert.DeserializeObject<FixItTask>(message.AsString);
            await _repository.CreateAsync(fixit);
            await queue.DeleteMessageAsync(message);
        }
    }
}

여기서는 Json.NET 라이브러리를 사용하여 fixit를 JSON 형식으로 직렬화합니다. 원하는 직렬화 방법을 사용할 수 있습니다. JSON은 사람이 읽을 수 있다는 장점이 있지만 XML보다 자세한 내용은 적습니다.

프로덕션 품질 코드는 오류 처리 논리를 추가하고, 데이터베이스를 사용할 수 없게 되면 일시 중지하고, 복구를 더 명확하게 처리하고, 애플리케이션 시작 시 큐를 만들고, "포이즌" 메시지를 관리합니다. (포이즌 메시지는 어떤 이유로 처리할 수 없는 메시지입니다. 작업자 역할이 지속적으로 처리, 실패, 다시 시도, 실패 등을 시도하는 큐에 포이즌 메시지를 배치하지 않도록 합니다.)

프런트 엔드 MVC 애플리케이션에서 새 작업을 만드는 코드를 업데이트해야 합니다. 작업을 리포지토리에 배치하는 대신 위에 표시된 메서드를 SendMessageAsync 호출합니다.

public async Task<ActionResult> Create(FixItTask fixittask, HttpPostedFileBase photo)
{
    if (ModelState.IsValid)
    {
        fixittask.CreatedBy = User.Identity.Name;
        fixittask.PhotoUrl = await photoService.UploadPhotoAsync(photo);
        //previous code:
        //await fixItRepository.CreateAsync(fixittask);
        //new code:
        await queueManager.SendMessageAsync(fixittask);
        return RedirectToAction("Success");
    }
    return View(fixittask);
}

큐 메시지 처리

큐에서 메시지를 처리하려면 백 엔드 서비스를 만듭니다. 백 엔드 서비스는 다음 단계를 수행하는 무한 루프를 실행합니다.

  1. 큐에서 다음 메시지를 가져옵니다.
  2. 메시지를 수정 작업으로 역직렬화합니다.
  3. 수정 작업을 데이터베이스에 씁니다.

백 엔드 서비스를 호스트하기 위해 작업자 역할이 포함된 Azure Cloud Service를 만듭니다. 작업자 역할은 백 엔드 처리를 수행할 수 있는 하나 이상의 VM으로 구성됩니다. 이러한 VM에서 실행되는 코드는 사용할 수 있게 되면 큐에서 메시지를 끌어오게 됩니다. 각 메시지에 대해 JSON 페이로드를 역직렬화하고 이전에 웹 계층에서 사용한 것과 동일한 리포지토리를 사용하여 수정 작업 엔터티의 instance 데이터베이스에 씁니다.

다음 단계에서는 표준 웹 프로젝트가 있는 솔루션에 작업자 역할 프로젝트를 추가하는 방법을 보여줍니다. 이러한 단계는 다운로드할 수 있는 수정 프로젝트에서 이미 수행되었습니다.

먼저 Visual Studio 솔루션에 클라우드 서비스 프로젝트를 추가합니다. 솔루션을 마우스 오른쪽 단추로 클릭하고 추가, 새 프로젝트를 차례로 선택합니다. 왼쪽 창에서 Visual C# 을 확장하고 클라우드를 선택합니다.

.Net Framework에서 새 프로젝트 메뉴를 추가하는 단계의 스크린샷

새 Azure Cloud Service 대화 상자의 왼쪽 창에서 Visual C# 노드를 확장합니다. 작업자 역할을 선택하고 오른쪽 화살표 아이콘을 클릭합니다.

다음 스크린샷은 이전 이미지의 연속을 보여 줍니다. 그리고 올바른 이미지를 강조 표시하여 Azure Cloud Service에 사용할 수 있는 다양한 선택 항목을 보여 줍니다.

(웹 역할을 추가할 수도 있습니다. Azure 웹 사이트에서 실행하는 대신 동일한 클라우드 서비스에서 Fix It 프런트 엔드를 실행할 수 있습니다. 프런트 엔드와 백 엔드 간의 연결을 보다 쉽게 조정할 수 있는 몇 가지 이점이 있습니다. 그러나 이 데모를 간단하게 유지하기 위해 프런트 엔드를 Azure App Service Web App에서 유지하고 클라우드 서비스에서만 백 엔드를 실행합니다.)

기본 이름은 작업자 역할에 할당됩니다. 이름을 변경하려면 마우스를 오른쪽 창의 작업자 역할 위로 마우스로 가리킨 다음 연필 아이콘을 클릭합니다.

다른 할당과 이름을 변경하는 방법을 보여 주는 작업자 역할 프로젝트를 보여 주는 스크린샷

확인을 클릭하여 대화 상자를 완료합니다. 그러면 Visual Studio 솔루션에 두 개의 프로젝트가 추가됩니다.

  • 구성 정보를 포함하여 클라우드 서비스를 정의하는 Azure 프로젝트입니다.
  • 작업자 역할을 정의하는 작업자 역할 프로젝트입니다.

작업자 역할을 보여 주는 스크린샷, 역할을 정의하고 'MyFixIt' 프로젝트 솔루션 옵션 목록을 보여 줌

자세한 내용은 Visual Studio를 사용하여 Azure 프로젝트 만들기를 참조하세요.

작업자 역할 내에서 앞에서 본 클래스의 메서드를 ProcessMessageAsyncFixItQueueManager 호출하여 메시지를 폴링합니다.

public class WorkerRole : RoleEntryPoint
{
    public override void Run()
    {
        Task task = RunAsync(tokenSource.Token);
        try
        {
            task.Wait();
        }
        catch (Exception ex)
        {
            logger.Error(ex, "Unhandled exception in FixIt worker role.");
        }
    }

    private async Task RunAsync(CancellationToken token)
    {
        using (var scope = container.BeginLifetimeScope())
        {
            IFixItQueueManager queueManager = scope.Resolve<IFixItQueueManager>();
            while (!token.IsCancellationRequested)
            {
                try
                {
                    await queueManager.ProcessMessagesAsync();
                }
                catch (Exception ex)
                {
                    logger.Error(ex, "Exception in worker role Run loop.");
                }
                await Task.Delay(1000);
            }
        }
    }
    // Other code not shown.
}

메서드는 ProcessMessagesAsync 대기 중인 메시지가 있는지 확인합니다. 메시지가 있는 경우 메시지를 엔터티로 FixItTask 역직렬화하고 엔터티를 데이터베이스에 저장합니다. 큐가 비어 있는 때까지 반복됩니다.

public async Task ProcessMessagesAsync()
{
    CloudQueue queue = _queueClient.GetQueueReference(fixitQueueName);
    await queue.CreateIfNotExistsAsync();
    while (true)
    {
        CloudQueueMessage message = await queue.GetMessageAsync();
        if (message == null)
        {
            break;
        }
        FixItTask fixit = JsonConvert.DeserializeObject<FixItTask>(message.AsString);
        await _repository.CreateAsync(fixit);
        await queue.DeleteMessageAsync(message);
    }
}

큐 메시지에 대한 폴링에는 작은 트랜잭션 요금이 발생하므로 처리 대기 중인 메시지가 없으면 작업자 역할의 RunAsync 메서드는 를 호출 Task.Delay(1000)하여 다시 폴링하기 전에 1초 동안 대기합니다.

웹 프로젝트에서 비동기 코드를 추가하면 IIS가 제한된 스레드 풀을 관리하므로 성능이 자동으로 향상될 수 있습니다. 작업자 역할 프로젝트의 경우는 그렇지 않습니다. 작업자 역할의 확장성을 향상시키기 위해 다중 스레드 코드를 작성하거나 비동기 코드를 사용하여 병렬 프로그래밍을 구현할 수 있습니다. 이 샘플에서는 병렬 프로그래밍을 구현하지 않지만 병렬 프로그래밍을 구현할 수 있도록 코드를 비동기식으로 만드는 방법을 보여 있습니다.

요약

이 챕터에서는 큐 중심 작업 패턴을 구현하여 애플리케이션 응답성, 안정성 및 확장성을 개선하는 방법을 살펴보았습니다.

이 전자책에서 다루는 13가지 패턴 중 마지막 패턴이지만 성공적인 클라우드 앱을 빌드하는 데 도움이 될 수 있는 다른 많은 패턴과 사례가 있습니다. 마지막 챕터에서는 이러한 13가지 패턴에서 다루지 않은 topics 리소스에 대한 링크를 제공합니다.

리소스

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

설명서:

비디오:

  • FailSafe: 확장 가능하고 복원력 있는 Cloud Services 빌드합니다. 울리히 호만, 마크 머큐리, 마크 심스의 9부작 비디오 시리즈. 실제 고객과의 MICROSOFT CAT(고객 자문 팀) 경험에서 가져온 스토리와 함께 매우 액세스 가능하고 흥미로운 방식으로 높은 수준의 개념과 아키텍처 원칙을 제시합니다. Azure Storage 서비스 및 큐에 대한 소개는 35:13부터 시작되는 에피소드 5를 참조하세요.