메시지 전송, 잠금 및 확인

Service Bus와 같은 메시지 브로커의 핵심적인 기능은 메시지를 큐 또는 토픽에 수신한 후 나중에 검색할 수 있도록 보관하는 것입니다. 송신은 메시지 브로커에 대한 메시지 전송에 일반적으로 사용되는 용어입니다. 수신은 검색 클라이언트로의 메시지 전송에 일반적으로 사용되는 용어입니다.

클라이언트가 메시지를 보낼 때 일반적으로 메시지가 broker에 의해 올바르게 전송되고 수락되었는지 또는 어떤 종류의 오류가 발생했는지 여부를 알고 싶어 합니다. 이러한 긍정 또는 부정 승인은 메시지의 전송 상태에 대한 클라이언트와 브로커 모두의 이해를 확인합니다. 따라서 이를 합의라고 합니다.

마찬가지로 브로커가 클라이언트로 메시지를 전송할 때 broker와 클라이언트는 메시지가 성공적으로 처리되어 제거될 수 있는지 또는 메시지 배달 또는 처리가 실패했는지 여부를 이해하려고 하므로 메시지를 다시 전달해야 할 수 있습니다.

송신 작업 확인

Service Bus에 대한 송신 작업은 지원되는 Service Bus API 클라이언트 중 하나를 사용하여 항상 명시적으로 확인됩니다. 즉, API 작업은 Service Bus의 수락 결과가 도착하기를 기다린 후 송신 작업을 완료합니다.

Service Bus에서 메시지를 거부하면 거부에는 오류 표시와 내부에 추적 ID가 들어 있는 텍스트가 포함됩니다. 또한 거부에는 성공을 예측하면서 작업을 다시 시도할 수 있는지 여부에 대한 정보도 포함됩니다. 클라이언트에서 이 정보는 예외로 전환되고 송신 작업의 호출자에게 제기됩니다. 메시지가 수락되면 작업이 자동으로 완료됩니다.

AMQP(Advanced Messaging Queuing Protocol)는 .NET 표준, Java, JavaScript, Python 및 Go 클라이언트에서 지원되는 유일한 프로토콜입니다. .NET Framework 클라이언트의 경우 SBMP(Service Bus 메시징 프로토콜) 또는 AMQP를 사용할 수 있습니다. AMQP 프로토콜을 사용하는 경우 메시지 전송 및 확인은 파이프라인되고 비동기적입니다. 비동기 프로그래밍 모델 API 변형을 사용하는 것이 좋습니다.

2026년 9월 30일에 Azure SDK 지침을 따르지 않는 Azure Service Bus SDK 라이브러리 WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus 및 com.microsoft.azure.servicebus를 사용 중지합니다. 또한 SBMP 프로토콜에 대한 지원이 종료되므로 2026년 9월 30일 이후에는 더 이상 이 프로토콜을 사용할 수 없습니다. 해당 날짜 마이그레이션에 중요한 보안 업데이트와 개선된 기능을 제공하는 최신 Azure SDK 라이브러리로 마이그레이션합니다.

이전 라이브러리는 2026년 9월 30일 이후에도 계속 사용할 수 있지만 더 이상 Microsoft로부터 공식 지원 및 업데이트를 받을 수 없습니다. 자세한 내용은 사용 중지 공지 지원을 참조하세요.

SBMP 프로토콜 또는 HTTP 1.1을 사용하는 경우처럼 보낸 사람은 각 메시지가 승인될 때까지 기다릴 필요 없이, 여러 메시지를 빠르게 연속해서 전송할 수 있습니다. 이러한 비동기 송신 작업은 해당 메시지가 분할된 엔터티에서 수락 및 저장될 때 또는 다른 엔터티에 대한 송신 작업이 겹칠 때 완료됩니다. 완료는 원래 송신 순서와 다르게도 발생할 수 있습니다.

