F#으로 Azure Queue Storage 시작하기

F#으로 Azure Queue Storage 시작하기

애플리케이션을 확장해서 만들다 보면, 컴포넌트를 서로 분리해서 각각 독립적으로 늘리고 싶을 때가 있어요. Azure Queue Storage는 바로 그런 컴포넌트 사이에 구름 메시징(cloud messaging)을 제공하는 서비스예요. 클라우드, 데스크톱, 온프레미스 서버, 모바일 기기 어디에서 실행 중이든, 비동기 메시징을 통해 컴포넘트끼리 통신하게 해 주죠. 비동기 태스크를 관리하거나 프로세스 워크플로를 구성하는 데도 쓰여요.

출처: Get started with Azure Queue Storage using F#

본문

이 튜토리얼에서는 Azure Queue Storage를 이용한 흔한 작업을 F# 코드로 어떻게 쓰는지를 다뤄요. 큐를 만들고 지우고, 큐 메시지를 추가·읽기·삭제하는 작업이 여기에 포함돼요.

큐 스토리지 개념 자체에 대한 정리는 .NET용 큐 스토리지 가이드를 보면 돼요. 편의상 이 튜토리얼들은 연결 문자열로 Azure에 인증해요. 다만 보안을 최우선으로 한다면 관리 ID와 함께 Microsoft Entra ID를 사용하는 쪽이 좋아요.

준비 사항(Prerequisites)

이 가이드를 쓰려면 먼저 Azure 스토리지 계정을 만들어야 해요. 그리고 이 계정의 스토리지 액세스 키도 필요해요.

F# 스크립트를 만들고 F# Interactive 시작하기

이 글의 예제는 F# 애플리케이션에서도, F# 스크립트에서도 그대로 쓸 수 있어요. F# 스크립트를 만드려면 .fsx 확장자의 파일을 하나 만들면 되는데요, 예를 들어 개발 환경에서 queues.fsx 파일을 만드는 식이에요.

스크립트 실행하기

F# Interactive, 곧 dotnet fsi는 대화형으로 실행할 수도 있고, 명령줄에서 스크립트를 실행하도록 띄울 수도 있어요. 명령줄 구문은 이렇게 생겼어요.

> dotnet fsi [options] [ script-file [arguments] ]

스크립트에서 패키지 추가하기

다음으로 #r nuget:package name 구문으로 Azure.Storage.Queues 패키지를 설치하고 네임스페이스를 열어요. 예를 들면 이런 식이죠.

> #r "nuget: Azure.Storage.Queues"
open Azure.Storage.Queues

네임스페이스 선언 추가하기

queues.fsx 파일 맨 위에 아래 open 문들을 추가해요.

open Azure.Storage.Queues // Namespace for Queue storage types
open System
open System.Text

연결 문자열 가져오기

이 튜토리얼에는 Azure Storage 연결 문자열이 필요해요. 연결 문자열에 대한 더 자세한 내용은 Storage 연결 문자열 구성 문서를 참고하세요.

튜토리얼에서는 스크립트 안에 연결 문자열을 아래처럼 직접 넣게 돼요.

let storageConnString = "..." // fill this in from your storage account

큐 서비스 클라이언트 만들기

QueueClient 클래스는 Queue storage에 저장된 큐를 가져올 수 있게 해 줘요. 클라이언트를 만드는 방법 하나를 보면 이래요.

let queueClient = QueueClient(storageConnString, "myqueue")

이제 Queue storage에서 데이터를 읽고 쓰는 코드를 짤 준비가 됐어요.

큐 만들기

이 예제는 큐가 아직 없으면 만드는 방법을 보여 줘요.

queueClient.CreateIfNotExists()

큐에 메시지 삽입하기

이미 존재하는 큐에 메시지를 넣으려면 먼저 새 Message를 만들고, 그다음 SendMessage 메서드를 호출해요. Message는 문자열(UTF-8 형식)이나 바이트 배열 어느 쪽에서도 만들 수 있는데, 아래처럼요.

queueClient.SendMessage("Hello, World") // Insert a String message into a queue
queueClient.SendMessage(BinaryData.FromBytes(Encoding.UTF8.GetBytes("Hello, World"))) // Insert a BinaryData message into a queue

다음 메시지 훑어보기(Peek)

큐 맨 앞의 메시지를 큐에서 빼내지 않고 그냥 들여다보려면 PeekMessage 메서드를 호출하면 돼요.

let peekedMessage = queueClient.PeekMessage()
let messageContents = peekedMessage.Value.Body.ToString()

처리를 위해 다음 메시지 가져오기

큐 맨 앞의 메시지를 꺼내서 처리하려면 ReceiveMessage 메서드로 가져오면 돼요.

let updateMessage = queueClient.ReceiveMessage().Value

처리를 성공적으로 마쳤다는 표시는 나중에 DeleteMessage로 해 줘요.

큐에 있는 메시지 내용 바꾸기

