poniedziałek, 3 lutego 2014

Wydajne i skalowalne systemy - wstęp

OK, lecimy dalej :)
Dziś zapoznajmy się z kilkoma terminami, które powinieneś już znać jeśli jesteś na poziomie Competent albo wyższym. Jeśli interesujesz się trochę wydajnością, profilowaniem, tuningiem jvm jak i całego stosu aplikacyjnego to na bank znasz już cały poniższy słownik. Możesz więc zamiast czytać tego posta, po prostu iść na browara. Ale jeśli chcesz utrwalić swoją wiedzę to możesz sobie trochę poczytać.
Na początek podam kilkanaście wydawało by się suchych danych, na które natknąłem się jakiś czas temu na portalu  http://highscalability.com/.
Regularnie tam wchodzę i bardzo polecam to innym. To najlepszy portal o tematyce wydajności rozwiązań oraz skalowalności. Znajdziesz tu np architekturę Facebooka, Twittera , YouTube oraz szeregu innych wielkich produkcji świata IT.
Zunifikujemy sobie  nasz słownik pojęć. Potem go wykorzystamy do tuningu naszych aplikacji. Najważniejsze jest to żeby zacząć coś robić czy zdobywać wiedzę. Bo temat skalowalności oraz wydajności przez skalowalność jest wykurwiście ciekawy.


Zbudujmy sobie mini klaster do ćwiczeń. Wystarczy do tego Tomcat lub JBoss oraz HAProxy lub Apache jak kto woli. Jak nie wiesz jak tego dokonać to dowiesz tego się w następnych postach. Sam możesz zacząć już teraz jak czas żeby coś szperać. Możesz wszystko to  potraktować  jako takie małe ćwiczenie.


Pamiętaj : Zasada n1 jeden -> Zapis jest drogi.
Tradycyjne bazy potrzebują dysków. Dostęp do dysku wymaga wyszukania danego sektora. Przyjmuje się , że trwa to średnio 10ms.
Wynika z tego, że przeciętna maszyna potrafi wykonać 100 seeks/sec maximum
Wynik ten zależy jednak od rozmiaru i kształtu danych do zapisu oraz od typu pracy np batch

Tip : NoSql , chunk processing , batch

Zasada numer 2 -> Odczyt jest tani.
Zwykle odczyty nie potrzebują transakcji, możemy traktować je jako zwarte atomowe operacje. Raz wyszukane dane można cache'ować co bardzo zwiększa  szybko ponownego dostępu do tychże danych. 
Trzymamy się zasady :
  - 250usec for 1MB of data from memory
  - 1s / 250usec = 4GB/sec maximum

Tip :  Spring : @Transactional(readOnly = true)

Teraz trochę ciekawych numerów :
   L1 cache reference 0.5 ns 
   Branch mispredict 5 ns
   L2 cache reference 7 ns
   Mutex lock/unlock 100 ns
   Main memory reference 100 ns
   Compress 1K bytes with Zippy 10,000 ns
   Send 2K bytes over 1 Gbps network 20,000 ns
   Read 1 MB sequentially from memory 250,000 ns
   Round trip within same datacenter 500,000 ns
   Disk seek 10,000,000 ns
   Read 1 MB sequentially from network 10,000,000 ns
   Read 1 MB sequentially from disk 30,000,000 ns
   Send packet CA->Netherlands->CA 150,000,000 ns  

Końcowe wnioski :
 Zapis jest ponad 40 razy bardziej kosztowny niż odczyt
 Globalne dane współdzielone są kosztowne. 
 To jest główne ograniczenie rozproszonych systemów. 
 Występują blokady do współdzielonych zasobów co skutkuje lockami, transakcje stają się wolne.
 Należy optymalizować operację zapisu, i w  miarę możliwości je zrównoleglać.

TIP : MASTER-SLAVE

źródło wiedzy : http://highscalability.com/ (dane i numerologia)

Terminy, które musisz znać : 

źródło wiedzy : JEE design pattern (słownik pojęć)

Rozszerzalność (Extensibility) - często idzie w parze z Elastycznością (Flexibility)

Wymagania stale się zmieniają. Samo życie, ale mamy manifest AGILE.
Z każdą następną wersją pojawiają się nowe błędy i nowe funkcjonalności w systemie.
Rozszerzalność określa  łatwość przeprowadzenia zmian, przewidujemy ją na etapie projektowym.