송신 작업의 결과를 처리하는 전략은 애플리케이션 성능에 즉각적으로 지대한 영향을 미칠 수 있습니다. 이 섹션의 예제는 C#으로 작성되었으며 Java 퓨처, Java 모노, JavaScript 프라미스 및 다른 언어의 동등한 개념에 적용됩니다.

여기서는 일반 루프로 표시된 것처럼 애플리케이션이 갑자기 대량의 메시지를 생성하며 다음 메시지를 송신하기 전에 각 송신 작업이 완료될 때까지 대기해야 할 경우, 동기 또는 비동기 API 둘 다 비슷하게 10개 메시지 송신은 확인을 위해 10번의 순차적 전체 왕복이 수행된 후에만 완료됩니다.

온-프레미스 사이트에서 Service Bus까지의 70밀리초 전송 제어 프로토콜(TCP) 왕복 대기 시간 거리를 가정하고 Service Bus가 각 메시지를 수락하고 저장할 수 있도록 10ms만 제공하면 다음 루프는 페이로드 전송 시간 또는 잠재적 경로 정체 영향을 계산하지 않고 최소 8초가 걸립니다.

for (int i = 0; i < 10; i++)
{
    // creating the message omitted for brevity
    await sender.SendMessageAsync(message);
}

애플리케이션이 10개의 비동기 송신 작업을 즉시 연속적으로 시작하고 각각이 별도로 완료될 때까지 기다리는 경우 해당 10개 송신 작업에 대한 왕복 시간이 겹칩니다. 10개 메시지가 바로 연속해서 전송되고 심지어 TCP 프레임을 공유할 수도 있으며, 전반적인 전송 기간은 대체적으로 브로커에 메시지를 전송하는 데 걸리는 네트워크 관련 시간에 따라 좌우됩니다.

이전 루프와 동일하게 가정할 경우 다음 루프에 대해 겹치는 총 실행 시간은 1초 미만으로 유지될 수 있습니다.

var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
    tasks.Add(sender.SendMessageAsync(message));
}
await Task.WhenAll(tasks);

모든 비동기 프로그래밍 모델이 보류 중인 작업을 보유하는 특정 형태의 메모리 기반의 숨겨진 작업 큐를 사용한다는 점에 유의해야 합니다. send API가 반환되면 송신 작업은 해당 작업 큐에 대기되지만 프로토콜 제스처는 해당 작업이 실행될 차례가 되어야만 시작됩니다. 메시지의 버스트를 푸시하는 경향이 있고 안정성이 중요한 코드의 경우 전송된 모든 메시지가 와이어에 배치될 때까지 메모리를 차지하기 때문에 너무 많은 메시지가 한 번에 "진행 중"이 아니라는 주의를 기울여야 합니다.

C#으로 작성된 다음 코드 조각에 표시되는 세마포는 필요할 때 이러한 애플리케이션 수준 제한을 사용하도록 설정하는 동기화 개체입니다. 이러한 세마포를 사용하면 최대 10개의 메시지를 한 번에 처리할 수 있습니다. 10개의 사용 가능한 세마포 잠금 중 하나는 송신 전에 적용되고 송신이 완료되면 해제됩니다. 루프를 통과하는 11번째 패스는 이전 보내기 작업 중 하나 이상이 완료될 때까지 기다린 다음 잠금을 사용할 수 있게 합니다.

var semaphore = new SemaphoreSlim(10);

var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
    await semaphore.WaitAsync();

    tasks.Add(sender.SendMessageAsync(message).ContinueWith((t)=>semaphore.Release()));
}
await Task.WhenAll(tasks);

애플리케이션은 작업 결과를 검색하지 않으면서 "fire-and-forget"(실행 후 망각) 방식으로 비동기 송신 작업을 시작하지 않아야 합니다. 이렇게 하면 메모리가 고갈될 때까지 내부 및 보이지 않는 작업 큐가 로드될 수 있으며 애플리케이션은 송신 오류를 검색하지 못할 수 있습니다.

for (int i = 0; i < 10; i++)
{
    sender.SendMessageAsync(message); // DON’T DO THIS
}

낮은 수준의 AMQP 클라이언트를 사용하는 Service Bus는 "사전 설정" 전송도 허용합니다. 미리 설정된 전송은 어떤 방식으로든 결과가 클라이언트에 다시 보고되지 않고 메시지가 전송될 때 해결된 것으로 간주되는 화재 및 잊어버리기 작업입니다. 클라이언트에 대한 피드백이 부족하다는 것은 진단을 위해 사용할 수 있는 실행 가능 데이터가 없으며, 이 모드가 Azure 지원을 통해 도움을 제공하는 데 적합하지 않다는 것을 의미합니다.

수신 작업 확인

수신 작업의 경우 Service Bus API 클라이언트에서는 두 가지 명시적 모드인 수신 및 삭제보기-잠금을 사용할 수 있습니다.

ReceiveAndDelete

수신 및 삭제 모드는 수신 클라이언트로 송신하는 모든 메시지를 송신 시 확인된 상태로 간주하도록 브로커에 지시합니다. 즉, 브로커가 메시지를 와이어에 배치하는 즉시 메시지가 사용되는 것으로 간주됩니다. 메시지 전송이 실패할 경우 메시지는 손실됩니다.

이 모드의 장점은 수신기가 메시지에 대해 추가 작업을 수행할 필요가 없으며 확인 결과를 기다리느라 지연되지도 않는다는 것입니다. 개별 메시지에 포함된 데이터에 값이 부족하고 매우 짧은 기간 동안만 의미가 있는 경우 이 모드를 사용하는 것이 좋습니다.

PeekLock

보기-잠금 모드는 수신 클라이언트가 수신된 메시지를 명시적으로 확인하기 원한다는 것을 브로커에 알려줍니다. 메시지가 서비스에서 배타적 잠금 상태에 있는 동안 메시지를 수신기가 처리할 수 있지만 경쟁하는 다른 수신기는 해당 메시지를 볼 수 없습니다. 잠금 기간은 처음에 큐 또는 구독 수준에서 정의되며 RenewMessageLockAsync 작업을 통해 잠금을 소유한 클라이언트에 의해 연장될 수 있습니다. 잠금 갱신에 대한 자세한 내용은 이 문서의 잠금 갱신 섹션을 참조하세요.

메시지가 잠겨 있으면 동일한 큐 또는 구독에서 수신하는 다른 클라이언트가 잠금을 해제하고, 활성 잠금 상태가 아닌 사용 가능한 다음 메시지를 검색합니다. 메시지에 대한 잠금이 명시적으로 해제되거나 잠금이 만료되면 메시지는 다시 배달을 위해 검색 순서의 앞이나 앞에 배치됩니다.

수신기가 메시지를 반복적으로 해제하거나 정의된 횟수(최대 배달 횟수) 동안 잠금 기한이 경과되도록 하면 메시지가 큐 또는 구독에서 자동으로 제거되고 연결된 배달 못한 편지 큐에 추가됩니다.

수신 클라이언트는 메시지에 대한 Complete API를 호출할 때 긍정 승인으로 수신된 메시지의 확인을 시작합니다. 이를 통해 브로커는 메시지가 성공적으로 처리되었음을 알 수 있으며, 메시지는 큐 또는 구독에서 제거됩니다. 브로커는 확인을 수행할 수 있는지 여부를 나타내는 응답으로 받는 사람의 확인 의도에 회신합니다.

수신 클라이언트가 메시지를 처리하지 못하지만 메시지를 다시 배달하려고 할 경우 메시지에 대한 Abandon API를 호출하여 메시지를 해제하고 즉시 잠금을 해제할 것을 명시적으로 요청하거나, 아무 작업도 수행하지 않고 잠금 기한이 경과되도록 할 수 있습니다.

수신 클라이언트가 메시지를 처리하지 못하고 메시지를 다시 배달하고 작업을 다시 시도해도 도움이 되지 않는다는 사실을 확인하면 메시지를 거부할 수 있습니다. 그러면 메시지에 대한 DeadLetter API를 호출하여 메시지를 배달 못한 편지 큐로 이동하고, 배달 못한 편지 큐에서 메시지와 함께 검색할 수 있는 이유 코드를 포함하는 사용자 지정 속성을 설정할 수 있습니다.

참고 항목

큐 또는 구독에 대해 배달 못한 편지 기능을 사용하도록 설정된 경우에만 큐 또는 토픽 구독에 배달 못 한 편지 하위 큐가 있습니다.

확인의 특수 사례는 지연으로, 이에 대해서는 별도 문서에서 논의됩니다.

Complete, DeadLetter 또는 RenewLock 작업은 네트워크 문제로 인해 실패하거나, 보류된 잠금이 만료되었거나, 해결을 방지하는 다른 서비스 쪽 조건이 있는 경우 실패할 수 있습니다. 후자의 경우 중 하나에서 서비스는 API 클라이언트에서 예외로 인식되는 부정 승인을 전송합니다. 끊어진 네트워크 연결이 문제인 경우 Service Bus는 다른 연결의 기존 AMQP 링크 복구를 지원하지 않으므로 잠금이 삭제됩니다.

Complete 실패하는 경우( 일반적으로 메시지 처리가 끝날 때 그리고 몇 분 동안의 처리 작업 후에 발생하는 경우) 수신 애플리케이션은 작업 상태를 유지하고 두 번째로 배달될 때 동일한 메시지를 무시할지 또는 메시지를 다시 배달할 때 작업 결과를 내보내고 다시 시도할지 여부를 결정할 수 있습니다.

중복 메시지 배달을 식별하기 위한 일반적인 메커니즘은 보낸 사람이 원본 프로세스의 식별자에 맞게 고유한 값으로 설정할 수 있고 설정해야 하는 메시지 ID를 확인하는 것입니다. 작업 스케줄러는 지정된 작업자를 사용하여 메시지 ID를 작업자에게 할당하려는 작업의 식별자로 설정하고, 작업자는 해당 작업이 이미 수행된 경우 해당 작업 할당이 두 번째로 나오는 경우를 무시합니다.

Important

PeekLock 또는 SessionLock이 메시지에 대해 획득하는 잠금은 휘발성이며 다음 조건에서 손실될 수 있다는 점에 유의해야 합니다.

  • 서비스 업데이트
  • OS 업데이트
  • 잠금을 보유하고 있는 동안 엔터티(큐, 토픽, 구독)의 속성 변경

잠금이 손실되면 Azure Service Bus는 클라이언트 애플리케이션에 표시되는 MessageLockLostException 또는 SessionLockLostException을 생성합니다. 이 경우 클라이언트의 기본 재시도 논리가 자동으로 시작되고 작업을 다시 시도해야 합니다.

잠금 갱신

잠금 기간의 기본값은 1분입니다. 큐 또는 구독 수준에서 잠금 기간에 대해 다른 값을 지정할 수 있습니다. 잠금을 소유하는 클라이언트는 수신자 개체의 메서드를 사용하여 메시지 잠금을 갱신할 수 있습니다. 대신 자동 잠금 갱신 기능을 사용하여 잠금을 계속 갱신할 기간을 지정할 수 있습니다.

잠금 기간을 일반 처리 시간보다 더 높은 값으로 설정하는 것이 가장 좋습니다. 따라서 잠금을 갱신할 필요가 없습니다. 최대값은 5분이므로 잠금을 더 길게 하려면 갱신해야 합니다. 필요한 것보다 더 긴 잠금 기간을 갖는 것도 몇 가지 의미가 있습니다. 예를 들어 클라이언트의 작동이 중지되면 잠금 기간이 경과한 후에만 메시지를 다시 사용할 수 있게 됩니다.

다음 단계