W tym artykule opisano sposób implementowania wzorca nowoczesnej aplikacji internetowej. Wzorzec nowoczesnej aplikacji internetowej definiuje sposób modernizacji aplikacji internetowych w chmurze i wprowadzenia architektury zorientowanej na usługę. Wzorzec zawiera normatywne wskazówki dotyczące architektury, kodu i konfiguracji, które są zgodne z zasadami Azure Well-Architected Framework. Ten wzorzec jest oparty na wzorcu niezawodnej aplikacji internetowej .
Dlaczego warto używać wzorca nowoczesnej aplikacji internetowej?
Wzorzec nowoczesnej aplikacji internetowej ułatwia optymalizowanie obszarów wysokiego zapotrzebowania aplikacji internetowej. Zawiera szczegółowe wskazówki dotyczące oddzielenia tych obszarów w celu umożliwienia niezależnego skalowania na potrzeby optymalizacji kosztów. Takie podejście umożliwia przydzielanie dedykowanych zasobów do krytycznych składników, co zwiększa ogólną wydajność. Oddzielenie usług separowalnych może zwiększyć niezawodność, zapobiegając spowolnieniu w jednej części aplikacji, aby wpływać na inne. Umożliwia również niezależne przechowywanie wersji poszczególnych składników aplikacji.
Jak zaimplementować wzorzec nowoczesnej aplikacji internetowej
Ten artykuł zawiera wskazówki dotyczące implementowania wzorca nowoczesnej aplikacji internetowej. Skorzystaj z poniższych linków, aby przejść do konkretnych potrzebnych wskazówek:
- wskazówki dotyczące architektury . Dowiedz się, jak modularyzować składniki aplikacji internetowej i wybierać odpowiednie rozwiązania platformy jako usługi (PaaS).
- wskazówki dotyczące kodu. Zaimplementuj cztery wzorce projektowe, aby zoptymalizować składniki rozdzielone: Rysowanie stranglera, Queue-Based bilansowanie obciążenia, konkurujący odbiorcy i monitorowanie punktu końcowego kondycji.
- wskazówki dotyczące konfiguracji . Konfigurowanie uwierzytelniania, autoryzacji, skalowania automatycznego i konteneryzacji dla składników rozdzielonych.
Napiwek
Istnieje implementacja referencyjna (przykładowa aplikacja) wzorca nowoczesnej aplikacji internetowej. Reprezentuje stan końcowy implementacji nowoczesnej aplikacji internetowej. Jest to aplikacja internetowa klasy produkcyjnej, która zawiera wszystkie aktualizacje kodu, architektury i konfiguracji omówione w tym artykule. Wdrażanie i używanie implementacji referencyjnej w celu kierowania implementacją wzorca nowoczesnej aplikacji internetowej.
Wskazówki dotyczące architektury
Wzorzec nowoczesnej aplikacji internetowej opiera się na wzorcu niezawodnej aplikacji internetowej. Wymaga to kilku dodatkowych składników architektury. Potrzebujesz kolejki komunikatów, platformy kontenerów, usługi magazynu i rejestru kontenerów, jak pokazano na poniższym diagramie:
Aby uzyskać wyższy cel poziomu usług (SLO), możesz dodać drugi region do architektury aplikacji internetowej. Skonfiguruj moduł równoważenia obciążenia tak, aby kierował ruch do drugiego regionu w celu obsługi konfiguracji aktywne-aktywne lub aktywne-pasywne w zależności od potrzeb biznesowych. Oba regiony wymagają tych samych usług, z wyjątkiem jednego regionu, ma sieć wirtualną koncentratora. Topologia sieci piasty i szprych umożliwia scentralizowanie i udostępnianie zasobów, takich jak zapora sieciowa. Uzyskaj dostęp do repozytorium kontenerów za pośrednictwem sieci wirtualnej koncentratora. Jeśli masz maszyny wirtualne, dodaj hosta bastionu do sieci wirtualnej koncentratora, aby zarządzać nimi przy użyciu zwiększonych zabezpieczeń. Na poniższym diagramie przedstawiono tę architekturę:
Oddzielenie architektury
Aby zaimplementować wzorzec nowoczesnej aplikacji internetowej, należy rozdzielić istniejącą architekturę aplikacji internetowej. Oddzielenie architektury wiąże się z podziałem aplikacji monolitycznej na mniejsze, niezależne usługi, z których każda odpowiada za określoną funkcję lub funkcję. Ten proces obejmuje ocenę bieżącej aplikacji internetowej, zmodyfikowanie architektury i wyodrębnienie kodu aplikacji internetowej na platformę kontenera. Celem jest systematyczne identyfikowanie i wyodrębnianie usług aplikacji, które najbardziej korzystają z oddzielenia. Aby rozdzielić architekturę, wykonaj następujące zalecenia:
Identyfikowanie granic usługi. Stosowanie zasad projektowania opartych na domenie w celu identyfikowania powiązanych kontekstów w aplikacji monolitycznej. Każdy ograniczony kontekst reprezentuje granicę logiczną i jest kandydatem do oddzielenia. Usługi, które reprezentują odrębne funkcje biznesowe i mają mniej zależności, są dobrymi kandydatami.
Ocena korzyści z usługi. Skoncentruj się na usługach, które korzystają najbardziej z niezależnego skalowania. Na przykład zależność zewnętrzna, na przykład dostawca usługi poczty e-mail w aplikacji LOB, może wymagać większej izolacji od awarii. Rozważ usługi, które są poddawane częstym aktualizacjom lub zmianom. Oddzielenie tych usług umożliwia niezależne wdrażanie i zmniejsza ryzyko wpływu na inne części aplikacji.
Ocena możliwości technicznych. Sprawdź bieżącą architekturę, aby zidentyfikować ograniczenia techniczne i zależności, które mogą mieć wpływ na proces oddzielenia. Planowanie sposobu zarządzania danymi i ich udostępniania w usługach. Oddzielone usługi powinny zarządzać własnymi danymi i zminimalizować bezpośredni dostęp do bazy danych w granicach usługi.
Wdrażanie usług platformy Azure. Wybierz i wdróż usługi platformy Azure potrzebne do obsługi usługi aplikacji internetowej, którą zamierzasz wyodrębnić. Aby uzyskać wskazówki, zobacz sekcję Wybierz odpowiednie usługi platformy Azure tego artykułu.
Rozdziel usługę aplikacji internetowej. Zdefiniuj jasne interfejsy i interfejsy API, których nowo wyodrębnione usługi aplikacji internetowej mogą używać do interakcji z innymi częściami systemu. Zaprojektuj strategię zarządzania danymi, która umożliwia każdej usłudze zarządzanie własnymi danymi, ale zapewnia spójność i integralność. Aby uzyskać szczegółowe strategie implementacji i wzorce projektowe do użycia podczas tego procesu wyodrębniania, zobacz sekcję wskazówki dotyczące kodu .
Użyj niezależnego magazynu na potrzeby usług rozdzielonych. Aby uprościć przechowywanie wersji i wdrażanie, upewnij się, że każda usługa oddzielona ma własne magazyny danych. Na przykład implementacja referencyjna oddziela usługę poczty e-mail od aplikacji internetowej i eliminuje konieczność uzyskania dostępu do bazy danych przez usługę. Zamiast tego usługa komunikuje stan dostarczania wiadomości e-mail z powrotem do aplikacji internetowej za pośrednictwem komunikatu usługi Azure Service Bus, a aplikacja internetowa zapisuje notatkę w bazie danych.
Zaimplementuj oddzielne potoki wdrażania dla każdej usługi oddzielonej. W przypadku implementowania oddzielnych potoków wdrażania każda usługa może zostać zaktualizowana zgodnie z własnym harmonogramem. Jeśli różne zespoły lub organizacje w firmie posiadają różne usługi, użycie oddzielnych potoków wdrażania zapewnia każdemu zespołowi kontrolę nad własnymi wdrożeniami. Użyj narzędzi ciągłej integracji i ciągłego dostarczania (CI/CD), takich jak Jenkins, GitHub Actions lub Azure Pipelines, aby skonfigurować te potoki.
Popraw mechanizmy kontroli zabezpieczeń. Upewnij się, że mechanizmy kontroli zabezpieczeń są aktualizowane tak, aby uwzględniały nową architekturę, w tym reguły zapory i mechanizmy kontroli dostępu.
Wybieranie odpowiednich usług platformy Azure
W przypadku każdej usługi platformy Azure w architekturze zapoznaj się z odpowiednim przewodnikiem dotyczącym usługi platformy Azure w strukturze dobrze zaprojektowanej. W przypadku wzorca nowoczesnej aplikacji internetowej potrzebny jest system obsługi komunikatów asynchronicznych, platforma aplikacji obsługująca konteneryzację i repozytorium obrazów kontenerów.
Wybierz kolejkę komunikatów. Kolejka komunikatów jest ważnym składnikiem architektur zorientowanych na usługi. Umożliwia oddzielenie nadawców komunikatów i odbiorników w celu włączenia asynchronicznej obsługi komunikatów. Skorzystaj ze wskazówek dotyczących wybierania usługi obsługi komunikatów platformy Azure, aby wybrać system obsługi komunikatów platformy Azure, który obsługuje twoje potrzeby projektowe. Platforma Azure ma trzy usługi obsługi komunikatów: Azure Event Grid, Azure Event Hubs i Service Bus. Zacznij od usługi Service Bus i użyj jednej z dwóch pozostałych opcji, jeśli usługa Service Bus nie spełnia Twoich potrzeb.
Usługa Przypadek użycia Service Bus Wybierz usługę Service Bus, aby zapewnić niezawodne, uporządkowane i ewentualnie transakcyjne dostarczanie komunikatów o wysokiej wartości w aplikacjach dla przedsiębiorstw. Event Grid Wybierz usługę Event Grid, jeśli chcesz wydajnie obsługiwać dużą liczbę zdarzeń dyskretnych. Usługa Event Grid jest skalowalna dla aplikacji opartych na zdarzeniach, w których wiele małych, niezależnych zdarzeń (takich jak zmiany stanu zasobu) musi być kierowanych do subskrybentów w modelu publikowania-subskrybowania o małych opóźnieniach. Event Hubs Wybierz usługę Event Hubs na potrzeby masowego pozyskiwania danych o wysokiej przepływności, takich jak dane telemetryczne, dzienniki lub analiza w czasie rzeczywistym. Usługa Event Hubs jest zoptymalizowana pod kątem scenariuszy przesyłania strumieniowego, w których dane zbiorcze muszą być pozyskiwane i przetwarzane w sposób ciągły. Zaimplementuj usługę kontenera. W przypadku elementów aplikacji, które chcesz konteneryzować, potrzebujesz platformy aplikacji obsługującej kontenery. Wskazówki dotyczące Wybieranie usługi kontenera platformy Azure mogą pomóc w ich wybraniu. Platforma Azure ma trzy główne usługi kontenerów: Azure Container Apps, Azure Kubernetes Service (AKS) i aplikacja systemu Azure Service. Zacznij od usługi Container Apps i użyj jednej z dwóch pozostałych opcji, jeśli usługa Container Apps nie spełnia Twoich potrzeb.
Usługa Przypadek użycia Aplikacje kontenera Wybierz pozycję Container Apps, jeśli potrzebujesz bezserwerowej platformy, która automatycznie skaluje kontenery i zarządza nimi w aplikacjach opartych na zdarzeniach. AKS Wybierz usługę AKS, jeśli potrzebujesz szczegółowej kontroli nad konfiguracjami platformy Kubernetes i zaawansowanymi funkcjami skalowania, sieci i zabezpieczeń. Web App for Containers Wybierz pozycję Web App for Containers w usłudze App Service, aby uzyskać najprostsze środowisko PaaS. Zaimplementuj repozytorium kontenerów. W przypadku korzystania z usługi obliczeniowej opartej na kontenerach musisz mieć repozytorium do przechowywania obrazów kontenerów. Możesz użyć publicznego rejestru kontenerów, takiego jak Docker Hub lub zarządzanego rejestru, takiego jak Azure Container Registry. Wskazówki dotyczące Wprowadzenie do rejestrów kontenerów na platformie Azure mogą pomóc w wyborze.
Wskazówki dotyczące kodu
Aby pomyślnie rozdzielić i wyodrębnić niezależną usługę, należy zaktualizować kod aplikacji internetowej przy użyciu następujących wzorców projektowych: Strangler Fig, Queue-Based Bilansowanie obciążenia, Konkurujący konsumenci, Monitorowanie punktu końcowego kondycji i Ponów próbę. Na poniższym diagramie przedstawiono role tych wzorców:
Wzorzec fig stranglera: Wzorzec figowy strangler przyrostowo migruje funkcje z aplikacji monolitycznej do usługi oddzielonej. Zaimplementuj ten wzorzec w głównej aplikacji internetowej, aby stopniowo migrować funkcje do niezależnych usług, kierując ruch na podstawie punktów końcowych.
Wzorzec bilansowania obciążenia opartego na kolejce: wzorzec bilansowania obciążenia opartego na kolejce zarządza przepływem komunikatów między producentem a odbiorcą przy użyciu kolejki jako buforu. Zaimplementuj ten wzorzec w części producenta usługi oddzielonej, aby asynchronicznie zarządzać przepływem komunikatów przy użyciu kolejki.
wzorzec konkurujących odbiorców: Wzorzec konkurujących odbiorców umożliwia wielu wystąpień usługi odłączonej niezależnie od odczytu z tej samej kolejki komunikatów i konkurowania o przetwarzanie komunikatów. Zaimplementuj ten wzorzec w oddzielonej usłudze, aby dystrybuować zadania między wieloma wystąpieniami.
wzorzec monitorowania punktu końcowego kondycji: Wzorzec monitorowania punktu końcowego kondycji uwidacznia punkty końcowe na potrzeby monitorowania stanu i kondycji różnych składników aplikacji internetowej. (4a) Zaimplementuj ten wzorzec w głównej aplikacji internetowej. (4b) Ponadto zaimplementuj ją w oddzielonej usłudze w celu śledzenia kondycji punktów końcowych.
Wzorzec ponawiania: wzorzec ponawiania obsługuje błędy przejściowe, ponawiając próby operacji, które mogą sporadycznie zakończyć się niepowodzeniem. (5a) Zaimplementuj ten wzorzec w głównej aplikacji internetowej na wszystkich wywołaniach wychodzących do innych usług platformy Azure, takich jak wywołania kolejki komunikatów i prywatnych punktów końcowych. (5b) Ponadto zaimplementuj ten wzorzec w oddzielonej usłudze, aby obsługiwać błędy przejściowe w wywołaniach prywatnych punktów końcowych.
Każdy wzorzec projektu zapewnia korzyści, które są zgodne z co najmniej jednym filarem Well-Architected Framework. Poniższa tabela zawiera szczegółowe informacje.
Wzorzec projektowania | Lokalizacja implementacji | Niezawodność (RE) | Zabezpieczenia (SE) | Optymalizacja kosztów (CO) | Doskonałość operacyjna (OE) | Wydajność (PE) | Obsługa dobrze zaprojektowanych zasad struktury |
---|---|---|---|---|---|---|---|
Wzorzec figowy stranglera | Główna aplikacja internetowa | ✔ | ✔ | ✔ |
RE:08 CO:07 CO:08 OE:06 OE:11 |
||
Wzorzec wyrównywania obciążeń przy użyciu kolejki | Producent usługi oddzielonej | ✔ | ✔ | ✔ |
RE:06 RE:07 CO:12 PE:05 |
||
Wzorzec konkurujących odbiorców | Odłączona usługa | ✔ | ✔ | ✔ |
RE:05 RE:07 CO:05 CO:07 PE:05 PE:07 |
||
Wzorzec monitorowania punktu końcowego kondycji | Główna aplikacja internetowa i usługa odłączona | ✔ | ✔ | ✔ |
RE:07 RE:10 OE:07 PE:05 |
||
Wzorzec ponawiania prób | Główna aplikacja internetowa i usługa odłączona | ✔ | RE:07 |
Implementowanie wzorca figowego stranglera
Użyj wzorca Strangler Fig, aby stopniowo migrować funkcje z bazy kodu monolitycznego do nowych niezależnych usług. Wyodrębnij nowe usługi z istniejącej bazy kodu monolitycznego i powoli modernizuj krytyczne części aplikacji internetowej. Aby zaimplementować wzorzec Fig stranglera, wykonaj następujące zalecenia:
Konfigurowanie warstwy routingu. W bazie kodu monolitycznej aplikacji internetowej zaimplementuj warstwę routingu, która kieruje ruch na podstawie punktów końcowych. Użyj niestandardowej logiki routingu zgodnie z potrzebami, aby obsługiwać określone reguły biznesowe na potrzeby kierowania ruchem. Jeśli na przykład masz punkt końcowy
/users
w aplikacji monolitycznej i przeniesiesz te funkcje do oddzielonej usługi, warstwa routingu kieruje wszystkie żądania do/users
do nowej usługi.Zarządzanie wdrażaniem funkcji.Zaimplementuj flagi funkcji i wdrożenie etapowe , aby stopniowo wdrażać oddzielone usługi. Istniejący routing aplikacji monolitycznych powinien kontrolować liczbę żądań odbieranych przez oddzielone usługi. Zacznij od niewielkiej liczby żądań i zwiększ użycie w miarę upływu czasu, gdy zyskujesz pewność co do stabilności i wydajności usługi.
Na przykład implementacja referencyjna wyodrębnia funkcję dostarczania wiadomości e-mail do autonomicznej usługi. Usługa może być stopniowo wprowadzana w celu obsługi większego procentu żądań wysyłania wiadomości e-mail zawierających przewodniki pomocy technicznej firmy Contoso. Ponieważ nowa usługa udowodni swoją niezawodność i wydajność, może ostatecznie przejąć cały zestaw obowiązków związanych z pocztą e-mail od monolitu, kończąc przejście.
Użyj usługi fasady (w razie potrzeby). Usługa fasady jest przydatna, gdy pojedyncze żądanie musi wchodzić w interakcje z wieloma usługami lub gdy chcesz ukryć złożoność podstawowego systemu od klienta. Jeśli jednak oddzielona usługa nie ma żadnych publicznych interfejsów API, usługa fasady może nie być konieczna.
W monolitycznej bazie kodu aplikacji internetowej zaimplementuj usługę fasady w celu kierowania żądań do odpowiedniego zaplecza (monolitycznego lub mikrousługi). Upewnij się, że nowa usługa oddzielona może obsługiwać żądania niezależnie, gdy jest ona dostępna za pośrednictwem fasady.
Implementowanie wzorca bilansowania obciążenia opartego na kolejce
Zaimplementuj wzorzec bilansowania obciążenia opartego na kolejce w części producenta usługi oddzielonej do asynchronicznego obsługi zadań, które nie wymagają natychmiastowych odpowiedzi. Ten wzorzec zwiększa ogólną szybkość reakcji i skalowalność systemu przy użyciu kolejki do zarządzania dystrybucją obciążeń. Umożliwia ona oddzielenie usługi do przetwarzania żądań ze stałą szybkością. Aby skutecznie zaimplementować ten wzorzec, postępuj zgodnie z następującymi zaleceniami:
Użyj funkcji kolejkowania komunikatów nieblokujących. Upewnij się, że proces, który wysyła komunikaty do kolejki, nie blokuje innych procesów podczas oczekiwania na odłączonej usługi do obsługi komunikatów w kolejce. Jeśli proces wymaga wyniku operacji odłączonej usługi, zaimplementuj alternatywny sposób obsługi sytuacji podczas oczekiwania na zakończenie operacji w kolejce. Na przykład w środowisku Spring Boot można użyć klasy
StreamBridge
do asynchronicznego publikowania komunikatów w kolejce bez blokowania wątku wywołującego:private final StreamBridge streamBridge; public SupportGuideQueueSender(StreamBridge streamBridge) { this.streamBridge = streamBridge; } // Asynchronously publish a message without blocking the calling thread @Override public void send(String to, String guideUrl, Long requestId) { EmailRequest emailRequest = EmailRequest.newBuilder() .setRequestId(requestId) .setEmailAddress(to) .setUrlToManual(guideUrl) .build(); log.info("EmailRequest: {}", emailRequest); var message = emailRequest.toByteArray(); streamBridge.send(EMAIL_REQUEST_QUEUE, message); log.info("Message sent to the queue"); }
W tym przykładzie języka Java użyto
StreamBridge
metody do asynchronicznego wysyłania komunikatów. Takie podejście zapewnia, że główna aplikacja pozostaje elastyczna i może obsługiwać inne zadania jednocześnie, podczas gdy oddzielona usługa przetwarza żądania w kolejce z możliwością zarządzania.Zaimplementuj ponawianie i usuwanie komunikatów. Zaimplementuj mechanizm ponawiania próby przetworzenia komunikatów w kolejce, których nie można pomyślnie przetworzyć. Jeśli awarie będą się powtarzać, te komunikaty powinny zostać usunięte z kolejki. Na przykład usługa Service Bus ma wbudowane funkcje kolejki ponawiania prób i utraconych komunikatów.
Konfigurowanie przetwarzania komunikatów idempotentnych. Logika, która przetwarza komunikaty z kolejki, musi być idempotentna do obsługi przypadków, w których komunikat może być przetwarzany więcej niż raz. W środowisku Spring Boot można użyć
@StreamListener
lub@KafkaListener
z unikatowym identyfikatorem komunikatu, aby zapobiec zduplikowanemu przetwarzaniu. Możesz też zorganizować proces biznesowy w taki sposób, aby działał w sposób funkcjonalny za pomocą usługi Spring Cloud Stream, gdzieconsume
metoda jest definiowana w sposób, który generuje ten sam wynik, gdy jest uruchamiany wielokrotnie. Aby uzyskać listę ustawień, które zarządzają zachowaniem zużycia komunikatów, zobacz Spring Cloud Stream with Service Bus.Zarządzanie zmianami w środowisku użytkownika. W przypadku korzystania z przetwarzania asynchronicznego zadania mogą nie być wykonywane natychmiast. Aby określić oczekiwania i uniknąć nieporozumień, upewnij się, że użytkownicy wiedzą, kiedy ich zadania są nadal przetwarzane. Użyj wizualnych wskazówek lub komunikatów, aby wskazać, że zadanie jest w toku. Nadaj użytkownikom możliwość otrzymywania powiadomień po zakończeniu zadania, takiego jak wiadomość e-mail lub powiadomienie wypychane.
Implementowanie wzorca konkurujących odbiorców
Zaimplementuj wzorzec konkurujących odbiorców w oddzielonej usłudze, aby zarządzać zadaniami przychodzącymi z kolejki komunikatów. Ten wzorzec obejmuje dystrybucję zadań w wielu wystąpieniach usług oddzielonych. Te usługi przetwarzają komunikaty z kolejki. Wzorzec zwiększa równoważenie obciążenia i zwiększa pojemność systemu do obsługi równoczesnych żądań. Wzorzec konkurujących odbiorców jest skuteczny, gdy:
- Sekwencja przetwarzania komunikatów nie ma kluczowego znaczenia.
- Kolejka pozostaje nienaruszona przez źle sformułowane komunikaty.
- Operacja przetwarzania jest idempotentna, co oznacza, że można ją stosować wiele razy bez zmiany wyniku po początkowej aplikacji.
Aby zaimplementować wzorzec konkurujących odbiorców, postępuj zgodnie z następującymi zaleceniami:
Obsługa współbieżnych komunikatów. Gdy usługi odbierają komunikaty z kolejki, upewnij się, że system skaluje się przewidywanie, konfigurując współbieżność w celu dopasowania do projektu systemu. Wyniki testu obciążeniowego mogą pomóc w określeniu odpowiedniej liczby współbieżnych komunikatów do obsługi. Możesz zacząć od jednego, aby zmierzyć sposób działania systemu.
Wyłącz pobieranie wstępne. Wyłącz wstępne pobieranie komunikatów, aby użytkownicy pobierali komunikaty tylko wtedy, gdy są gotowe.
Używaj niezawodnych trybów przetwarzania komunikatów. Użyj niezawodnego trybu przetwarzania, takiego jak Peek-Lock, który automatycznie ponawia próby komunikatów, które kończą się niepowodzeniem. Ten tryb zapewnia większą niezawodność niż metody usuwania. Jeśli jeden proces roboczy nie obsługuje komunikatu, inny musi być w stanie przetworzyć go bez błędów, nawet jeśli komunikat jest przetwarzany wiele razy.
Implementowanie obsługi błędów. Kierowanie nieprawidłowo sformułowanych lub nieprzetworzonych komunikatów do oddzielnej kolejki utraconych komunikatów. Ten projekt uniemożliwia powtarzalne przetwarzanie. Można na przykład przechwytywać wyjątki podczas przetwarzania komunikatów i przenosić problematyczne komunikaty do oddzielnej kolejki. W usłudze Service Bus komunikaty są przenoszone do kolejki dead-leter po określonej liczbie prób dostarczenia lub po jawnym odrzuceniu przez aplikację.
Obsługa komunikatów poza kolejnością. Zaprojektuj użytkowników, aby przetwarzali komunikaty wychodzące z sekwencji. Jeśli masz wielu odbiorców równoległych, mogą przetwarzać komunikaty poza kolejnością.
Skalowanie na podstawie długości kolejki. Usługi, które używają komunikatów z kolejki, powinny być automatycznie skalowane na podstawie długości kolejki. Skalowanie automatyczne oparte na skalowaniu umożliwia wydajne przetwarzanie skoków przychodzących komunikatów.
Użyj kolejki odpowiedzi z komunikatami. Jeśli system wymaga powiadomień dotyczących przetwarzania po komunikatach, skonfiguruj dedykowaną odpowiedź lub kolejkę odpowiedzi. Ta konfiguracja oddziela komunikaty operacyjne od procesów powiadomień.
Korzystanie z usług bezstanowych. Rozważ użycie usług bezstanowych do przetwarzania żądań z kolejki. Umożliwia to łatwe skalowanie i efektywne użycie zasobów.
Konfigurowanie rejestrowania. Integrowanie rejestrowania i obsługi określonych wyjątków w przepływie pracy przetwarzania komunikatów. Skoncentruj się na przechwytywaniu błędów serializacji i kierowaniu tych problematycznych komunikatów do mechanizmu utraconych komunikatów. Te dzienniki zapewniają cenne szczegółowe informacje dotyczące rozwiązywania problemów.
Implementacja referencyjna używa wzorca konkurujących odbiorców w usłudze bezstanowej, która działa w usłudze Container Apps do przetwarzania żądań dostarczania wiadomości e-mail z kolejki usługi Service Bus.
Procesor rejestruje szczegóły przetwarzania komunikatów, aby ułatwić rozwiązywanie problemów i monitorowanie. Przechwytuje błędy deserializacji i udostępnia szczegółowe informacje, które mogą być przydatne podczas debugowania. Usługa jest skalowana na poziomie kontenera, aby umożliwić wydajną obsługę skoków komunikatów na podstawie długości kolejki. Oto kod:
@Configuration
public class EmailProcessor {
private static final Logger log = LoggerFactory.getLogger(EmailProcessor.class);
@Bean
Function<byte[], byte[]> consume() {
return message -> {
log.info("New message received");
try {
EmailRequest emailRequest = EmailRequest.parseFrom(message);
log.info("EmailRequest: {}", emailRequest);
EmailResponse emailResponse = EmailResponse.newBuilder()
.setEmailAddress(emailRequest.getEmailAddress())
.setUrlToManual(emailRequest.getUrlToManual())
.setRequestId(emailRequest.getRequestId())
.setMessage("Email sent to " + emailRequest.getEmailAddress() + " with URL to manual " + emailRequest.getUrlToManual())
.setStatus(Status.SUCCESS)
.build();
return emailResponse.toByteArray();
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Error parsing email request message", e);
}
};
}
}
Implementowanie wzorca monitorowania punktu końcowego kondycji
Zaimplementuj wzorzec monitorowania punktu końcowego kondycji w kodzie głównej aplikacji i odłączony kod usługi w celu śledzenia kondycji punktów końcowych aplikacji. Koordynatorzy, tacy jak AKS lub Container Apps, mogą sondować te punkty końcowe, aby zweryfikować kondycję usługi i ponownie uruchomić wystąpienia w złej kondycji. Siłownik Spring Boot zapewnia wbudowaną obsługę kontroli kondycji. Może uwidaczniać punkty końcowe sprawdzania kondycji dla kluczowych zależności, takich jak bazy danych, brokerzy komunikatów i systemy magazynowania. Aby zaimplementować wzorzec monitorowania punktu końcowego kondycji, wykonaj następujące zalecenia:
Implementowanie kontroli kondycji. Użyj siłownika Spring Boot, aby zapewnić punkty końcowe sprawdzania kondycji. Siłownik uwidacznia punkt końcowy
/actuator/health
zawierający wbudowane wskaźniki kondycji i niestandardowe kontrole różnych zależności. Aby włączyć punkt końcowy kondycji, dodaj zależnośćspring-boot-starter-actuator
w plikupom.xml
lubbuild.gradle
:<!-- Add Spring Boot Actuator dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Skonfiguruj punkt końcowy kondycji w
application.properties
, jak pokazano w implementacji referencyjnej:management.endpoints.web.exposure.include=metrics,health,info,retry,retryevents
Zweryfikuj zależności. Siłownik Spring Boot zawiera wskaźniki kondycji dla różnych zależności, takich jak bazy danych, brokerzy komunikatów (RabbitMQ lub Kafka) i usługi magazynowania. Aby zweryfikować dostępność usług platformy Azure, takich jak Azure Blob Storage lub Service Bus, użyj technologii takich jak Azure Spring Apps lub Micrometer, które zapewniają wskaźniki kondycji dla tych usług. Jeśli potrzebujesz niestandardowych testów, możesz je zaimplementować, tworząc niestandardowy
HealthIndicator
fasoli:import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.stereotype.Component; @Component public class CustomAzureServiceBusHealthIndicator implements HealthIndicator { @Override public Health health() { // Implement your health check logic here (for example, ping Service Bus). boolean isServiceBusHealthy = checkServiceBusHealth(); return isServiceBusHealthy ? Health.up().build() : Health.down().build(); } private boolean checkServiceBusHealth() { // Implement health check logic (pinging or connecting to the service). return true; // Placeholder. Implement the actual logic. } }
Konfigurowanie zasobów platformy Azure. Skonfiguruj zasób platformy Azure, aby używać adresów URL kontroli kondycji aplikacji w celu potwierdzenia aktualności i gotowości. Możesz na przykład użyć narzędzia Terraform do potwierdzenia aktualności i gotowości aplikacji wdrożonych w usłudze Container Apps. Aby uzyskać więcej informacji, zobacz Sondy kondycji w usłudze Container Apps.
Implementowanie wzorca ponawiania prób
Wzorzec ponawiania umożliwia aplikacjom odzyskiwanie po błędach przejściowych. Ten wzorzec jest kluczowy dla wzorca niezawodnej aplikacji internetowej, dlatego aplikacja internetowa powinna już używać wzorca ponawiania prób. Zastosuj wzorzec ponawiania prób do żądań do systemów obsługi komunikatów i żądań, które są wystawiane przez oddzielone usługi wyodrębnione z aplikacji internetowej. Aby zaimplementować wzorzec ponawiania prób, wykonaj następujące zalecenia:
Skonfiguruj opcje ponawiania prób. Pamiętaj, aby skonfigurować klienta, który jest odpowiedzialny za interakcje z kolejką komunikatów z odpowiednimi ustawieniami ponawiania prób. Określ parametry, takie jak maksymalna liczba ponownych prób, opóźnienie między ponowną próbą i maksymalnym opóźnieniem.
Użyj wycofywania wykładniczego. Zaimplementuj strategię wycofywania wykładniczego na potrzeby ponownych prób. Ta strategia polega na zwiększeniu czasu między kolejnymi próbami wykładniczo, co pomaga zmniejszyć obciążenie systemu w okresach wysokich współczynników awarii.
Użyj funkcji ponawiania prób zestawu SDK. W przypadku usług, które mają wyspecjalizowane zestawy SDK, takie jak Service Bus lub Blob Storage, użyj wbudowanych mechanizmów ponawiania prób. Te wbudowane mechanizmy są zoptymalizowane pod kątem typowych przypadków użycia usługi, mogą obsługiwać ponawianie prób bardziej efektywnie i wymagać mniejszej konfiguracji.
Użyj standardowych bibliotek odporności dla klientów HTTP. W przypadku klientów HTTP można używać funkcji Resilience4j razem z interfejsem RestTemplate platformy Spring lub elementem WebClient do obsługi ponownych prób w komunikacji HTTP. Możesz opakowować architekturę RestTemplate za pomocą logiki ponawiania prób w usłudze Resilience4j, aby efektywnie obsługiwać przejściowe błędy HTTP.
Obsługa blokowania komunikatów. W przypadku systemów opartych na komunikatach zaimplementuj strategie obsługi komunikatów, które obsługują ponawianie prób bez utraty danych. Na przykład użyj trybów podglądu blokady, gdy są dostępne. Upewnij się, że komunikaty, które zakończyły się niepowodzeniem, są skutecznie ponawiane i przenoszone do kolejki utraconych komunikatów po powtarzających się awariach.
Wskazówki dotyczące konfiguracji
Poniższe sekcje zawierają wskazówki dotyczące implementowania aktualizacji konfiguracji. Każda sekcja jest zgodna z co najmniej jednym filarem Well-Architected Framework.
Konfigurowanie | Niezawodność (RE) | Zabezpieczenia (SE) | Optymalizacja kosztów (CO) | Doskonałość operacyjna (OE) | Wydajność (PE) | Obsługa dobrze zaprojektowanych zasad struktury |
---|---|---|---|---|---|---|
Konfigurowanie uwierzytelniania i autoryzacji | ✔ | ✔ |
SE:05 OE:10 |
|||
Implementowanie niezależnego skalowania automatycznego | ✔ | ✔ | ✔ |
RE:06 CO:12 PE:05 |
||
Konteneryzowanie wdrożenia usługi | ✔ | ✔ |
CO:13 PE:09 PE:03 |
Konfigurowanie uwierzytelniania i autoryzacji
Aby skonfigurować uwierzytelnianie i autoryzację dla wszystkich nowych usług platformy Azure (tożsamości obciążeń), które są dodawane do aplikacji internetowej, wykonaj następujące zalecenia:
Użyj tożsamości zarządzanych dla każdej nowej usługi. Każda niezależna usługa powinna mieć własną tożsamość i używać tożsamości zarządzanych do uwierzytelniania między usługami. Tożsamości zarządzane eliminują konieczność zarządzania poświadczeniami w kodzie i zmniejszają ryzyko wycieku poświadczeń. Ułatwiają one unikanie dołączania poufnych informacji, takich jak parametry połączenia w kodzie lub plikach konfiguracji.
Przyznaj każdemu nowemu usłudze najmniej uprawnień. Przypisz tylko niezbędne uprawnienia do każdej nowej tożsamości usługi. Jeśli na przykład tożsamość musi być wypychana tylko do rejestru kontenerów, nie daj jej uprawnień ściągnięcia. Regularnie przeglądaj te uprawnienia i dostosowuj je w razie potrzeby. Użyj różnych tożsamości dla różnych ról, takich jak wdrażanie i aplikacja. Ogranicza to potencjalne szkody w przypadku naruszenia zabezpieczeń jednej tożsamości.
Użyj infrastruktury jako kodu (IaC). Użyj narzędzia Bicep lub podobnego narzędzia IaC, takiego jak Terraform, aby zdefiniować zasoby w chmurze i zarządzać nimi. Infrastruktura IaC zapewnia spójne stosowanie konfiguracji zabezpieczeń we wdrożeniach i umożliwia kontrolowanie wersji konfiguracji infrastruktury.
Aby skonfigurować uwierzytelnianie i autoryzację dla użytkowników (tożsamości użytkowników), wykonaj następujące zalecenia:
Przyznawanie użytkownikom najniższych uprawnień. Podobnie jak w przypadku usług, upewnij się, że użytkownicy mają tylko uprawnienia, których potrzebują do wykonywania swoich zadań. Regularnie sprawdzaj i dostosowuj te uprawnienia.
Przeprowadzanie regularnych inspekcji zabezpieczeń. Regularnie sprawdzaj i przeprowadzaj inspekcję konfiguracji zabezpieczeń. Poszukaj błędów konfiguracji i niepotrzebnych uprawnień oraz naprawy lub usuń je natychmiast.
Implementacja referencyjna używa usługi IaC do przypisywania tożsamości zarządzanych do dodanych usług i określonych ról do każdej tożsamości. Definiuje role i uprawnienia dostępu do wdrożenia, definiując role dla wypychania i ściągania usługi Container Registry. Oto kod:
resource "azurerm_role_assignment" "container_app_acr_pull" {
principal_id = var.aca_identity_principal_id
role_definition_name = "AcrPull"
scope = azurerm_container_registry.acr.id
}
resource "azurerm_user_assigned_identity" "container_registry_user_assigned_identity" {
name = "ContainerRegistryUserAssignedIdentity"
resource_group_name = var.resource_group
location = var.location
}
resource "azurerm_role_assignment" "container_registry_user_assigned_identity_acr_pull" {
scope = azurerm_container_registry.acr.id
role_definition_name = "AcrPull"
principal_id = azurerm_user_assigned_identity.container_registry_user_assigned_identity.principal_id
}
# For demo purposes, allow the current user to access the container registry.
# Note: When running as a service principal, this is also needed.
resource "azurerm_role_assignment" "acr_contributor_user_role_assignement" {
scope = azurerm_container_registry.acr.id
role_definition_name = "Contributor"
principal_id = data.azuread_client_config.current.object_id
}
Konfigurowanie niezależnego skalowania automatycznego
Wzorzec nowoczesnej aplikacji internetowej zaczyna rozpadać architekturę monolityczną i wprowadza oddzielenie usług. W przypadku oddzielenia architektury aplikacji internetowej można niezależnie skalować oddzielone usługi. Skalowanie usług platformy Azure w celu obsługi niezależnej usługi aplikacji internetowej, a nie całej aplikacji internetowej, optymalizuje skalowanie kosztów przy jednoczesnym spełnieniu wymagań. Aby automatycznie skalować kontenery, wykonaj następujące zalecenia:
Korzystanie z usług bezstanowych. Upewnij się, że usługi są bezstanowe. Jeśli aplikacja internetowa zawiera stan sesji procesu, należy ją zewnętrznie połączyć z rozproszoną pamięcią podręczną, na przykład Redis lub bazą danych, na przykład SQL Server.
Konfigurowanie reguł skalowania automatycznego. Użyj konfiguracji skalowania automatycznego, które zapewniają najbardziej ekonomiczną kontrolę nad usługami. W przypadku usług konteneryzowanych skalowanie oparte na zdarzeniach, takie jak Kubernetes Event-Driven Autoscaler (KEDA), często zapewnia szczegółową kontrolę, która umożliwia skalowanie na podstawie metryk zdarzeń. Usługa Container Apps i usługa AKS obsługują usługę KEDA. W przypadku usług, które nie obsługują usługi KEDA, takich jak usługa App Service, użyj funkcji skalowania automatycznego udostępnianych przez samą platformę. Te funkcje często obejmują skalowanie na podstawie reguł opartych na metrykach lub ruchu HTTP.
Skonfiguruj minimalną liczbę replik. Aby zapobiec zimnym startom, skonfiguruj ustawienia skalowania automatycznego, aby zachować co najmniej jedną replikę. Zimny start to inicjowanie usługi ze stanu zatrzymania. Zimny start często opóźnia odpowiedź. Jeśli minimalizacja kosztów jest priorytetem i można tolerować opóźnienia uruchamiania zimnego, ustaw minimalną liczbę replik na 0 podczas konfigurowania skalowania automatycznego.
Skonfiguruj okres ochładzania. Zastosuj odpowiedni okres ochładzania, aby wprowadzić opóźnienie między zdarzeniami skalowania. Celem jest zapobieganie nadmiernym skalowaniu działań wyzwalanych przez tymczasowe skoki obciążenia.
Konfigurowanie skalowania opartego na kolejce. Jeśli aplikacja używa kolejki komunikatów, takiej jak Service Bus, skonfiguruj ustawienia skalowania automatycznego w celu skalowania na podstawie długości kolejki komunikatów żądania. Program scaler próbuje zachować jedną replikę usługi dla każdej N komunikatów w kolejce (zaokrąglone w górę).
Na przykład implementacja referencyjna używa modułu skalowania KEDA usługi Service Bus do automatycznego skalowania aplikacji kontenera na podstawie długości kolejki usługi Service Bus. Reguła skalowania o nazwie service-bus-queue-length-rule
dostosowuje liczbę replik usługi na podstawie liczby komunikatów w określonej kolejce usługi Service Bus. Parametr messageCount
jest ustawiony na 10, co umożliwia skonfigurowanie modułu skalowania w celu dodania jednej repliki dla każdego 10 komunikatów w kolejce. Maksymalna liczba replik (max_replicas
) jest ustawiona na 10. Minimalna liczba replik jest niejawnie 0, chyba że zostanie zastąpiona. Ta konfiguracja umożliwia usłudze skalowanie w dół do zera, gdy nie ma żadnych komunikatów w kolejce. Parametry połączenia kolejki usługi Service Bus są przechowywane jako wpis tajny na platformie Azure o nazwie azure-servicebus-connection-string
, który służy do uwierzytelniania modułu skalowania w usłudze Service Bus. Oto kod narzędzia Terraform:
max_replicas = 10
min_replicas = 1
custom_scale_rule {
name = "service-bus-queue-length-rule"
custom_rule_type = "azure-servicebus"
metadata = {
messageCount = 10
namespace = var.servicebus_namespace
queueName = var.email_request_queue_name
}
authentication {
secret_name = "azure-servicebus-connection-string"
trigger_parameter = "connection"
}
}
Konteneryzowanie wdrożenia usługi
Konteneryzacja to hermetyzacja wszystkich zależności wymaganych przez aplikację w uproszczonym obrazie, który można niezawodnie wdrożyć na wielu hostach. Aby konteneryzować wdrożenie, wykonaj następujące zalecenia:
Zidentyfikuj granice domeny. Zacznij od zidentyfikowania granic domeny w aplikacji monolitycznej. Dzięki temu można określić, które części aplikacji można wyodrębnić do oddzielnych usług.
Tworzenie obrazów platformy Docker. Podczas tworzenia obrazów platformy Docker dla usług Java użyj oficjalnych obrazów podstawowych openJDK. Te obrazy zawierają tylko minimalny zestaw pakietów, które należy uruchomić w języku Java. Użycie tych obrazów minimalizuje zarówno rozmiar pakietu, jak i obszar powierzchni podatnej na ataki.
Użyj wieloetapowych plików Dockerfile. Użyj wieloetapowego pliku Dockerfile, aby oddzielić zasoby czasu kompilacji od obrazu kontenera środowiska uruchomieniowego. Użycie tego typu pliku pomaga zachować małe i bezpieczne obrazy produkcyjne. Możesz również użyć wstępnie skonfigurowanego serwera kompilacji i skopiować plik JAR do obrazu kontenera.
Uruchom polecenie jako użytkownik niebędący użytkownikiem głównym. Uruchom kontenery Java jako użytkownik niebędący użytkownikiem głównym (za pomocą nazwy użytkownika lub identyfikatora UID $APP_UID), aby dopasować się do zasady najniższych uprawnień. Ogranicza to potencjalne skutki naruszenia zabezpieczeń kontenera.
Nasłuchiwanie na porcie 8080. Po uruchomieniu kontenerów jako użytkownik niebędący użytkownikiem głównym skonfiguruj aplikację do nasłuchiwania na porcie 8080. Jest to powszechna konwencja dla użytkowników niebędących użytkownikami głównymi.
Hermetyzowanie zależności. Upewnij się, że wszystkie zależności wymagane przez aplikację są hermetyzowane w obrazie kontenera platformy Docker. Hermetyzacja umożliwia niezawodne wdrażanie aplikacji na wielu hostach.
Wybierz odpowiednie obrazy podstawowe. Wybrany obraz podstawowy zależy od środowiska wdrażania. Jeśli wdrażasz na przykład w usłudze Container Apps, musisz użyć obrazów platformy Docker systemu Linux.
Implementacja referencyjna demonstruje proces kompilacji platformy Docker na potrzeby konteneryzowania aplikacji Java. Plik Dockerfile używa kompilacji jednoetapowej z podstawowym obrazem OpenJDK (mcr.microsoft.com/openjdk/jdk:17-ubuntu
), który zapewnia niezbędne środowisko uruchomieniowe Java.
Plik Dockerfile obejmuje następujące kroki:
- Deklarowanie woluminu. Zdefiniowano wolumin tymczasowy (
/tmp
). Ten wolumin zapewnia magazyn plików tymczasowy, który jest oddzielony od głównego systemu plików kontenera. - Kopiowanie artefaktów. Plik JAR aplikacji (
email-processor.jar
) jest kopiowany do kontenera wraz z agentem usługi Application Insights (applicationinsights-agent.jar
), który jest używany do monitorowania. - Ustawianie punktu wejścia. Kontener jest skonfigurowany do uruchamiania aplikacji z włączonym agentem usługi Application Insights. Kod używa
java -javaagent
do monitorowania aplikacji w czasie wykonywania.
Plik Dockerfile zachowuje mały obraz, uwzględniając tylko zależności środowiska uruchomieniowego. Jest ona odpowiednia dla środowisk wdrażania, takich jak Container Apps, które obsługują kontenery oparte na systemie Linux.
# Use the OpenJDK 17 base image on Ubuntu as the foundation.
FROM mcr.microsoft.com/openjdk/jdk:17-ubuntu
# Define a volume to allow temporary files to be stored separately from the container's main file system.
VOLUME /tmp
# Copy the packaged JAR file into the container.
COPY target/email-processor.jar app.jar
# Copy the Application Insights agent for monitoring.
COPY target/agent/applicationinsights-agent.jar applicationinsights-agent.jar
# Set the entrypoint to run the application with the Application Insights agent.
ENTRYPOINT ["java", "-javaagent:applicationinsights-agent.jar", "-jar", "/app.jar"]
Wdrażanie implementacji referencyjnej
Wdróż implementację referencyjną wzorca nowoczesnej aplikacji internetowej dla języka Java. W repozytorium znajdują się instrukcje dotyczące wdrażania programistycznego i produkcyjnego. Po wdrożeniu implementacji można symulować i obserwować wzorce projektowe.
Na poniższym diagramie przedstawiono architekturę implementacji referencyjnej:
Pobierz plik programu Visio tej architektury.