Transferowanie, blokady i uzgadnianie komunikatów

Centralną funkcją brokera komunikatów, takiego jak usługa Service Bus, jest akceptowanie komunikatów w kolejce lub temacie i przechowywanie ich dostępnych do późniejszego pobierania. Send to termin, który jest często używany do transferu komunikatu do brokera komunikatów. Receive to termin często używany do transferu komunikatu do klienta pobierania.

Gdy klient wysyła komunikat, zwykle chce wiedzieć, czy komunikat jest prawidłowo przesyłany i akceptowany przez brokera, czy jakiś błąd wystąpił. To pozytywne lub negatywne potwierdzenie rozstrzyga zrozumienie zarówno klienta, jak i brokera o stanie transferu komunikatu. W związku z tym jest on określany jako osada.

Podobnie, gdy broker przesyła komunikat do klienta, broker i klient chcą ustalić, czy komunikat został pomyślnie przetworzony i w związku z tym może zostać usunięty, lub czy dostarczanie komunikatu lub przetwarzanie nie powiodło się, a tym samym komunikat może zostać dostarczony ponownie.

Rozliczanie operacji wysyłania

Korzystając z dowolnego z obsługiwanych klientów interfejsu API usługi Service Bus, operacje wysyłania do usługi Service Bus są zawsze jawnie rozliczane, co oznacza, że operacja interfejsu API czeka na wynik akceptacji z usługi Service Bus, a następnie kończy operację wysyłania.

Jeśli komunikat zostanie odrzucony przez usługę Service Bus, odrzucenie zawiera wskaźnik błędu i tekst z identyfikatorem śledzenia w nim. Odrzucenie zawiera również informacje o tym, czy można ponowić operację z oczekiwaniem na powodzenie. W kliencie te informacje są przekształcane w wyjątek i zgłaszane do obiektu wywołującego operacji wysyłania. Jeśli komunikat zostanie zaakceptowany, operacja zostanie ukończona w trybie dyskretnym.

Advanced Messaging Queuing Protocol (AMQP) to jedyny protokół obsługiwany przez klientów .NET Standard, Java, JavaScript, Python i Go. W przypadku klientów programu .NET Framework można użyć protokołu SBMP (Service Bus Messaging Protocol) lub AMQP. W przypadku korzystania z protokołu AMQP transfery komunikatów i rozliczenia są potokowe i asynchroniczne. Zalecamy używanie wariantów interfejsu API modelu programowania asynchronicznego.

30 września 2026 r. wycofamy biblioteki zestawu SDK usługi Azure Service Bus WindowsAzure.ServiceBus, Microsoft.Azure.ServiceBus i com.microsoft.azure.servicebus, które nie są zgodne z wytycznymi dotyczącymi zestawu Azure SDK. Zakończymy również obsługę protokołu SBMP, więc nie będzie można już używać tego protokołu po 30 września 2026 r. Przeprowadź migrację do najnowszych bibliotek zestawu Azure SDK, które oferują krytyczne aktualizacje zabezpieczeń i ulepszone możliwości przed tą datą.

Mimo że starsze biblioteki mogą być nadal używane poza 30 września 2026 r., nie będą już otrzymywać oficjalnej pomocy technicznej i aktualizacji od firmy Microsoft. Aby uzyskać więcej informacji, zobacz ogłoszenie o wycofaniu pomocy technicznej.

Nadawca może umieścić kilka komunikatów w sieci w krótkim odstępie czasu bez konieczności oczekiwania na potwierdzenie każdego komunikatu, tak jak w przeciwnym razie w przypadku protokołu SBMP lub HTTP 1.1. Te asynchroniczne operacje wysyłania są wykonywane, ponieważ odpowiednie komunikaty są akceptowane i przechowywane w jednostkach podzielonych na partycje lub podczas wysyłania operacji do różnych jednostek nakładają się. Ukończenia mogą również wystąpić z oryginalnego zamówienia wysyłania.

Strategia obsługi wyników operacji wysyłania może mieć natychmiastowy i znaczący wpływ na wydajność aplikacji. Przykłady w tej sekcji są napisane w języku C# i dotyczą kontraktów terminowych Java, mono języka Java, obietnic języka JavaScript i równoważnych pojęć w innych językach.

Jeśli aplikacja generuje serie komunikatów, zilustrowane tutaj z zwykłą pętlą i miały czekać na zakończenie każdej operacji wysyłania przed wysłaniem następnego komunikatu, synchronicznych lub asynchronicznych kształtów interfejsu API, wysyłając 10 komunikatów tylko po 10 sekwencyjnych pełnych rundach dla rozliczenia.

Przy założeniu 70-milisekundowego opóźnienia komunikacji dwukierunkowej protokołu TCP (Transmission Control Protocol) z lokacji lokalnej do usługi Service Bus i dając zaledwie 10 ms dla usługi Service Bus do akceptowania i przechowywania każdego komunikatu, następująca pętla zajmuje co najmniej 8 sekund, nie licząc czasu transferu ładunku lub potencjalne skutki przeciążenia trasy:

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

