ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 149
Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 36
Abonament PDF

Fault-Tolerant Microservices cu Netflix Hystrix

Radu Butnaru
Senior Developer @ SDL



PROGRAMARE

Acest articol continuă seria destinată soluțiilor aplicate într-un sistem construit folosind o arhitectură bazată pe Microservicii. Articolul precedent a tratat (Micro)service Discovery cu Netflix Eureka. Articolul curent prezintă biblioteca Java Hystrix, dezvoltată în regim open-source de către compania Netflix. Hystrix oferă o implementare matură a pattern-ului Circuit Breaker, al cărui scop este să reducă impactul defectărilor și al timpilor mari de latență în sisteme distribuite.

Problema

O caracteristică principală a sistemelor construite pe bază de microservicii este utilizarea unui număr mare de componente distribuite. Pe măsură ce numărul de interacțiuni sincrone prin rețea crește, impactul unei defectări a unui serviciu poate deveni din ce în ce mai sever.

Enumerăm câteva din cazurile cele mai frecvente de comportament anormal ale unui serviciu:

Fără mecanisme de protecție, erorile și în mod special timpii mari de latență se vor propaga către clienții serviciului, unde se va putea ajunge în situația ca resurse de sistem limitate să fie epuizate (de exemplu, pool-ul de thread-uri al serverului web). Prin escaladarea erorilor, disponibilitatea (engl. availability) sistemului este afectată în mod semnificativ: întregul sistem poate deveni indisponibil din cauzei unei singure dependențe defecte, deși restul serviciilor de care depinde, funcționează corect.

Soluția

Un Circuit Breaker (rom. Întrerupător de circuit) este folosit pentru a intermedia operațiile prin rețea dintre un client și un serviciu. Circuit Breaker-ul monitorizează și detectează când serviciul invocat se comportă anormal, respingând apelurile către serviciu până când acesta va deveni funcțional din nou. Întorcând o eroare imediată, se previne epuizarea resurselor din procesul client. În același timp, se reduce încărcarea serviciului invocat, crescând astfel șansele ca acesta să își revină din condiția defectă.

În secțiunile care urmează, vom analiza implementarea pattern-ului Circuit Breaker din biblioteca Hystrix.

Circuit Breaker-ul Hystrix

Să presupunem că un client invocă un serviciu. Clientul va izola toate punctele de acces către serviciu prin efectuarea tuturor apelurilor prin intermediul unui Circuit Breaker (aceasta se realizează la nivel de cod prin extinderea de clase Hystrix sau prin adnotări - detalii în cele ce urmează). Circuit Breaker-ul va intercepta și monitoriza toate apelurile și va acționa în cazul unor condiții de eroare, efectuând tranzițiile de stare descrise mai jos.

Starea Închis

În cazul de funcționare normal când nu există condiții de eroare, Circuit Breaker-ul este în starea închis. Toate apelurile sunt transmise în mod transparent către serviciu.

Starea Deschis

Circuit Breaker-ul consideră următoarele condiții drept simptome ale unei defectări și le va lua în calcul pentru a decide întreruperea circuitului:

Circuitul este întrerupt de îndată ce Hystrix determină că pragul de erori de pe parcursul unei ferestre de timp statistice a fost atins ( implicit, 50% erori pe parcursul unei perioade de timp de 10 secunde). În starea deschis, Circuit Breaker-ul va respinge apeluri prin:

Starea Semi-deschis

Pentru a permite recuperarea din condiția de eroare, atunci când Circuit Breaker-ul se află în starea deschis, el va permite în mod periodic câte un apel, la un interval configurabil (implicit, 5 secunde) - aceasta este starea semi-deschis. Dacă apelul se efectuează cu succes, circuitul se va închide din nou.

Folosire

Vom prezenta două modalități de a integra biblioteca Hystrix în proiecte:

  1. Direct folosind API-ul Hystrix - necesită implementarea și invocarea de comenzi Hystrix pentru fiecare apel de serviciu.
  2. Folosind bibliotecile Spring Cloud Netflix și Javanica - o modalitate de a folosi Hystrix cu impact mai redus asupra codului de proiect, prin adnotarea metodelor ce apelează servicii.

API Hystrix direct

Pentru a folosi biblioteca Hystrix, trebuie adăugată următoare dependință în proiectul Maven:

<dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
    <version>1.3.20</version>
</dependency>

Pentru a proteja un apel de serviciu cu un Circuit Breaker, trebuie extinsă clasa HystrixCommand. Exemplul fictiv de mai jos apelează un serviciu de produse:

public class FindAllProductsCommand extends HystrixCommand> {

    private RestTemplate restTemplate;

    public FindAllProductsCommand(
           RestTemplate restTemplate) {

      super(HystrixCommandGroupKey.Factory
        .asKey("ProductGroup"));

      this.restTemplate = restTemplate;
    }

    @Override
    protected List run() throws Exception {
        // Apel serviciu HTTP
      ResponseEntity responseEntity = 
          restTemplate.getForEntity(
          "http://host/products", Product[].class);

      Product[] products = responseEntity.getBody();
      return Arrays.asList(products);
    } 
}

Pentru a invoca clasa comandă, ea trebuie instanțiată și apoi apelată metoda execute():

new FindAllProductsCommand(productService).execute();

Pentru a întoarce un rezultat predefinit în locul unei excepții atunci când Circuit Breaker-ul este deschis, suprascriem metoda getFallback() în implementarea comenzii:

 public class FindAllProductsCommand extends HystrixCommand> {
     ...
     @Override
     protected List getFallback() {
         return Collections.emptyList();
     }
 }

