W jaki sposób architektury systemów mikrousług unikają wąskich gardeł w sieci?


72

Dużo czytałem o architekturach mikrousług dla aplikacji serwerowych i zastanawiałem się, w jaki sposób wykorzystanie sieci wewnętrznej nie stanowi wąskiego gardła ani znaczącej wady w porównaniu z architekturą monolityczną.

Dla precyzji oto moje interpretacje tych dwóch terminów:

  1. Architektura Monolith: jedna aplikacja w jednym języku, która obsługuje wszystkie funkcje, dane itp. Moduł równoważenia obciążenia rozdziela żądania od użytkownika końcowego na wiele komputerów, z których każde uruchamia jedną instancję naszej aplikacji.

  2. Architektura mikrousług: Wiele aplikacji (mikrousług) obsługuje niewielką część funkcjonalności i danych. Każda mikrousługa udostępnia wspólny interfejs API, do którego dostęp uzyskuje się przez sieć (w przeciwieństwie do komunikacji międzyprocesowej lub pamięci współdzielonej na tym samym komputerze). Wywołania API są przeważnie przeważnie na serwerze w celu utworzenia strony, chociaż być może niektóre z tych prac są wykonywane przez klienta odpytującego poszczególne mikrousługi.

Według mojej naiwnej wyobraźni wydaje się, że architektura mikrousług wykorzystuje wolny ruch sieciowy w przeciwieństwie do szybszych zasobów na tym samym komputerze (pamięci i dysku). Jak można się upewnić, że zapytania API przez sieć wewnętrzną nie spowolnią całkowitego czasu odpowiedzi?


Sieć wewnętrzna jest często 1 Gb / s, czasem szybsza. Pomyśl o średnim rozmiarze odpowiedzi JSON z interfejsu API. Ile takich odpowiedzi można przesłać przez połączenie 1 Gb / s w ciągu jednej sekundy?
Arseni Mourzenko

3
jeśli uważasz, że potrzebujesz mikrousług - i możesz! - dwie doskonałe książki do przygotowania: amazon.com/Building-Microservices-Sam-Newman/dp/1491950358 i amazon.com/Release-It-Production-Ready-Pragmatic-Programmers/dp/…
Steven A. Lowe

@MainMa problemem nie jest przepustowość, ale opóźnienie. A jeśli musisz przejść w obie strony, zdziwisz się, jak mało rzeczywistej przepustowości możesz użyć
Stephan Eggermont

Odpowiedzi:


61

Sieci wewnętrzne często używają połączeń 1 Gb / s lub szybszych. Połączenia światłowodowe lub łączenie umożliwiają znacznie większą przepustowość między serwerami. Teraz wyobraź sobie średni rozmiar odpowiedzi JSON z API. Ile takich odpowiedzi można przesłać przez połączenie 1 Gb / s w ciągu jednej sekundy?

Zróbmy matematykę. 1 Gb / s to 131 072 KB na sekundę. Jeśli średnia odpowiedź JSON wynosi 5 KB (co jest całkiem sporo!), Możesz wysłać 26 214 odpowiedzi na sekundę przewodowo za pomocą tylko jednej pary maszyn . Nie jest tak źle, prawda?

Dlatego połączenie sieciowe zwykle nie stanowi wąskiego gardła.

Innym aspektem mikrousług jest to, że można łatwo skalować. Wyobraź sobie dwa serwery, jeden hostujący interfejs API, a drugi go wykorzystujący. Jeśli kiedykolwiek połączenie stanie się wąskim gardłem, wystarczy dodać dwa inne serwery i można podwoić wydajność.

To wtedy nasze wcześniejsze 26 214 odpowiedzi na sekundę stają się zbyt małe dla skali aplikacji. Dodajesz pozostałe dziewięć par i możesz teraz obsłużyć 262 140 odpowiedzi.