Trudności w wprowadzania zmian rozszerzalności to:
-obawa że zepsuję inną cześć funkcjonalności nawet pozornie nie powiązanego z obszarem w którym robię zmiany
- wraz z rozbudowa aplikacji wzrasta liczba zależności miedzy modułami oraz tym samym liczba testów, które należy wykonać po każdej modyfikacji

Strach przed wprowadzeniem modyfikacji może paraliżować i aplikacja przestaje rozwijać czyli ewaluować w stronę lepszego.
Sytuacja może być jeszcze gorsza, ponieważ modyfikację jednej aplikacji mogą mieć wpływ na działanie pozostałych. 

Np system bankowy od dziś przelicza wszystkie transakcje tylko w $, jeśli systemy z nim powiązane nie zostaną odpowiednio dobrze przygotowane - powstanie chaos.

TIP : testy, test coverage, TDD

Zmiana interfejsu - modyfikacja nie tylko obiektu, który korzysta z tego interfejsu ale również obiektów z nim powiązanych
(Gwarantowanie zgodności interfejsu)

Rozszerzalność wymaga ścisłego korzystania z interfejsów w celu zagwarantowania, że obiekty współdziałają ze sobą w określony sposób na określonych zasadach.
Komponenty o rozbudowanych interfejsach są bardziej użyteczne niż komponenty o  ograniczonych interfejsach ale rozbudowane interfejsy powodują też ze powiązania miedzy komponentami mogą wydawać się zbyt ścisłe.

Dobrze zaprojektowane interfejsy pozwalają oddzielić implementacje od interakcji. Dzięki czemu implementacja może być niezależna od reszty kodu i tym samym dowolnie modyfikowana, poprawiana lub zastępowana nową wersją.

TIP : DIP, ISP

W ten sposób aplikacja przestaje być całością a staje się kolekcją komponentów w znacznym stopniu niezależnych od siebie.
Wspomaga to też prace w zespole poprzez :
   - używanie scm
   - testability
   - grupowanie w większe byty
   - reużywalność 

Zastosowanie takich reużywalnych komponentów pozwala ograniczyć stopień komplikacji tworzenia aplikacji, zapobiega tworzeniu ścisłych zależności, łatwość rozbudowy.

Koszt rozbudowy oraz aktualizacji (extensibility problem)
  - dług projektowy
  - nawarstwiające się poprawki utrudniają zarządzanie projektem oraz ograniczają możliwości ponownego użycia  jego kodu
- kod nadmiernie rozbudowanych programów jest trudny do zrozumienia
- wprowadzenie DSL  (camel)

Jak poprawić rozszerzalność :
- usuwanie powiązań - loose coupling (testablity, rozbudowa, zrozumienie)
- centralizacja - gromadzimy wspólną funkcjonalność w centralnych zasobach - ułatwia to zrozumienie, aktualizację oraz rozbudowę
- ponowne użycie (reusability)
- hermetyzacja wspólnej funkcjonalności za pomocą komponentów wielokrotnego użycia (umieszczenie zbyt wielu funkcji w jednym komponencie powoduje jego specjalizacje!!)

Skalowalność (scalability) - (własność systemu umożliwiająca obsługę rosnącej ilości pracy lub też łatwość rozbudowy istniejącego systemu w przypadku zwiększenia zapotrzebowania na pewne zasoby)

Skalowalność  jest to także zdolność aplikacji do zachowania wydajności przy jednoczesnym wzroście obsługiwanych żądań. Najlepszym przypadkiem skalowalności jest stały czas odpowiedzi, czyli sytuacja gdy czas przetwarzania zadania nie zależy od bieżącego obciążenia serwera.

W praktyce najlepiej jak aplikacja potrafi zachować w przybliżeniu stały czas odpowiedzi, gdy liczba obsługiwanych żądań zbliżona jest do standardowego obciążenia przewidzianego dla aplikacji.
Np jeśli aplikacja a będzie obsługiwać np  400 żądań na min. Sek to czas obsługi każdego żądania powinien zająć tyle czasu ile obsługa pojedynczego żądania w każdych innych warunkach.

O liniowej skalowalności mówimy wtedy, gdy czas potrzebny do przetworzenia n - żądań jest równy n * czas przetworzenia jednego żądania. Systemy mogą osiągać liniową skalowalność znajdując się pod szczególnie dużym obciążeniem . Jeśli obciążenie serwera osiągnie 400 userów na sek to czas odpowiedzi może wydłużyć się 2x w stosunku do sytuacji gdy jest obsługiwanych 200 userów na sek.

