Udostępnij przez


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. Termin wysyłania służy do opisywania procesu przesyłania komunikatu do brokera komunikatów, podczas gdy odbieranie odnosi się do procesu pobierania komunikatu z brokera.

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 ustala wspólne zrozumienie zarówno klienta, jak i brokera dotyczące stanu przesyłania wiadomości. 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 oraz tekst z identyfikatorem śledzenia tracking-id. 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 wywołującego operację 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, gdy odpowiednie komunikaty są akceptowane i przechowywane na podzielonych na partycje jednostkach lub gdy operacje wysyłania do różnych jednostek się nakładają. Ukończenia mogą również wystąpić poza oryginalną kolejnością 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 prostą pętlą, i miała czekać na zakończenie każdej operacji wysyłania przed wysłaniem następnego komunikatu, czy to w synchronicznej, czy asynchronicznej formie API, wysłanie 10 komunikatów zostanie zakończone dopiero po 10 sekwencyjnych pełnych rundach rozliczenia.

Przy założeniu 70-milisekundowego opóźnienia komunikacji dwukierunkowej protokołu TCP (Transmission Control Protocol) z lokalizacji na miejscu do Service Bus i przyznając tylko 10 ms na przyjęcie i zapisanie przez Service Bus każdego komunikatu, następująca pętla zajmuje co najmniej 8 sekund, nie licząc czasu transferu danych ani potencjalnych skutków 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 bezpośrednio po sobie i oczekuje na ich zakończenie oddzielnie, czas podróży w obie strony dla tych 10 operacji wysyłania nakłada się. 10 komunikatów jest przesyłanych natychmiastowo w ciągu, potencjalnie nawet korzystając z tych samych ramek TCP, a ogólny czas trwania transferu w dużej mierze zależy głównie od czasu związanego z siecią potrzebnego na przesłanie komunikatów do brokera.

W przypadku tych samych założeń co w przypadku poprzedniej pętli, całkowity czas nakładania wykonywania dla następującej pętli może być znacznie krótszy niż jedna sekunda.

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. Gdy interfejs API wysyłania zwróci odpowiedź, zadanie wysyłania jest umieszczane w kolejce roboczej, ale procedura protokołu rozpoczyna się dopiero, gdy nadejdzie moment, aby to zadanie się wykonało. 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.

Semafory, jak pokazano w poniższym fragmencie kodu w języku C#, to obiekty synchronizacji, które umożliwiają ograniczanie na poziomie aplikacji, gdy jest to konieczne. Takie użycie semafora umożliwia co najwyżej 10 komunikatów jednocześnie w locie. Jeden z 10 dostępnych blokad semafora jest zajmowany przed wysłaniem i jest zwalniany po zakończeniu wysyłania. Jedenaste przejście przez pętlę czeka, aż zakończy się co najmniej jedna 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". Transfer z góry zaakceptowany to operacja typu „wyślij i zapomnij”, której wynik nie jest zgłaszany z powrotem do klienta, a komunikat jest uznawany za rozstrzygnięty po wysłaniu. Brak informacji zwrotnej dla klienta oznacza również, że nie ma dostępnych danych użytecznych w diagnostyce, co oznacza, że ten tryb nie kwalifikuje się do pomocy za pośrednictwem pomocy technicznej 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.

Odbierz i Usuń

Tryb odbierania i usuwania instruuje brokera, aby uznawał wszystkie wiadomości wysyłane do klienta odbierającego za zakończone w chwili wysłania. 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 odbiorca nie musi podejmować dalszych działań na wiadomość i również nie jest opóźniony, 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ę, jednocześnie trzymany pod wyłączną blokadą w usłudze, tak aby inni odbiorcy konkurujący 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ą przejąć 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 odbiorniki wielokrotnie zwalniają wiadomość lub pozwalają na wygaśnięcie blokady zdefiniowaną liczbę razy (Maksymalna liczba dostarczeń), wiadomość jest automatycznie usuwana z kolejki lub subskrypcji i umieszczana w powiązanej kolejce nieodebranych wiadomości.

Klient odbierający inicjuje rozliczenie odebranego komunikatu z pozytywnym potwierdzeniem, gdy wywołuje interfejs API Complete dla 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

Dla kolejki lub subskrypcji tematu istnieje kolejka dead-letter tylko wtedy, gdy włączono funkcję dead-letter dla tej kolejki lub subskrypcji.

Szczególny przypadek rozliczenia jest odroczenie. Aby uzyskać szczegółowe informacje, zobacz odroczenie komunikatu.

Operacje Complete, DeadLetter lub RenewLock mogą zakończyć się niepowodzeniem z powodu problemów z siecią, jeśli trzymana blokada wygasła lub istnieją inne warunki po stronie usługi, które uniemożliwiają rozliczenie. 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 awarii Complete, co zwykle występuje na samym końcu obsługi komunikatów, a w niektórych przypadkach po minutach pracy przetwarzania, aplikacja odbierająca może zdecydować, czy zachować stan pracy i zignorować ten sam komunikat, gdy zostanie on dostarczony po raz drugi, lub czy odrzucić wynik pracy i ponowić próbę, gdy komunikat zostanie ponownie dostarczony.

Typowy mechanizm identyfikowania zduplikowanych przesłań komunikatów polega na sprawdzeniu wartości message-id, która może i powinna zostać ustawiona przez nadawcę na unikatową wartość, prawdopodobnie zgodną z identyfikatorem procesu źródłowego. Harmonogram zadań prawdopodobnie ustawi message-id na identyfikator zadania, które próbuje przypisać do pracownika, a pracownik zignoruje drugie wystąpienie przypisania zadania, jeśli to zadanie zostało już wykonane.

W tym miejscu projektowanie pod kątem obsługi komunikatów idempotentnych staje się krytyczne. Aby zapoznać się z praktycznymi technikami i przykładami osiągnięcia idempotencji w systemach rozproszonych, zobacz ten przewodnik: Co to jest średnia idempotentna?.

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 trzymania blokady.
  • Jeśli aplikacja kliencka usługi Service Bus utraci połączenie z usługą Service Bus z jakiegokolwiek powodu.
  • W przypadku korzystania z sesji, jeśli SessionIdleTimeout jest krótszy niż czas trwania blokady komunikatu i żadna operacja nie jest wykonywana na komunikacie w okresie SessionIdleTimeout, sesja wygaśnie, a blokada komunikatu zostanie utracona.

Po utracie blokady usługa Azure Service Bus generuje wyjątek MessageLockLostException lub SessionLockLostException, który jest wyświetlany w aplikacji klienckiej. W takim przypadku domyślna logika ponawiania klienta powinna automatycznie uruchomić i ponowić operację. Ponadto licznik dostarczeń wiadomości nie jest zwiększany.

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, korzystając z metod na 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