Przejdź do treści

Emulator Azure Service Bus

  • przez

Jeśli używasz Azure Service Bus to być może zaciekawi Cię fakt, że powstał emulator, który pozwoli Ci testować aplikację lokalnie.

Jest kilka sposób na zainstalowanie emultora. Szczegóły znajdziesz tutaj. Ja użyję dockera. Zaczynam od utworzenia pliku docker-compose.yml:

name: microsoft-azure-servicebus-emulator
services:
    emulator:
        container_name: "servicebus-emulator"
        image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
        volumes:
            - "./config.json:/ServiceBus_Emulator/ConfigFiles/Config.json"
        ports:
            - "5672:5672"
        environment:
            SQL_SERVER: sqledge
            MSSQL_SA_PASSWORD: pa$$W0rd1234
            ACCEPT_EULA: Y
        depends_on:
            - sqledge
        networks:
            sb-emulator:
                aliases:
                    - "sb-emulator"
    sqledge:
        container_name: "sqledge"
        image: "mcr.microsoft.com/azure-sql-edge:latest"
        networks:
            sb-emulator:
                aliases:
                    - "sqledge"
        environment:
            ACCEPT_EULA: Y
            MSSQL_SA_PASSWORD: pa$$W0rd1234
networks:
    sb-emulator:

Teraz w pliku config.json zdefinuję testowy topic oraz subskrypcję. Dodam też filtr dla subscrypcji, który filtruje wiadomości (tylko te gdzie type równa się default)

{
    "UserConfig": {
        "Namespaces": [
            {
                "Name": "sbemulatorns",
                "Topics": [
                    {
                        "Name": "topic.1",
                        "Properties": {
                            "DefaultMessageTimeToLive": "PT1H",
                            "DuplicateDetectionHistoryTimeWindow": "PT20S",
                            "RequiresDuplicateDetection": false
                        },
                        "Subscriptions": [
                            {
                                "Name": "subscription.1",
                                "Properties": {
                                    "DeadLetteringOnMessageExpiration": false,
                                    "DefaultMessageTimeToLive": "PT1H",
                                    "LockDuration": "PT1M",
                                    "MaxDeliveryCount": 10,
                                    "ForwardDeadLetteredMessagesTo": "",
                                    "ForwardTo": "",
                                    "RequiresSession": false
                                },
                                "Rules": [
                                    {
                                        "Name": "user-prop-filter-1",
                                        "Properties": {
                                            "FilterType": "Correlation",
                                            "CorrelationFilter": {
                                                "Properties": {
                                                    "type": "default"
                                                }
                                            }
                                        }
                                    }
                                ]
                            }
                        ]
                    }
                ]
            }
        ],
        "Logging": {
            "Type": "File"
        }
    }
}

Teraz uruchamiamy docker compose:

docker-compose up

Powinieneś zobaczyć coś podobnego do tego:

Jeśli wszystko zakończy się pomyślnie to w docker desktop zobaczysz to co w czerwonym prostokącie:

Przykładowa aplikacja, która publikuje i subskrybuje 10 wiadomości wygląda jak poniżej. Musimy dodać też bibliotekę Azure.Messaging.ServiceBus

using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.Json;
using Azure.Messaging.ServiceBus;

var connectionString = $"Endpoint=sb://{GetLocalIPAddress()}:5672;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
var topicName = "topic.1";
var subscriptionName = "subscription.1";

// publish example messages to topic.1 and subscription.1 (type is default)

await using (var client = new ServiceBusClient(connectionString))
{
    ServiceBusSender sender = client.CreateSender(topicName);

    for (int i = 1; i <= 10; i++)
    {
        var dto = new MessageDto { Id = Guid.NewGuid(), Message = $"Message {i}" };
        ServiceBusMessage message = new ServiceBusMessage(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(dto)));
        message.ApplicationProperties.Add("type", "default");

        await sender.SendMessageAsync(message);
    }
}

// subscribe message from topic.1 and subscription.1

await using (var client = new ServiceBusClient(connectionString))
{
    var opt1 = new ServiceBusProcessorOptions();
    opt1.ReceiveMode = ServiceBusReceiveMode.PeekLock;
    var processor1 = client.CreateProcessor(topicName, subscriptionName, opt1);

    processor1.ProcessMessageAsync += MessageHandler;
    processor1.ProcessErrorAsync += ErrorHandler;

    await processor1.StartProcessingAsync();

    await Task.Delay(TimeSpan.FromSeconds(5));

    await processor1.StopProcessingAsync();
    await processor1.DisposeAsync();
    await client.DisposeAsync();
}

async Task MessageHandler(ProcessMessageEventArgs args)
{
    string body = args.Message.Body.ToString();
    Console.WriteLine($"Received message: SequenceNumber:{args.Message.SequenceNumber} Body:{body}");
    await args.CompleteMessageAsync(args.Message);
}

static Task ErrorHandler(ProcessErrorEventArgs args)
{
    Console.WriteLine($"Message handler encountered an exception {args.Exception}.");
    return Task.CompletedTask;
}

string GetLocalIPAddress()
{
    var host = Dns.GetHostEntry(Dns.GetHostName());
    foreach (var ip in host.AddressList)
    {
        if (ip.AddressFamily == AddressFamily.InterNetwork)
        {
            return ip.ToString();
        }
    }
    throw new Exception("No network adapters with an IPv4 address in the system!");
}

Po uruchomieniu kody powinieneś zobaczyć coś podobnego do tego:

Received message: SequenceNumber:31 Body:{"Id":"580d30fb-7816-4cff-b7fe-a0e454933407","Message":"Message 1"}
Received message: SequenceNumber:32 Body:{"Id":"becac0d0-4f20-4734-8278-4e03b21d011d","Message":"Message 2"}
Received message: SequenceNumber:33 Body:{"Id":"9f12abbf-67c4-467b-9551-1cb0c2d021be","Message":"Message 3"}
Received message: SequenceNumber:34 Body:{"Id":"9af6dc36-6562-40f5-a397-189b94108092","Message":"Message 4"}
Received message: SequenceNumber:35 Body:{"Id":"b012b0d8-c49a-4f92-97d6-99b4ecda4006","Message":"Message 5"}
Received message: SequenceNumber:36 Body:{"Id":"2b60114d-946e-43b3-85c3-87c031ed6083","Message":"Message 6"}
Received message: SequenceNumber:37 Body:{"Id":"c5d73caf-4d52-431c-9773-f7363e4defe1","Message":"Message 7"}
Received message: SequenceNumber:38 Body:{"Id":"dbf4a412-a679-4d6f-99ac-3e5f9aa60d6f","Message":"Message 8"}
Received message: SequenceNumber:39 Body:{"Id":"6fa37a4a-b0ff-43ad-98a9-4991dc5a2589","Message":"Message 9"}
Received message: SequenceNumber:40 Body:{"Id":"04375976-bfe6-45f9-b7e0-7c3655f0accf","Message":"Message 10"}

Moim zdaniem emulator jest pomocny. Nie musimy tworzyć testowych queue lub topic. Możemy testować filtry. Więcej przykładów znajdziesz tutaj