Jeśli aplikacja uruchamia 10 asynchronicznych operacji wysyłania w najbliższym czasie i oczekuje na ich zakończenie oddzielnie, czas rundy dla tych 10 operacji wysyłania nakłada się. 10 komunikatów jest przesyłanych bezpośrednio z rzędu, potencjalnie nawet współużytkowanie ramek TCP, a ogólny czas trwania transferu w dużej mierze zależy od czasu związanego z siecią, który zajmuje pobranie komunikatów przesyłanych do brokera.

W przypadku tych samych założeń co w przypadku poprzedniej pętli łączny nakładany czas wykonywania dla następującej pętli może pozostawać na drugim miejscu:

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

Należy pamiętać, że wszystkie modele programowania asynchronicznego używają jakiejś formy ukrytej kolejki roboczej opartej na pamięci, która przechowuje oczekujące operacje. Po powrocie interfejsu API wysyłania zadanie wysyłania jest kolejkowane w tej kolejce roboczej, ale gest protokołu rozpoczyna się tylko po uruchomieniu zadania podrzędnego. W przypadku kodu, który ma tendencję do wypychania wybuchów komunikatów i gdy niezawodność jest problemem, należy zadbać, aby nie zbyt wiele komunikatów było umieszczanych "w locie", ponieważ wszystkie wysyłane komunikaty zajmują pamięć, dopóki nie zostaną umieszczone w przewodzie.

Semaphores, jak pokazano w poniższym fragmencie kodu w języku C#, to obiekty synchronizacji, które umożliwiają takie ograniczanie na poziomie aplikacji w razie potrzeby. Takie użycie semafora umożliwia co najwyżej 10 komunikatów w locie jednocześnie. Jeden z 10 dostępnych blokad semafora jest wykonywany przed wysłaniem i jest zwalniany w miarę zakończenia wysyłania. 11. przejście przez pętlę czeka na ukończenie co najmniej jednej z poprzednich operacji wysyłania, a następnie udostępnia blokadę:

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);

Aplikacje nigdy nie powinny inicjować asynchronicznej operacji wysyłania w sposób "fire and forget" bez pobierania wyniku operacji. Dzięki temu może załadować wewnętrzną i niewidoczną kolejkę zadań do wyczerpania pamięci i zapobiec wykrywaniu błędów wysyłania przez aplikację:

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

W przypadku klienta protokołu AMQP niskiego poziomu usługa Service Bus akceptuje również transfery "presettled". Wstępnie ustawiony transfer jest operacją fire-and-forget, dla której wynik, w obu przypadkach, nie jest zgłaszany z powrotem do klienta, a komunikat jest uznawany za rozstrzygnięty po wysłaniu. Brak opinii do klienta oznacza również, że nie ma dostępnych danych z możliwością działania dla diagnostyki, co oznacza, że ten tryb nie kwalifikuje się do pomocy za pośrednictwem pomoc techniczna platformy Azure.

Rozliczanie operacji odbierania

W przypadku operacji odbierania klienci interfejsu API usługi Service Bus włączają dwa różne tryby jawne: Receive-and-Delete i Peek-Lock.

ReceiveAndDelete

Tryb odbierania i usuwania informuje brokera o rozważeniu wszystkich komunikatów wysyłanych do klienta odbierającego zgodnie z rozliczeniem po wysłaniu. Oznacza to, że komunikat jest uznawany za wykorzystany, gdy tylko broker umieści go w przewodzie. Jeśli transfer komunikatu zakończy się niepowodzeniem, komunikat zostanie utracony.

Plusem tego trybu jest to, że odbiornik nie musi podejmować dalszych działań na komunikat i nie jest również spowolniony, czekając na wynik rozliczenia. Jeśli dane zawarte w poszczególnych komunikatach mają niską wartość i/lub mają znaczenie tylko przez bardzo krótki czas, ten tryb jest rozsądnym wyborem.

PeekLock

Tryb Peek-Lock informuje brokera, że klient odbierający chce jawnie rozstrzygnąć odebrane komunikaty. Komunikat jest udostępniany do przetworzenia przez odbiorcę, w trybie wyłącznym w usłudze, tak aby inni, konkurujący odbiorcy nie mogli go zobaczyć. Czas trwania blokady jest początkowo definiowany na poziomie kolejki lub subskrypcji i może zostać przedłużony przez klienta, który jest właścicielem blokady, za pośrednictwem operacji RenewMessageLockAsync . Aby uzyskać szczegółowe informacje na temat odnawiania blokad, zobacz sekcję Odnawianie blokad w tym artykule.

Gdy komunikat jest zablokowany, inni klienci odbierający z tej samej kolejki lub subskrypcji mogą podjąć blokady i pobrać następne dostępne komunikaty, które nie są objęte aktywną blokadą. Gdy blokada komunikatu zostanie jawnie zwolniona lub gdy blokada wygaśnie, komunikat zostanie umieszczony w lub w pobliżu przodu kolejności pobierania dla ponownego dostarczenia.