Wróćmy jednak do naszej pary serwerów i dokonajmy porównań.

  • Jeśli przeciętne niebuforowane zapytanie do bazy danych zajmuje 10 ms, masz do 100 zapytań na sekundę. 100 zapytań. 26 214 odpowiedzi. Osiągnięcie prędkości 26 214 odpowiedzi na sekundę wymaga dużej ilości buforowania i optymalizacji (jeśli odpowiedź faktycznie wymaga zrobienia czegoś pożytecznego, na przykład zapytania do bazy danych; odpowiedzi w stylu „Hello World” nie kwalifikują się).

  • Na moim komputerze, DOMContentLoaded dla strony głównej Google stało się 394 ms. po wysłaniu zapytania. To mniej niż 3 żądania na sekundę. Dla strony głównej Programmers.SE stało się to 603 ms. po wysłaniu zapytania. To nie są nawet 2 żądania na sekundę. Nawiasem mówiąc, mam połączenie internetowe 100 Mbps i szybki komputer: wielu użytkowników będzie czekać dłużej.

    Jeśli wąskim gardłem jest szybkość sieci między serwerami, te dwie witryny mogą dosłownie wykonywać tysiące połączeń z różnymi interfejsami API podczas wyświetlania strony.

Te dwa przypadki pokazują, że sieć prawdopodobnie nie będzie twoim wąskim gardłem w teorii (w praktyce powinieneś zrobić rzeczywiste testy porównawcze i profilowanie, aby określić dokładną lokalizację wąskiego gardła twojego konkretnego systemu hostowanego na określonym sprzęcie). Czas poświęcony na wykonanie rzeczywistej pracy (czy byłyby to zapytania SQL, kompresja itp.) I wysłanie wyniku do użytkownika końcowego jest znacznie ważniejszy.

Pomyśl o bazach danych

Zazwyczaj bazy danych są hostowane oddzielnie od aplikacji internetowej, która je używa. Może to budzić obawy: co z szybkością połączenia między serwerem obsługującym aplikację a serwerem obsługującym bazę danych?

Wydaje się, że w niektórych przypadkach prędkość połączenia staje się problematyczna, to znaczy, gdy przechowujesz ogromne ilości danych, które nie muszą być przetwarzane przez samą bazę danych i powinny być teraz dostępne (czyli duże pliki binarne). Ale takie sytuacje są rzadkie: w większości przypadków szybkość przesyłania nie jest tak duża w porównaniu do szybkości przetwarzania samego zapytania.

Rzeczywista prędkość transferu ma znaczenie wtedy, gdy firma hostuje duże zestawy danych na serwerze NAS, a dostęp do NAS ma wielu klientów jednocześnie. Tutaj SAN może być rozwiązaniem. Biorąc to pod uwagę, nie jest to jedyne rozwiązanie. Kable kategorii 6 mogą obsługiwać prędkości do 10 Gb / s; Łączenie można również wykorzystać do zwiększenia prędkości bez zmiany kabli lub kart sieciowych. Istnieją inne rozwiązania, obejmujące replikację danych na wielu serwerach NAS.

Zapomnij o prędkości; pomyśl o skalowalności

Ważnym punktem aplikacji internetowej jest możliwość skalowania. Chociaż rzeczywista wydajność ma znaczenie (ponieważ nikt nie chce płacić za mocniejsze serwery), skalowalność jest o wiele ważniejsza, ponieważ pozwala wrzucić dodatkowy sprzęt w razie potrzeby.

  • Jeśli masz niezbyt szybką aplikację, stracisz pieniądze, ponieważ będziesz potrzebować mocniejszych serwerów.

  • Jeśli masz szybką aplikację, której nie można skalować, stracisz klientów, ponieważ nie będziesz w stanie odpowiedzieć na rosnące zapotrzebowanie.

W ten sam sposób maszyny wirtualne były dziesięć lat temu postrzegane jako ogromny problem z wydajnością. Rzeczywiście, hostowanie aplikacji na serwerze w porównaniu z hostowaniem jej na maszynie wirtualnej miało istotny wpływ na wydajność. Choć luka jest dziś znacznie mniejsza, nadal istnieje.

Pomimo tej utraty wydajności środowiska wirtualne stały się bardzo popularne ze względu na elastyczność, jaką zapewniają.