Jeśli system przetwarza żądania - otrzymuje 10 000 żądań i odpowiada na każde z nich  w 3 sek - to powiemy, że system skaluję się poprawnie a nawet wyjątkowo dobrze. Natomiast gdy system ten otrzyma 100 000 i odpowie na każde  z nich w ciągu 3 minut  sytuacja to będzie nie do zaakceptowania.












TIP : Zawsze dąż do liniowej skalowalności













Skalowalność jest kombinacją czynników: stałego czasu odpowiedzi aż do osiągnięcia pewnej liczby obsługiwanych klientów.

Skalowalność często wymaga kompromisu z rozszerzalnością. Czasami rozkład większych komponentów na mniejsze oraz ich powielenie stosowanie do potrzeb, pozwala zwiększyć skalowalność systemu.
Ale też czas komunikacji między komponentami ogranicza skalowalność więc potrzebny jest kompromis.

Wzorce projektowe wspomagają skalowalność umożliwiają efektywne użycie zasobów, tak by obsługa n klientów nie wymagała n jednostek zasobów. Rozszerzają też rozszerzalność aplikacji w celu poprawienia skalowalności.(rozproszenie operacji pomiędzy wiele serwerów, zastąpienie komponentów innymi o większej wydajności, o  lepszych algorytmach lub ułatwiając przeniesienie systemu na wydajniejszy serwer)













   























 




Wyróżniamy typy skalowalności jak :

 - horizonal-out - dodanie nowych węzłów mających taką samą funkcjonalność jak te które używaliśmy  dotychczas
- vertical-up rozbudowa istniejących węzłów poprzez dodanie nowych zasobów np pamięć, procesor itd
Wysoka dostępność (ang. high availability) czyli zdolność do przyjmowania wywołań na dowolnym węźle bez względu na stan pozostałych węzłów, dzięki czemu aplikacja pozostaje dostępna pomimo niedostępności niektórych węzłów. Czyli zapewnienie ciągłości funkcjonalnej systemu przez cały czas.
Użycie klastra pozwala na osiągniecie osiągniecie pewnego rodzaju high availability poprzez dostarczenie dodatkowych nadmiarowych serwerów w klastrze fail-over.
Dostępność liczymy na podstawie wzoru : 100 - (100*D/U) gdzie :
  -  D to unplanned downtime 
  -  U to uptime.


źródło : technews.tmcnet.com








  




























TIP : MapReduce, Spring Batch, Stateless

Reakcja na błędy (fail-over) – poprawna reakcja na uszkodzenie węzła,
polegająca na automatycznym przekierowaniu wywołań do innych sprawnych węzłów. Działanie to jest przezroczyste dla końcowego użytkownika.
Wymagania dwie lub więcej maszyn w klastrze.

Niezawodność (Availability, Robustness, Fault Tolerance and Reliability)
Oprogramowanie powinno działać zawsze w sposób przewidywalny .
Jest zawsze dostępne dla użytkownika (availability, fault tolerance)
Wprowadzenie tych samych danych powoduje uzyskanie tych samych wyników  (thread safe, robustness)

Jeśli któryś z komponentów nie działa poprawnie w ramach systemu , a użytkownik nie może skorzystać z aplikacji uznajemy system jako zawodny.

TIP : Zapewnienie jakości oprogramowania (wydzielić osobę z zespołu programistów) , audyt

Terminowość :
to terminowe dostarczenie gotowego - działającego produktu.

Np łatwo rozbudowany system może również lepiej skalować się poprzez dołączenie komponentów o większej wydajności, czasem czas poświęcony na zaimplementowanie obsługi skalowalności ułatwi dodatkowo terminowe opracowanie kolejnych wersji aplikacji.
Zastosowanie wzorców projektowych umożliwi poprawę efektywności aplikacji w 4 wyżej wymieniowych obszarach.

Tip : Oszczędności : zastosowanie wzorców projektowych, modularność, łatwość rozbudowy
Równoważenie obciążenia (ang. load balancing) - zapewnienie efektywnego
przetwarzanie wywołań poprzez ich rozdzielenie pomiędzy dostępne w klastrze
węzły. Jednocześnie fakt istnienia mutliplexingu jest ukryty przez końcowym klientem. Możemy użyć load balancingu bezstanowego (REST , WS) lub stanowego wymagającego współdzielenia sesji HTTP. Istnieje także wiele algorytmów rozdzielania żądań jak  np round robin. Pomaga osiągnąć high availability.