Gdy komunikat jest wielokrotnie zwalniany przez odbiorniki lub pozwala na upłynięcie blokady przez zdefiniowaną liczbę razy (Maksymalna liczba dostarczania), komunikat zostanie automatycznie usunięty z kolejki lub subskrypcji i umieszczony w skojarzonej kolejce utraconych komunikatów.

Klient odbierający inicjuje rozliczenie odebranego komunikatu z pozytywnym potwierdzeniem, gdy wywołuje on kompletny interfejs API komunikatu. Wskazuje ona brokerowi, że komunikat został pomyślnie przetworzony, a komunikat zostanie usunięty z kolejki lub subskrypcji. Broker odpowiada na intencję rozliczenia odbiorcy z odpowiedzią wskazującą, czy można wykonać rozliczenie.

Gdy klient odbierający nie może przetworzyć komunikatu, ale chce, aby komunikat został ponownie dostarczony, może jawnie poprosić o zwolnienie i odblokowanie komunikatu natychmiast przez wywołanie interfejsu API porzucenia komunikatu lub nie może nic zrobić i pozwolić na upłynięcie blokady.

Jeśli klient odbierający nie może przetworzyć komunikatu i wie, że ponowne pobieranie komunikatu i ponawianie próby operacji nie pomoże, może odrzucić komunikat, który przenosi go do kolejki utraconych komunikatów, wywołując interfejs API DeadLetter w komunikacie, co umożliwia również ustawienie właściwości niestandardowej, w tym kod przyczyny, który można pobrać z komunikatu z kolejki utraconych komunikatów.

Uwaga

Kolejka nieaktywna istnieje dla kolejki lub subskrypcji tematu tylko wtedy, gdy dla kolejki lub subskrypcji włączono funkcję utraconych komunikatów.

Szczególny przypadek rozliczenia to odroczenie, które zostało omówione w osobnym artykule.

Operacje Complete, DeadLetterlub RenewLock mogą zakończyć się niepowodzeniem z powodu problemów z siecią, jeśli blokada blokady wygasła lub istnieją inne warunki po stronie usługi, które uniemożliwiają ugodę. W jednym z ostatnich przypadków usługa wysyła negatywne potwierdzenie, które pojawia się jako wyjątek w klientach interfejsu API. Jeśli przyczyną jest przerwane połączenie sieciowe, blokada zostanie porzucona, ponieważ usługa Service Bus nie obsługuje odzyskiwania istniejących łączy protokołu AMQP na innym połączeniu.

W przypadku Complete awarii, co zwykle występuje na samym końcu obsługi komunikatów, a w niektórych przypadkach po minutach pracy przetwarzania aplikacja odbierającego może zdecydować, czy zachować stan pracy i zignorować ten sam komunikat po zakończeniu po raz drugi, lub czy wysyła wynik pracy i ponawia próbę, ponieważ komunikat zostanie ponownie dostarczony.

Typowy mechanizm identyfikacji zduplikowanych dostaw komunikatów polega na sprawdzeniu identyfikatora komunikatu, który może i powinien zostać ustawiony przez nadawcę na unikatową wartość, prawdopodobnie wyrównaną z identyfikatorem z procesu źródłowego. Harmonogram zadań prawdopodobnie ustawi identyfikator komunikatu na identyfikator zadania, które próbuje przypisać do procesu roboczego z danym procesem roboczym, a proces roboczy zignoruje drugie wystąpienie przypisania zadania, jeśli to zadanie zostało już wykonane.

Ważne

Należy pamiętać, że blokada, którą uzyskuje PeekLock lub SessionLock w komunikacie, jest niestabilna i może zostać utracona w następujących warunkach

  • Aktualizacja usługi
  • Aktualizacja systemu operacyjnego
  • Zmiana właściwości jednostki (kolejka, temat, subskrypcja) podczas przechowywania blokady.

Po utracie blokady usługa Azure Service Bus wygeneruje wyjątek MessageLockLostException lub SessionLockLostException, który pojawi się w aplikacji klienckiej. W takim przypadku domyślna logika ponawiania klienta powinna automatycznie uruchomić i ponowić operację.

Odnawianie blokad

Wartość domyślna czasu trwania blokady to 1 minuta. Możesz określić inną wartość czasu trwania blokady na poziomie kolejki lub subskrypcji. Klient, który jest właścicielem blokady, może odnowić blokadę komunikatu przy użyciu metod w obiekcie odbiorcy. Zamiast tego możesz użyć funkcji automatycznego odnawiania blokady, w której można określić czas trwania, przez który chcesz nadal odnawiać blokadę.

Najlepiej ustawić czas trwania blokady na wartość wyższą niż normalny czas przetwarzania, aby nie trzeba było odnawiać blokady. Maksymalna wartość to 5 minut, więc musisz odnowić blokadę, jeśli chcesz mieć ją dłużej. Posiadanie dłuższego czasu trwania blokady, niż jest to konieczne, ma również pewne konsekwencje. Na przykład gdy klient przestanie działać, komunikat będzie ponownie dostępny tylko po upływie czasu trwania blokady.

Następne kroki