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:
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.
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?
źródło
Odpowiedzi:
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.
źródło
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)
źródło
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ł.
źródło
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:
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.
źródło
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.
źródło
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:
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 .
źródło
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ą.
źródło