Cluster -  składa się z dwóch lub większej ilości serwerów połączonych ze sobą fizycznie siecią. Jest przezroczysty dla końcowego użytkownika. Jego zadaniem jest zazwyczaj zwiększanie wydajności i dostępności systemu. Koszt skalowania horyzontalnego w celu osiągnięcia tej samej wydajności jak w skalowaniu wertykalnym powinien być niższy.
Wyróżniamy tryby pracy jak  active-active (A/A) i  active-passive(A/P).
W A/A wszystkie maszyny pracują jednocześnie.
W A/P  inne maszyny przejmują zadania gdy jakaś maszyna aktywna ulegnie awarii.

Cache - przechowywanie danych najczęściej w pamięci w celu szybkiego serwowania ich do końcowego klienta. Dane najlepiej pasujące do tego schematu to dane ciężkie lub dane których generacja wymaga znacznych nakładów procesora.
Jeśli to są dane typu READ_ONLY wtedy mamy do czynienia z sytuacją idealną odnoście ich cache'owania.















Heterogeniczność węzłów (nodes heterogeneity) – możliwość dodawania do klastra węzłów bazujących na różnych systemach czy platformach sprzętowych

Dynamiczna rekonfiguracja (dynamic reconfiguration) – dodajemy, usuwamy, powielamy węzły  bez potrzeby chociaż chwilowego przerywania pracy całego systemu.

Tip : Zookeeper

Przepustowość (throughput) - ilość jednostek pracy (transakcji) jaką system może wykonać w zadanym okresie czasu

Opóźnienie (Latency) - minimalny czas wymagany do otrzymania jakiejkolwiek odpowiedzi z systemu na dane żądanie.

Ilość transakcji na sekundę (TPS, transactions per second) - jest to ilość przetworzonych transakcji systemu, które to wystąpiły w ciągu jednej sekundy.


Wykorzystanie zasobów (Utilization) - stosunek procentowy dostępnych zasobów, które system wykorzystuje do przetwarzania procesów. Nie wliczamy do tego zasobów będących w spoczynku (idle resource)


Przepustowosc systemu na sekunde DTPS (data throughput per second) - to wartość oznaczająca ilość danych jaka system przyjmuje do przetworzenia w ciągu sekundy. Jest to ilość danych zapisanych w każdej operacji zakończonej w jedno sekundowym przedziale.

Efektywność (Efficiency) - stosunek przepustowości do wykorzystanych zasobów

Pojemność (Capacity) - największa dopuszczalna wartość przepustowości lub obciążenia, które mogą być przyjmowane przez systemem w danej chwili.

Czas odpowiedzi (Response time) - ilość czasu potrzebna systemowi na udzielenie odpowiedzi na dane żądanie
Czas reakcji (Responsiveness) - szybkość z jaką system potwierdza przyjęcie żądania. Uwaga proces niezależny od jego przetworzenia. (Ważne w UI)
Zależność pomiędzy średnim obciążeniem, a szybkością generowania odpowiedzi (responsiveness) zależy często od rodzajów procesów oczekujących (priorytety).

Obciążenie (Load) - liczna jednocześnie podłączonych klientów

Wąskie gardło (bootleneck) - jest to odcinek drogi o przepustowości mniejszej niż pozostałych odcinków trasy czy danego przepływu powodujący znaczący spadek wydajności.

TCO (Total Cost of Ownership) - koszt wyrażony czasem pracy programisty niezbędnym do wprowadzenia danej poprawki , przekracza wskaźnik całkowitego kosztu pozyskania sprzętu, który zapewni podobny wzrost wydajności.

Profilowanie kodu (code profiling) - rejestrowanie w zadanym czasie informacji i markerów na temat przetwarzani i działania procesów programu. Powstałe dane zgromadzone są w celu późniejszej analizy i eliminacji ewentualnych wąskich gardeł.



Wrażliwość na obciążenie (Load sensivity) - stosunek czasu odpowiedzi, latencji lub przepustowości do wzrostu obciążenia. Zwany jest też degradacją systemu (Degradation)

Mediana (Median) - możemy ją traktować jako średnią artmetyczną bardziej odporną za wszelkiego rodzaju zakłócenia lub odchylenia.

Wydajność: (moje stare notatki)











Co należy mierzyć i na co zwracać uwagę:









































































Jeśli używasz springa : 








  
Popraw wydajności swojej aplikacji web :




Przedstawiałem Wam dzisiaj problem wydajności i skalowalności w pigułce.
Warto zapoznać się dobrze z powyższymi pojęciami aby przejść dalej i ewoluować w kierunku zwiększania poprawności oraz jakości swoich systemów.

next ->



Brak komentarzy:

Prześlij komentarz