꺼내 온 메시지의 내용은 큐 안에서 그대로(제자리에서) 바꿀 수 있어요. 메시지가 작업(task)을 나타낸다면, 이 기능으로 작업 상태를 갱신할 수 있겠죠. 아래 코드는 큐 메시지를 새 내용으로 갱신하면서, visibility timeout을 60초 더 연장해요. 이렇게 하면 메시지에 얽힌 작업 상태를 저장해 두고, 클라이언트가 그 메시지를 계속 처리할 시간을 한 분 더 주는 거예요. 이 기법으로 큐 메시지에 여러 단계로 이루어진 워크플로를 추적할 수 있어요. 처리 중간에 하드웨어나 소프트웨어 오류가 나서 단계 하나가 실패해도, 처음부터 다시 시작할 필요가 없어요. 보통은 재시도 횟수(retry count)도 함께 두는데요, 메시지가 어떤 횟수 이상 재시도되면 그냥 지워버려요. 그렇게 하면 처리할 때마다 애플리케이션 오류를 일으키는 메시지로부터 보호할 수 있어요.

queueClient.UpdateMessage(
    updateMessage.MessageId,
    updateMessage.PopReceipt,
    "Updated contents.",
    TimeSpan.FromSeconds(60.0))

다음 메시지 큐에서 빼내기(De-queue)

코드가 큐에서 메시지를 빼내는 과정은 두 단계로 나뉘어요. ReceiveMessage를 호출하면 큐의 다음 메시지를 받아오는데, 이 메시지는 같은 큐에서 메시지를 읽는 다른 코드에는 보이지 않게 돼요. 기본적으로 이 메시지는 30초 동안 보이지 않게 유지돼요. 큐에서 메시지를 완전히 제거하려면 DeleteMessage도 호출해야 해요. 이렇게 두 단계로 메시지를 지우는 이유는, 코드가 메시지 처리를 하드웨어나 소프트웨어 오류로 실패했을 때 다른 코드 인스턴스가 같은 메시지를 받아 다시 시도할 수 있게 하려는 거예요. 메시지 처리가 끝나면 바로 DeleteMessage를 호출해요. 지금까지 본 Queue 메서드에는 모두 Async 대응 버전이 있어요.

let deleteMessage = queueClient.ReceiveMessage().Value
queueClient.DeleteMessage(deleteMessage.MessageId, deleteMessage.PopReceipt)

흔한 Queue storage API와 함께 Async 워크플로 사용하기

이 예제는 흔한 Queue storage API를 async 워크플로와 함께 쓰는 방법을 보여 줘요.

async {
    let! exists = queueClient.CreateIfNotExistsAsync() |> Async.AwaitTask

    let! delAsyncMessage = queueClient.ReceiveMessageAsync() |> Async.AwaitTask

    // ... process the message here ...

    // Now indicate successful processing:
    queueClient.DeleteMessageAsync(delAsyncMessage.Value.MessageId, delAsyncMessage.Value.PopReceipt) |> Async.AwaitTask
}

큐에서 메시지를 빼내는 추가 옵션

큐에서 메시지를 가져오는 방식을 커스터마이즈하는 방법은 두 가지가 있어요. 첫째, 메시지를 **배치(batch)**로 가져올 수 있어요(최대 32개). 둘째, visibility timeout을 더 길게 또는 더 짧게 설정해서 각 메시지를 충분히 처리할 시간을 조절할 수 있어요. 아래 코드는 ReceiveMessages로 한 번에 20개 메시지를 가져와 각각을 처리해요. 그리고 각 메시지의 visibility timeout을 5분으로 설정했어요. 이 5분은 모든 메시지에 동시에 시작돼요. 그래서 ReceiveMessages 호출 시점부터 5분이 지나면, 삭제되지 않은 메시지는 다시 보이게 돼요.

for dequeueMessage in queueClient.ReceiveMessages(20, Nullable(TimeSpan.FromMinutes(5.))).Value do
        // Process the message here.
        queueClient.DeleteMessage(dequeueMessage.MessageId, dequeueMessage.PopReceipt)

큐 길이 가져오기

큐에 있는 메시지 수의 추정값은 가져올 수 있어요. GetProperties 메서드는 Queue 서비스에 큐의 속성(메시지 수 포함)을 가져오라고 요청해요. ApproximateMessagesCount 프로퍼티는 GetProperties 메서드가 가져온 마지막 값을 돌려줘요.

let properties = queueClient.GetProperties().Value
let count = properties.ApproximateMessagesCount

큐 삭제하기

큐와 그 안에 담긴 모든 메시지를 지우려면 큐 객체의 Delete 메서드를 호출하면 돼요.

queueClient.DeleteIfExists()

참고(Note)

이전 라이브러리에서 넘어오는 경우라면 한 가지 알아둘게요. 예전 라이브러리는 기본적으로 메시지를 Base64로 인코딩했는데, 새 라이브러리는 성능상 그렇게 하지 않아요. 인코딩을 설정하는 방법은 MessageEncoding 문서를 참고하세요.

더 알아보기