Podobnie jak w przypadku szybkości sieci, może się okazać, że VM jest wąskim gardłem, a biorąc pod uwagę faktyczną skalę, zaoszczędzisz miliardy dolarów, hostując aplikację bezpośrednio, bez maszyn wirtualnych. Ale nie dzieje się tak w przypadku 99,9% aplikacji: ich wąskie gardło występuje gdzie indziej, a wadę utraty kilku mikrosekund z powodu maszyny wirtualnej łatwo kompensują korzyści wynikające z abstrakcji sprzętu i skalowalności.


Jasne, możemy powiedzieć, że odpowiedzi JSON są małe, ale co z ich ilością ? Wydaje mi się, że witryna o dużym obciążeniu miałaby większy ruch sieciowy w architekturze mikrousługowej niż architektura monolityczna (gdzie jedynym ruchem sieciowym jest do / z serwerów bazy danych). Buforowanie może pomóc, ale w przypadku treści generowanych w czasie rzeczywistym i / lub dynamicznie nie wiem, jak daleko by się posunęło.
James Mishra

@JamesMishra: Zredagowałem moją odpowiedź, aby odpowiedzieć na twoje obawy.
Arseni Mourzenko

Twoja odpowiedź jest idealna . Nie tylko odpowiedziałeś na każdy zarzut, jaki wymyśliłem, ale odpowiedziałeś na zastrzeżenia, o których nie myślałem.
James Mishra,

5
Moje 2 centy z prawdziwego świata: system złożony z bardzo gadatliwych mikrousług może mieć problemy z wydajnością wyłącznie z powodu duszącej się sieci. Buforowanie i projektowanie oparte na strumieniu zdarzeń jest twoim przyjacielem w takich przypadkach. Oprócz sieci, procesora i pamięci, system oparty na mikrousługach musi także uwzględniać odporność w swojej konstrukcji: co się stanie, jeśli mikrousługa ulegnie awarii? Jak budować próby, rozproszone transakcje, samoleczenie, monitorowanie - sugeruję wyszukanie hasła „musisz być taki wysoki, aby korzystać z mikrousług”
Sudhanshu Mishra

4
Popraw mnie, jeśli się mylę, ale o ile wiem, jeśli masz sieć 1 Gb / s, oznacza to, że możesz teoretycznie wysyłać dane o wartości 1 Gb / s za pośrednictwem tej sieci. Bez względu na liczbę połączeń. Im większa liczba połączeń, tym mniejsza przepustowość dla każdego połączenia. Rzeczywisty limit bez modernizacji sieci w celu obsługi większej przepustowości wyniósłby 26,214 odpowiedzi na sekundę. Dodanie większej liczby serwerów nie zwiększy przepustowości sieci. Jeśli pojedynczy klaster jest w stanie wyrzucić taki ruch, dodanie większej liczby serwerów generujących jeszcze więcej danych spowoduje przeciążenie sieci.
Sebbe

7

Myślę, że za dużo czytasz w części „mikro”. Nie oznacza to zastąpienia każdej klasy usługą sieciową, ale ułożenie monolitycznej aplikacji na komponenty o rozsądnych rozmiarach, z których każdy dotyczy aspektu twojego programu. Usługi nie będą ze sobą rozmawiać, więc w najgorszym przypadku podzieliłeś duże żądanie sieciowe na kilka mniejszych. Zwracane dane i tak nie będą się znacząco różnić od otrzymywanych (chociaż możesz zwrócić więcej danych i skonsolidować je w kliencie)


3
„Usługi nie będą ze sobą rozmawiać”. Wyobrażam sobie, że mikrousługi mogą mieć wspólne zależności (być może uwierzytelnianie?), Które można rozdzielić na inne mikrousługi. W pewnym sensie LDAP jest mikrousługą uwierzytelniania i wyobrażam sobie, że wszystkie inne mikrousługi z nią rozmawiają. Lub ... czy uwierzytelnianie odbywa się tylko raz? W jaki sposób każda mikrousługa sprawdza uprawnienia przed uwierzytelnieniem, aby zapobiec atakom z bezpośrednim dostępem do obiektów?
James Mishra,