În cazul în care un anumit tip de eroare este considerat comportament așteptat/tratabil (de ex. validări de logică business), tipul excepției întoarse trebuie să fie HystrixBadRequestException. În caz contrar, excepția va fi tratată ca simptom al unui comportament defectuos.

public class FindAllProductsCommand extends HystrixCommand> {
...
    @Override
    protected List run() throws Exception {
        try {
            // Apel serviciu HTTP
            ...
        } catch (IllegalArgumentException e) {
    // Dacă se întoarce HystrixBadRequestException, 
    // Circuit Breaker-ul nu se va deschide

       throw new HystrixBadRequestException(
         "Bad request.", e);
        }
     }
 }

Valorile specifice de configurări (timpi de expirare, capacitatea pool-ului de thread-uri, praguri procentuale de eroare, etc.), pot fi atribuite programatic la momentul instanțierii comenzii.

new FindAllProductsCommand(HystrixCommand.Setter.
    withGroupKey(HystrixCommandGroupKey.Factory
     .asKey("ProductGroup")).
    andCommandPropertiesDefaults(
     HystrixCommandProperties.Setter()
     .withCircuitBreakerRequestVolumeThreshold(20)
     .withCircuitBreakerErrorThresholdPercentage(50)
     .withExecutionIsolationThreadTimeout-
        InMilliseconds(1000)
     .withMetricsRollingStatisticalWindow
        InMilliseconds(10000)
     .withMetricsRollingStatistical
        WindowBuckets(10))
     .andThreadPoolPropertiesDefaults(
        HystrixThreadPoolProperties.Setter()
     .withCoreSize(10)), restTemplate)
     .execute();

Alternativ, pentru configurări se poate folosi biblioteca Netflix Archaius.

Spring Cloud Netflix / Javanica

Biblioteca Spring Cloud a fost prezentată în articolul precedent din serie. Spring Cloud este construită pe baza Spring Boot și furnizează interfețe abstracte pentru tehnologia din stiva open-source Netflix. Suportul pentru Hystrix se bazează pe biblioteca third-party Javanica.

Pentru a utiliza suportul Spring Cloud Netflix / Javanica, trebuie adăugată următoarea dependință în proiectul Maven:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.0.0.RELEASE</version>
</dependency>

În plus, adăugăm adnotarea EnableCircuitBreaker pe clasa principală de configurare Spring Boot:

@EnableCircuitBreaker
public class HystrixClientDemoApp {
...
}

Pentru a proteja un apel de serviciu cu un Circuit Breaker, este suficient să se adauge adnotarea HystrixCommand pe metoda respectivă:

@HystrixCommand
public List findAllProducts() {
     // Apel serviciu HTTP
    ResponseEntity responseEntity = restTemplate.getForEntity("http://host/products", Product[].class);
    Product[] products = responseEntity.getBody();
    return Arrays.asList(products);
}

Pentru a întoarce un rezultat predefinit în locul unei excepții atunci când Circuit Breaker-ul este deschis, trebuie menționată metoda de fallback în adnotare:

@HystrixCommand(fallbackMethod = "defaultProducts")
public List findAllProducts() {
     // Apel serviciu HTTP
     ...
}

public List defaultProducts() {
     return Collections.emptyList();
} 

Dacă se dorește ca un anumit tip de excepție să nu fie considerat simptom al unei defectări, tipul excepției trebuie menționat în adnotare:

@HystrixCommand(ignoreExceptions = {IllegalArgumentException.class})
public List findAllProducts() {
     // Apel serviciu HTTP
     ...
}

Pentru configurări (timpi de expirare, capacitatea pool-ului de thread-uri, praguri procentuale de eroare, etc.), se poate utiliza mecanismul standard Spring Boot de configurare în fișierul application.yml:

hystrix:
    command:
        findAllProducts:
            execution:
                isolation:
                    thread:
                        timeoutInMilliseconds: 1000
            circuitBreaker:
                requestVolumeThreshold: 20
                errorThresholdPercentage: 50
            metrics:
                rollingStats:
                    timeInMilliseconds: 10000
                    numBuckets: 10
    threadpool:
        ProductService:
            coreSize: 10

Monitorizare cu Hystrix Dashboard / Turbine

Hystrix oferă suport pentru vizualizarea și monitorizarea stării curente a Circuit Breaker-elor prin trimiterea continuă de măsurători către o aplicație web tip panou de comandă: Hystrix Dashboard. Pentru scenarii cu servere multiple (cluster) Hystrix oferă posibilitatea de a trimite măsurătorile unui agregator intermediar: Turbine, înainte ca acestea să ajungă la Hystrix Dashboard.

Capturile de ecran de mai jos prezintă Hystrix Dashboard:

Circuit Breaker Închis

Circuit Breaker Deschis

Următoarele măsurători sunt arătate și actualizate în timp real:

Pe wiki-ul Hystrix Dashboard se poate consulta documentația necesară interpretării diagramelor și contoarelor.

Concluzie

Netflix Hystrix este o implementare matură a pattern-ului Circuit Breaker, configurabilă în detaliu, cu suport solid pentru vizualizare și monitorizare. Bibliotecile Spring Cloud Netflix/Javanica oferă o alternativă de folosire bazată pe adnotări, cu un impact mai redus asupra codului de proiect.

Bibliografie

  1. Pattern-ul Circuit Breaker- autor Martin Fowler
  2. Proiectul Hystrix
  3. Wiki-ul Hystrix
  4. Proiectul Spring Cloud Netflixh
  5. Proiectul Javanica
  6. Proiectul Hystrix Dashboard
  7. Wiki-ul Hystrix Dashboard
  8. Proiectul Turbine
  9. Proiectul Archaius
  10. Prezentare Hystrix JavaOne- autor Ben Christensen

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects