자습서: Azure Portal 및 토픽/구독을 사용하여 재고 업데이트
Azure Service Bus는 애플리케이션과 서비스 간에 정보를 전송하는 다중 테넌트 클라우드 메시징 서비스입니다. 비동기 작업은 구조적 FIFO(선입 선출) 메시징 및 게시/구독 기능과 함께 유연하게 조정할 수 있는 메시징 기능을 제공합니다. Azure Service Bus에 대한 자세한 개요는 Service Bus란?을 참조하세요.
이 자습서에서는 소매점 재고 시나리오에서 Azure Portal 및 .NET을 사용하여 게시/구독 채널을 통해 Service Bus 토픽과 구독을 사용하는 방법을 보여 줍니다. 이 시나리오의 예는 여러 소매점에 대한 재고 분류 업데이트입니다. 이 시나리오에서 각 상점 또는 상점 집합은 해당 분류를 업데이트하기 위한 메시지를 받습니다. 이 자습서에서는 구독과 필터를 사용하여 이 시나리오를 구현하는 방법을 보여 줍니다. 먼저 세 개의 구독이 포함된 토픽을 만들고, 몇 가지 규칙과 필터를 추가한 다음, 토픽과 구독에서 메시지를 보내고 받습니다.
이 자습서에서는 다음을 하는 방법을 알아볼 수 있습니다.
- Azure Portal을 사용하여 Service Bus 토픽 및 해당 토픽에 대한 세 개의 구독 만들기
- .NET 코드를 사용하여 구독에 대한 필터 추가
- 다양한 콘텐츠를 사용하여 메시지 만들기
- 메시지를 보내고 예상 구독에 도착했는지 확인
- 구독에서 메시지 받기
필수 조건
이 자습서를 완료하려면 다음이 설치되어 있어야 합니다.
- Azure 구독. Azure Service Bus를 비롯한 Azure 서비스를 사용하려면 구독이 필요합니다. Azure 구독이 아직 없으면 시작하기 전에 무료 계정을 만들 수 있습니다.
- Visual Studio 2019 이상
Service Bus 토픽 및 구독
토픽에 대한 각 구독은 각 메시지의 복사본을 받을 수 있습니다. 토픽은 완전히 프로토콜이며, 의미상 Service Bus 큐와 호환됩니다. Service Bus 토픽은 메시지 속성을 설정하거나 수정하는 선택적 동작과 함께 필터 조건이 포함된 다양한 선택 규칙을 지원합니다. 규칙이 일치할 때마다 메시지를 생성합니다. 규칙, 필터 및 작업에 대해 자세히 알아보려면 이 링크를 따르세요.
Azure Portal에서 네임스페이스 만들기
Azure에서 Service Bus 메시징 엔터티 사용을 시작하려면 먼저 Azure에서 고유한 이름인 네임스페이스를 만들어야 합니다. 네임스페이스는 애플리케이션 내의 Service Bus 리소스(큐, 토픽 등)에 대한 범위 지정 컨테이너를 제공합니다.
네임스페이스를 만들려면
Azure Portal에 로그인합니다.
전체 서비스 페이지로 이동합니다.
왼쪽 탐색 모음에서 범주 목록에서 통합을 선택하고 마우스를 Service Bus 위로 가져간 다음 Service Bus 타일에서 + 단추를 선택합니다.
네임스페이스 만들기 페이지의 기본 사항 태그에서 다음 단계를 수행합니다.
구독에 대해 네임스페이스를 만들 Azure 구독을 선택합니다.
리소스 그룹의 경우 기존 리소스 그룹을 선택하거나 새 리소스 그룹을 만듭니다.
네임스페이스 이름을 입력합니다. 네임스페이스 이름은 다음 명명 규칙을 따라야 합니다.
- 이름은 Azure에서 고유해야 합니다. 시스템에서 사용 가능한 이름인지 즉시 확인합니다.
- 이름 길이는 6~50자여야 합니다.
- 이름에는 문자, 숫자, 하이픈만 포함될 수 있습니다
-
. - 이름은 문자로 시작하고 문자 또는 숫자로 끝나야 합니다.
- 이름은
-mgmt>로 -sb
끝나지 않습니다.
위치에 대해 네임스페이스가 호스팅되어야하는 지역을 선택합니다.
가격 책정 계층에 대해 네임스페이스에 대한 가격 책정 계층(기본, 표준 또는 프리미엄)을 선택합니다. 이 빠른 시작의 경우 표준을 선택합니다.
프리미엄 계층을 선택하는 경우 네임스페이스에 대해 지역에서 복제를 사용하도록 설정할 수 있는지 여부를 선택합니다. 지역 복제 기능은 네임스페이스의 메타데이터 및 데이터가 주 지역에서 하나 이상의 보조 지역으로 지속적으로 복제되도록 보장합니다.
Important
토픽 및 구독을 사용하려면 표준 또는 프리미엄을 선택합니다. 토픽/구독은 기본 가격 책정 계층에서 지원되지 않습니다.
프리미엄 가격 책정 계층을 선택한 경우 메시징 단위 수를 지정합니다. 프리미엄 계층은 CPU 및 메모리 수준에서 리소스 격리를 제공하므로 각 워크로드가 독립적으로 실행됩니다. 이 리소스 컨테이너를 메시징 단위라고 합니다. 프리미엄 네임스페이스에는 하나 이상의 메시징 단위가 있습니다. 각 Service Bus 프리미엄 네임스페이스에 대해 1, 2, 4, 8 또는 16개의 메시징 단위를 선택할 수 있습니다. 자세한 내용은 Service Bus 프리미엄 메시징을 참조하세요.
페이지 아래쪽에서 검토 + 만들기를 선택합니다.
검토 + 만들기 페이지에서 설정을 검토하고 만들기를 선택합니다.
리소스 배포에 성공하면 배포 페이지에서 리소스로 이동을 선택합니다.
Service Bus 네임스페이스에 대한 홈페이지가 표시됩니다.
네임스페이스에 대한 연결 문자열 가져오기(Azure Portal)
새 네임스페이스를 만들면 기본 키 및 보조 키와 각각 네임스페이스의 모든 측면에 대한 전체 제어 권한을 부여하는 기본 및 보조 연결 문자열이 포함된 초기 SAS(공유 액세스 서명) 정책이 자동으로 생성됩니다. 일반적인 보낸 사람과 받는 사람에 대해 자세히 제한된 권한이 적용된 규칙을 만드는 방법에 대한 자세한 내용은 Service Bus 인증 및 권한 부여를 참조하세요.
클라이언트는 연결 문자열을 사용하여 Service Bus 네임스페이스에 연결할 수 있습니다. 네임스페이스에 대한 기본 연결 문자열을 복사하려면 다음 단계를 수행합니다.
Service Bus 네임스페이스 페이지의 왼쪽 메뉴에서 공유 액세스 정책을 선택합니다.
공유 액세스 정책 페이지에서 RootManageSharedAccessKey를 선택합니다.
정책: RootManageSharedAccessKey 창에서 기본 연결 문자열 옆의 복사 단추를 선택하여 나중에 사용할 수 있도록 연결 문자열을 클립보드에 복사합니다. 메모장이나 기타 다른 위치에 임시로 이 값을 붙여 넣습니다.
이 페이지를 사용하여 기본 키, 보조 키, 기본 연결 문자열 및 보조 연결 문자열을 복사할 수 있습니다.
Azure Portal을 사용하여 항목 만들기
Service Bus 네임스페이스 페이지에서 탐색 메뉴의 엔터티를 왼쪽으로 확장하고 왼쪽 메뉴에서 토픽을 선택합니다.
도구 모음에서 + 항목을 선택합니다.
항목의 이름을 입력합니다. 다른 옵션은 기본값 그대로 둡니다.
만들기를 실행합니다.
항목에 대한 구독 만들기
이전 섹션에서 만든 항목을 선택합니다.
Service Bus 토픽 페이지의 도구 모음에서 + 구독을 선택합니다.
구독 만들기 페이지에서 다음 단계를 수행합니다.
구독 이름에 S1을 입력합니다.
그런 다음, 만들기를 선택하여 구독을 만듭니다.
이전 단계를 두 번 반복하여 S2 및 S3라는 구독을 만듭니다.
구독에 대한 필터 규칙 만들기
네임스페이스와 토픽/구독이 프로비전되고 네임스페이스에 대한 연결 문자열이 있으면 구독에 대한 필터 규칙을 만든 다음, 메시지를 보내고 받을 준비가 된 것입니다. 이 GitHub 샘플 폴더에서 코드를 검사할 수 있습니다.
메시지 보내기 및 받기
코드를 실행하려면 다음 단계를 수행합니다.
명령 프롬프트 또는 PowerShell 프롬프트에서 다음 명령을 실행하여 Service Bus GitHub 리포지토리를 복제합니다.
git clone https://github.com/Azure/azure-service-bus.git
다음 샘플 폴더로 이동합니다.
azure-service-bus\samples\DotNet\Azure.Messaging.ServiceBus\BasicSendReceiveTutorialWithFilters
이 자습서에서 이전에 메모장에 복사한 연결 문자열을 가져옵니다. 또한 이전 섹션에서 만든 토픽의 이름도 필요합니다.
명령 프롬프트에서 다음 명령을 입력합니다.
dotnet build
BasicSendReceiveTutorialWithFilters\bin\Debug\netcoreapp3.1
폴더로 이동합니다.다음 명령을 입력하여 프로그램을 실행합니다.
myConnectionString
을 앞에서 가져온 값으로 바꾸고,myTopicName
을 만든 토픽의 이름으로 바꿔야 합니다.dotnet --roll-forward Major BasicSendReceiveTutorialWithFilters.dll -ConnectionString "myConnectionString" -TopicName "myTopicName"
먼저, 콘솔의 지침에 따라 필터 만들기를 선택합니다. 필터 만들기의 일부로 기본 필터를 제거하는 것이 포함됩니다. PowerShell 또는 CLI를 사용하는 경우 기본 필터를 제거할 필요가 없지만, 코드에서 이 작업을 수행하는 경우 해당 필터를 제거해야 합니다. 콘솔 명령 1과 3은 이전에 만든 구독의 필터를 관리하는 데 도움이 됩니다.
실행 1: 기본 필터를 제거합니다.
실행 2: 사용자 고유의 필터를 추가합니다.
실행 3: 자습서에서는 이 단계를 건너뜁니다. 이 옵션은 필요에 따라 사용자 고유의 필터를 제거합니다. 이 경우 기본 필터를 다시 만들지 않습니다.
필터가 만들어지면 메시지를 보낼 수 있습니다. 4를 누르고 토픽에 보내는 10개 메시지를 관찰합니다.
5를 누르고 받는 메시지를 관찰합니다. 10개의 메시지를 다시 받지 못한 경우 "m"을 눌러 메뉴를 표시한 다음, 5를 다시 누릅니다.
리소스 정리
더 이상 필요하지 않은 경우 다음 단계에 따라 리소스를 정리합니다.
- Azure Portal에서 네임스페이스로 이동합니다.
- Service Bus 네임스페이스 페이지의 명령 모음에서 삭제를 선택하여 네임스페이스와 리소스(큐, 토픽 및 구독)를 삭제합니다.
샘플 코드 이해
이 섹션에는 샘플 코드의 기능에 대한 자세한 내용이 포함되어 있습니다.
연결 문자열 및 토픽 가져오기
먼저, 다음 코드에서는 프로그램의 나머지 실행을 구동하는 변수 집합을 선언합니다.
string ServiceBusConnectionString;
string TopicName;
static string[] Subscriptions = { "S1", "S2", "S3" };
static IDictionary<string, string[]> SubscriptionFilters = new Dictionary<string, string[]> {
{ "S1", new[] { "StoreId IN('Store1', 'Store2', 'Store3')", "StoreId = 'Store4'"} },
{ "S2", new[] { "sys.To IN ('Store5','Store6','Store7') OR StoreId = 'Store8'" } },
{ "S3", new[] { "sys.To NOT IN ('Store1','Store2','Store3','Store4','Store5','Store6','Store7','Store8') OR StoreId NOT IN ('Store1','Store2','Store3','Store4','Store5','Store6','Store7','Store8')" } }
};
// You can have only have one action per rule and this sample code supports only one action for the first filter, which is used to create the first rule.
static IDictionary<string, string> SubscriptionAction = new Dictionary<string, string> {
{ "S1", "" },
{ "S2", "" },
{ "S3", "SET sys.Label = 'SalesEvent'" }
};
static string[] Store = { "Store1", "Store2", "Store3", "Store4", "Store5", "Store6", "Store7", "Store8", "Store9", "Store10" };
static string SysField = "sys.To";
static string CustomField = "StoreId";
static int NrOfMessagesPerStore = 1; // Send at least 1.
연결 문자열 및 토픽 이름은 명령줄 매개 변수를 통해 표시된 대로 전달된 다음, Main()
메서드에서 읽습니다.
static void Main(string[] args)
{
string ServiceBusConnectionString = "";
string TopicName = "";
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "-ConnectionString")
{
Console.WriteLine($"ConnectionString: {args[i + 1]}");
ServiceBusConnectionString = args[i + 1]; // Alternatively enter your connection string here.
}
else if (args[i] == "-TopicName")
{
Console.WriteLine($"TopicName: {args[i + 1]}");
TopicName = args[i + 1]; // Alternatively enter your queue name here.
}
}
if (ServiceBusConnectionString != "" && TopicName != "")
{
Program P = StartProgram(ServiceBusConnectionString, TopicName);
P.PresentMenu().GetAwaiter().GetResult();
}
else
{
Console.WriteLine("Specify -Connectionstring and -TopicName to execute the example.");
Console.ReadKey();
}
}
기본 필터 제거
구독이 만들어지면 Service Bus에서 구독별 기본 필터를 만듭니다. 이 필터를 사용하면 토픽으로 보낸 모든 메시지를 받을 수 있습니다. 사용자 지정 필터를 사용하려는 경우 다음 코드와 같이 기본 필터를 제거할 수 있습니다.
private async Task RemoveDefaultFilters()
{
Console.WriteLine($"Starting to remove default filters.");
try
{
var client = new ServiceBusAdministrationClient(ServiceBusConnectionString);
foreach (var subscription in Subscriptions)
{
await client.DeleteRuleAsync(TopicName, subscription, CreateRuleOptions.DefaultRuleName);
Console.WriteLine($"Default filter for {subscription} has been removed.");
}
Console.WriteLine("All default Rules have been removed.\n");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
await PresentMenu();
}
필터 만들기
다음 코드에서는 이 자습서에서 정의한 사용자 지정 필터를 추가합니다.
private async Task CreateCustomFilters()
{
try
{
for (int i = 0; i < Subscriptions.Length; i++)
{
var client = new ServiceBusAdministrationClient(ServiceBusConnectionString);
string[] filters = SubscriptionFilters[Subscriptions[i]];
if (filters[0] != "")
{
int count = 0;
foreach (var myFilter in filters)
{
count++;
string action = SubscriptionAction[Subscriptions[i]];
if (action != "")
{
await client.CreateRuleAsync(TopicName, Subscriptions[i], new CreateRuleOptions
{
Filter = new SqlRuleFilter(myFilter),
Action = new SqlRuleAction(action),
Name = $"MyRule{count}"
});
}
else
{
await client.CreateRuleAsync(TopicName, Subscriptions[i], new CreateRuleOptions
{
Filter = new SqlRuleFilter(myFilter),
Name = $"MyRule{count}"
});
}
}
}
Console.WriteLine($"Filters and actions for {Subscriptions[i]} have been created.");
}
Console.WriteLine("All filters and actions have been created.\n");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
await PresentMenu();
}
만든 사용자 지정 필터 제거
구독에 대한 모든 필터를 제거하려는 경우 다음 코드에서 이 작업을 수행하는 방법을 보여 줍니다.
private async Task CleanUpCustomFilters()
{
foreach (var subscription in Subscriptions)
{
try
{
var client = new ServiceBusAdministrationClient(ServiceBusConnectionString);
IAsyncEnumerator<RuleProperties> rules = client.GetRulesAsync(TopicName, subscription).GetAsyncEnumerator();
while (await rules.MoveNextAsync())
{
await client.DeleteRuleAsync(TopicName, subscription, rules.Current.Name);
Console.WriteLine($"Rule {rules.Current.Name} has been removed.");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.WriteLine("All default filters have been removed.\n");
await PresentMenu();
}
메시지 보내기
토픽에 메시지를 보내는 것은 큐에 메시지를 보내는 것과 비슷합니다. 다음 예제에서는 작업 목록과 비동기 처리를 사용하여 메시지를 보내는 방법을 보여 줍니다.
public async Task SendMessages()
{
try
{
await using var client = new ServiceBusClient(ServiceBusConnectionString);
var taskList = new List<Task>();
for (int i = 0; i < Store.Length; i++)
{
taskList.Add(SendItems(client, Store[i]));
}
await Task.WhenAll(taskList);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("\nAll messages sent.\n");
}
private async Task SendItems(ServiceBusClient client, string store)
{
// create the sender
ServiceBusSender tc = client.CreateSender(TopicName);
for (int i = 0; i < NrOfMessagesPerStore; i++)
{
Random r = new Random();
Item item = new Item(r.Next(5), r.Next(5), r.Next(5));
// Note the extension class which is serializing an deserializing messages
ServiceBusMessage message = item.AsMessage();
message.To = store;
message.ApplicationProperties.Add("StoreId", store);
message.ApplicationProperties.Add("Price", item.GetPrice().ToString());
message.ApplicationProperties.Add("Color", item.GetColor());
message.ApplicationProperties.Add("Category", item.GetItemCategory());
await tc.SendMessageAsync(message);
Console.WriteLine($"Sent item to Store {store}. Price={item.GetPrice()}, Color={item.GetColor()}, Category={item.GetItemCategory()}"); ;
}
}
메시지 받기
메시지는 작업 목록을 통해 다시 받고, 코드에서 일괄 처리를 사용합니다. 일괄 처리를 사용하여 보내고 받을 수 있지만, 다음 예제에서는 일괄 처리 방식으로 받는 방법만 보여 줍니다. 실제로는 루프에서 벗어나지 않고 계속 반복하고, 더 긴 시간 범위(예: 1분)를 설정합니다. broker에 대한 수신 호출은 이 시간 동안 열려 있고, 메시지가 도착하면 즉시 반환되며, 새 수신 호출이 실행됩니다. 이 개념을 긴 폴링이라고 합니다. 빠른 시작 및 리포지토리의 여러 다른 샘플에서 볼 수 있는 수신 펌프를 사용하는 것이 더 일반적인 옵션입니다.
public async Task Receive()
{
var taskList = new List<Task>();
for (var i = 0; i < Subscriptions.Length; i++)
{
taskList.Add(this.ReceiveMessages(Subscriptions[i]));
}
await Task.WhenAll(taskList);
}
private async Task ReceiveMessages(string subscription)
{
await using var client = new ServiceBusClient(ServiceBusConnectionString);
ServiceBusReceiver receiver = client.CreateReceiver(TopicName, subscription);
// In reality you would not break out of the loop like in this example but would keep looping. The receiver keeps the connection open
// to the broker for the specified amount of seconds and the broker returns messages as soon as they arrive. The client then initiates
// a new connection. So in reality you would not want to break out of the loop.
// Also note that the code shows how to batch receive, which you would do for performance reasons. For convenience you can also always
// use the regular receive pump which we show in our Quick Start and in other GitHub samples.
while (true)
{
try
{
//IList<Message> messages = await receiver.ReceiveAsync(10, TimeSpan.FromSeconds(2));
// Note the extension class which is serializing an deserializing messages and testing messages is null or 0.
// If you think you did not receive all messages, just press M and receive again via the menu.
IReadOnlyList<ServiceBusReceivedMessage> messages = await receiver.ReceiveMessagesAsync(maxMessages: 100);
if (messages.Any())
{
foreach (ServiceBusReceivedMessage message in messages)
{
lock (Console.Out)
{
Item item = message.As<Item>();
IReadOnlyDictionary<string, object> myApplicationProperties = message.ApplicationProperties;
Console.WriteLine($"StoreId={myApplicationProperties["StoreId"]}");
if (message.Subject != null)
{
Console.WriteLine($"Subject={message.Subject}");
}
Console.WriteLine(
$"Item data: Price={item.GetPrice()}, Color={item.GetColor()}, Category={item.GetItemCategory()}");
}
await receiver.CompleteMessageAsync(message);
}
}
else
{
break;
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
참고 항목
Service Bus Explorer로 Service Bus 리소스를 관리할 수 있습니다. Service Bus Explorer를 사용하면 Service Bus 네임스페이스에 연결하고 쉬운 방식으로 메시징 엔터티를 관리할 수 있습니다. 이 도구는 가져오기/내보내기 기능 또는 항목, 큐, 구독, 릴레이 서비스, Notification Hubs 및 이벤트 허브를 테스트하는 기능과 같은 고급 기능을 제공합니다.
다음 단계
이 자습서에서는 Azure Portal을 사용하여 리소스를 프로비전한 다음, Service Bus 토픽 및 해당 구독에서 메시지를 보내고 받았습니다. 다음 방법에 대해 알아보았습니다.
- Azure Portal을 사용하여 Service Bus 토픽 및 해당 토픽에 대한 하나 이상의 구독 만들기
- .NET 코드를 사용하여 토픽 필터 추가
- 내용이 서로 다른 두 개의 메시지 만들기
- 메시지 보내기 및 필요한 구독에 도착했는지 확인
- 구독에서 메시지 받기
메시지 송수신에 대한 더 많은 예제는 GitHub의 Service Bus 샘플에서 시작하세요.
Service Bus의 게시/구독 기능을 사용하는 방법에 대해 자세히 알아보려면 다음 자습서로 계속 진행하세요.