2
@JamesMishra dobrze .. to zależy. Kiedy ostatnio korzystałem z architektury mikrousług, każda usługa była całkowicie niezależna od innych ze względów bezpieczeństwa (ale także ze względów korporacyjnych). Auth był obsługiwany przez każdego inaczej, choć kontrolowany przez politykę architektury. Nadal nie ma powodu, dla którego nie mogliby na przykład rozmawiać z auth lub po prostu mieli auth na podstawie biblioteki. Ale .. Próbowałem powiedzieć, że nie powinni przekazywać wielu połączeń między sobą, nie że nie powinni korzystać z usług jako sami klienci.
gbjbaanb,

@JamesMishra, auth jest często własną usługą w tych środowiskach, więc każda usługa powinna po prostu z niej skorzystać, zamiast sami wykonać pełną implementację.
Paul,

2

Dzięki takiej strukturze dostępu do kodu i zasobów, aby wynikowy system był wystarczająco elastyczny, aby działał jako aplikacja monolityczna lub rozproszona poprzez konfigurację. Jeśli wyodrębnisz mechanizm komunikacji za jakimś wspólnym interfejsem i zbudujesz system z myślą o współbieżności, możesz łatwo zoptymalizować wszystko po wyprofilowaniu systemu i znalezieniu prawdziwych wąskich gardeł.


Przykład wyjaśniający, co zakładam, że @mortalapeman oznacza: masz interfejs java / c # IProductA Dostępnośćibitiy, z którym wszyscy klienci IProductA dostępność są powiązani. Istnieje również klasa ProductAvailibitiyImpl, która implementuje ten interfejs, oraz ProductAvailibitiyMicroservice, która korzysta z ProductAvailibitiyImpl. Konsumentów można skonfigurować do korzystania z lokalnego ProductAvailibitiyImpl lub zdalnego proxy do ProductAvailibitiyMicroservice
k3b

2

Chciałbym dodać inną perspektywę, z innej branży o bardzo różnych założeniach - symulacja rozproszona (na poziomie jednostki). Pod względem koncepcyjnym przypomina to rozproszoną grę wideo FPS. Kluczowe różnice: wszyscy gracze mają taki sam stan: gdzie smok jest teraz; brak wywołań bazy danych; wszystko jest trzymane w pamięci RAM dla szybkości i małych opóźnień, przepustowość jest mniej istotna (ale myślę, że nie można tego całkowicie zignorować).

Możesz myśleć o każdej aplikacji uczestniczącej albo jako monolicie (który reprezentuje wszystkie aspekty gracza), albo jako mikrousługi (która reprezentuje tylko jednego gracza w tłumie).

Zainteresowanie ze strony moich kolegów polegało na rozbiciu samej aplikacji uczestniczącej, na mniejsze mikrousługi, które mogą być współużytkowane, np. Arbitraż uszkodzeń lub obliczenia w polu widzenia, rzeczy, które zwykle są dołączane do symulacji.

Problemem jest opóźnienie wysyłania połączeń i oczekiwania na żądania. Jak zresztą zauważyli inni, przepustowość jest nieistotna i obfita. Ale jeśli obliczenia dotyczące linii wzroku wynoszą od 1 mikrosekundy do 100 mikrosekund (powiedzmy, ze względu na kolejkowanie w nowej mikrousługie współużytkowanej przez wszystkie aplikacje odtwarzacza), jest to ogromna strata (może wymagać kilku lub wielu obliczeń linii wzroku dla każda aktualizacja, kilka aktualizacji / sekundę).

Zastanów się bardzo, jak działają usługi, kiedy są wywoływane i jakie dane są wymieniane. Nasze aplikacje już nie wymieniają tylko informacji o pozycji, lecz wymieniają informacje o martwym rachunku - jestem na pozycji x, jadę w kierunku y z prędkością q. I nie muszę aktualizować moich informacji, dopóki te założenia się nie zmienią. Znacznie mniej aktualizacji i opóźnień (wciąż stanowiących problem) pojawia się proporcjonalnie rzadziej.

Zamiast więc żądać usługi z drobnym ziarnem na wyższej częstotliwości, spróbuj obniżyć częstotliwość przez:

  1. zmiana żądanych danych i wykorzystanie obliczeń lokalnych
  2. wysyłanie parametrów zapytania lub wyzwalacza w celu odpowiedzi asynchronicznej
  3. żądania wsadowe
  4. przewidywanie wniosków i przygotowywanie odpowiedzi z wyprzedzeniem, w sprawie spekulacji (przeciwieństwo leniwej oceny)
  5. w miarę możliwości unikaj mikrousług wywołujących inne mikrousług; to oczywiście zwiększa problem. Rozumiem, że jest to bodziec do powiększania mikrousług i nieco to przeczy, ale mikrousług nie są przyjacielem opóźnień. Może po prostu przyznaj się i przełam.

Teraz pamiętaj, aby sprawdzić swoje założenia dotyczące systemu. Jeśli bardziej zależy Ci na przepustowości niż na opóźnieniu lub nie masz stanu współużytkowania itp., Skorzystaj z mikrousług tam, gdzie ma to sens. Mówię tylko, że nie używaj ich tam, gdzie nie mają sensu.


1

Twoja naiwna wyobraźnia ma rację. I często to nie ma znaczenia. Nowoczesne maszyny są szybkie. Główne zalety architektury mikrousług widać w pracach rozwojowych i pracach konserwacyjnych oraz czasie.

I oczywiście nie ma reguły mówiącej, że nie możesz używać pamięci współdzielonej ani nawet fizycznie wdrażać wielu usług w jednym pliku wykonywalnym. Tak długo, jak je projektujesz, nie zależy od tego.


Procesory są szybkie. Pamięć jest szybka. Dyski SSD są szybkie. Ale czy karty sieciowe, routery i przełączniki są „szybkie”? Nalega inna odpowiedź, ale nie jestem pewien.
James Mishra

Zdecydowanie łatwo jest napotkać problemy z prędkością sieci. Uruchom jedną usługę w San Francisco, drugą w Amsterdamie i konsumuj je w Sydney. Opóźnienie jest kluczem, a nie przepustowością. Więc nie rób tego. I rób usługi tak duże, jak to ma sens
Stephan Eggermont

1

Jak wspomniało wiele osób, nie chodzi o wąskie gardła w sieci. Chodzi bardziej o kruchość sieci. Pierwszym krokiem jest więc uniknięcie komunikacji synchronicznej. To łatwiejsze niż się wydaje. Wszystko czego potrzebujesz to usługi z odpowiednimi granicami. Właściwe granice powodują, że usługi są autonomiczne, luźno powiązane i bardzo spójne. Dobra usługa nie potrzebuje informacji z innej usługi, już ją ma. Jedynym sposobem, w jaki dobre usługi komunikują się, są zdarzenia. Dobre usługi są w końcu również spójne, więc nie ma transakcji rozproszonych.

Sposobem na osiągnięcie tej dobroci jest najpierw identyfikacja możliwości biznesowych. Możliwości biznesowe to szczególna odpowiedzialność biznesowa. Pewien wkład w ogólną wartość biznesową. Oto moja sekwencja kroków, którą biorę, gdy myślę o granicach systemu:

  1. Zidentyfikuj obowiązki biznesowe wyższego poziomu. Będzie ich kilka. Traktuj te usługi jako kroki, przez które Twoja organizacja powinna przejść, aby osiągnąć swój cel biznesowy.
  2. Sięgnij głębiej w ramach każdej usługi. Zidentyfikuj usługi niższego poziomu obejmujące usługę nadrzędną.
  3. Oprócz pierwszych dwóch punktów pomyśl o komunikacji usługowej. Powinni to robić przede wszystkim za pośrednictwem zdarzeń, aby wzajemnie powiadamiać się o wynikach procesów biznesowych. Zdarzenia nie powinny być traktowane jako przenośniki danych.

Należy pamiętać, że usługa biznesowa obejmuje ludzi, aplikacje, procesy biznesowe. Zwykle tylko część jest reprezentowana jako władza techniczna.

Może to zabrzmieć nieco abstrakcyjnie, więc prawdopodobnie interesujący może być przykład identyfikacji granic usług .


0

To kolejny czynnik, który należy dodać do aktualnych odpowiedzi. Z usługami gruboziarnistymi . Chcesz uniknąć opóźnień we wszystkich połączeniach, więc zamiast wykonywać 10 połączeń, wykonujesz połączenie, które otrzymuje 10 kawałków danych potrzebnych w DTO.

I pamiętaj, że mikrousług nie są tak mikro, jak ludzie myślą.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.