Haladó Olvasási idő: ~9 perc

Spring Cloud

config server, service discovery, load balancing

Spring Cloud

A Spring Cloud arra való, hogy a microservice rendszer ne csak elinduljon, hanem központilag konfigurálható, felfedezhető és stabilan kommunikáló ökoszisztémává álljon össze.

1. Definíció / Definition

Mi ez? / What is it?

A Spring Cloud a Spring ökoszisztéma azon része, ami a distributed system problémáira ad kész building blockokat. Nem egyetlen library, hanem több komponens együttese: konfigurációkezelés, service discovery, client-side load balancing, API gateway, messaging-alapú refresh, circuit breaker és egyéb cloud-native minták. A cél nem az, hogy eltüntesse a microservice komplexitást, hanem hogy standardizálja.

Miért létezik? / Why does it exist?

Amint több Spring Boot service-ed lesz, nagyon gyorsan előjönnek ugyanazok a fájdalmak: hol van a közös config, hogyan találják meg egymást a service-ek, hogyan osszuk szét a kéréseket több példány között, hogyan frissüljenek a beállítások új deploy nélkül. Ezeket kézzel is meg lehet oldani, de akkor minden csapat saját mini platformot épít. A Spring Cloud azért létezik, hogy ezekre legyen konvenció és kipróbált integráció.

Hol helyezkedik el? / Where does it fit?

A Spring Boot fölött ülő infrastruktúra-rétegként érdemes rá gondolni. Az üzleti logikát nem váltja ki, viszont erősíti az application környezetet: honnan jön a config, milyen néven regisztrál a service, hogyan hív meg egy másik service-et név alapján, és hogyan propagálódik egy config változás a rendszerben.

2. Alapfogalmak / Core Concepts

2.1 Config Server

A Spring Cloud Config Server központi konfigurációforrás. A service-ek nem saját application.yml másolatokból élnek, hanem egy távoli repositoryból, tipikusan Gitből vagy Vaultból olvassák a konfigurációt.

Tipikus konfigurációs folyamat:

  1. A Git vagy Vault tárolja a konfiguráció forrását.
  2. A Config Server HTTP-n elérhetővé teszi ezeket az értékeket.
  3. A Config Clientek induláskor vagy frissítéskor betöltik a távoli property-ket.
Elem Szerep Tipikus előny
Config Server Központi config endpoint Egységes forrás
Git backend Verziózott property store Audit, rollback
Vault backend Secret storage Biztonságos titokkezelés
Config Client Távoli property betöltése Környezetfüggetlen deploy

Git-backed confignál a legnagyobb erő a versioning. Ha egy property elromlik productionben, vissza tudsz térni egy korábbi commitra. Vault-backed confignál az a fontos, hogy a secret ne kódban és ne sima Git repóban éljen.

2.2 Bootstrap context vs application context

Történelmileg a Spring Cloud a bootstrap context segítségével töltötte be a távoli konfigurációt az application context előtt. Ez azért volt fontos, mert egyes property-knek már a startup legelején rendelkezésre kellett állniuk. Újabb Spring Boot/Spring Cloud verziókban a Config Data API miatt ez a működés részben átalakult, de interjúban és legacy rendszereknél továbbra is előjön a bootstrap mental model.

Startup sorrend röviden:

  • Bootstrap context — korán betölti a távoli konfigurációt, így a kritikus property-k időben elérhetők.
  • Application context — létrehozza a Spring beaneket és wiringot a már feloldott konfiguráció alapján.

A lényeg: bizonyos konfigurációk korábban érkeznek, mint a normál bean inicializáció. Ha ezt nem érted, nehéz megmagyarázni, miért nem frissül vagy miért nem látszik egy property ott, ahol várod.

2.3 Service discovery Eureka-val

A Netflix Eureka Server registryként működik. A service példányok beregisztrálnak, heartbeatet küldenek, majd más service-ek név alapján kérdezik le őket.

Tipikus discovery ciklus:

  1. A payment-service és az order-service beregisztrál a Eureka Serverbe.
  2. A példányok heartbeat üzenetekkel életben tartják a lease-t.
  3. A hívó service név alapján kérdezi le a cél példányait a registryből.

Fő fogalmak:

  • registration: a service elmondja, hogy létezik és hol érhető el;
  • heartbeat/renewal: jelzi, hogy még életben van;
  • lease expiration: ha nincs heartbeat, a registry idővel kidobja;
  • discovery client: a fogyasztó service lekéri az instance-okat.

2.4 Client-side load balancing

Régebben Ribbon volt a klasszikus megoldás, ma Spring Cloud LoadBalancer az ajánlott út. A lényeg ugyanaz: a kliens kap egy listát az elérhető példányokról, és ő választ közülük.

Megoldás Állapot Lényeg
Ribbon Legacy Netflix stack része volt
Spring Cloud LoadBalancer Jelenlegi ajánlott Egyszerű, Spring-be jól illeszkedik
@LoadBalanced RestTemplate Kényelmi integráció Service név alapú hívás

Ez eltér a server-side load balancingtől, ahol egy külső proxy vagy ingress osztja el a forgalmat. Itt maga az alkalmazás tud a registryről és a példányokról.

2.5 `@EnableDiscoveryClient`, `@LoadBalanced`, `@RefreshScope`

  • @EnableDiscoveryClient: explicit módon jelzi, hogy az app discovery kliensként működjön. Sok modern setupban auto-configuration miatt már nem mindig kötelező, de a koncepció fontos.
  • @LoadBalanced RestTemplate: a http://inventory-service/api/items típusú URI-kat feloldja valós instance-okra.
  • @RefreshScope: újratölthető bean scope. Ha config frissül és refresh történik, a bean új példányt kaphat az új property-kkel.

2.6 Spring Cloud Bus

A Config Server önmagában nem pusholja ki automatikusan a változást minden service-nek. A Spring Cloud Bus tipikusan RabbitMQ vagy Kafka fölött broadcastolja a refresh eseményt.

Frissítési propagáció:

  1. Egy konfigurációváltozás bekerül a központi forrásba.
  2. A Spring Cloud Bus refresh eseményt terít a brokeren keresztül.
  3. Az érintett service-ek újratöltik a friss konfigurációt.

Ez különösen akkor hasznos, ha 30-40 service-et nem akarsz külön manuálisan frissíteni.

3. Gyakorlati használat / Practical Usage

Egy tipikus production rendszerben van config-repo, config-server, eureka-server, majd több domain service: order-service, payment-service, inventory-service. A service induláskor először lehúzza a saját profil szerinti konfigurációját, például order-service-prod.yml, utána beregisztrál a Eurekába. Ha az order-service meg akarja hívni az inventory-service-t, nem fix hostot használ, hanem service nevet.

Ez nagy előny konténeres környezetben, ahol az instance-ok jönnek-mennek. Kubernetes sok dolgot natívan megold, de sok szervezetnél még ma is vannak VM-es vagy vegyes környezetek, ahol Eureka és Config Server teljesen legitim platformelem.

Jó use case a feature flag vagy rate-limit konfiguráció központi kezelése. Például egy fraud thresholdot a config repóban módosítasz, a Config Server kiadja az új értéket, majd Bus segítségével a releváns service-ek refreshelik az új beállítást. Nem kell új image-et építeni csak egy property miatt.

Másik reális példa a blue-green vagy canary környezet. Ugyanaz a service különböző profilokkal futhat: staging, prod, prod-eu. A konfigurációk központilag és verziózottan maradnak, ami audit és compliance szempontból is sokkal jobb, mint szerverenként szerkesztett property fájlok.

4. Kód példák / Code Examples

4.1 Config Server

@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServerApplication.class, args);
    }
}
server:
  port: 8888
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/acme/platform-config
          default-label: main

4.2 Config Client + `@RefreshScope`

@RestController
@RefreshScope
class PricingController {

    @Value("${pricing.discount-percentage:0}")
    private BigDecimal discountPercentage;

    @GetMapping("/pricing/discount")
    public Map<String, BigDecimal> currentDiscount() {
        return Map.of("discountPercentage", discountPercentage);
    }
}
spring:
  application:
    name: pricing-service
  config:
    import: optional:configserver:http://localhost:8888
management:
  endpoints:
    web:
      exposure:
        include: health,info,refresh,busrefresh

4.3 Eureka Server és kliens

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(DiscoveryServerApplication.class, args);
    }
}
@SpringBootApplication
@EnableDiscoveryClient
public class InventoryApplication {

    public static void main(String[] args) {
        SpringApplication.run(InventoryApplication.class, args);
    }
}

4.4 `@LoadBalanced RestTemplate`

@Configuration
class HttpClientConfig {

    @Bean
    @LoadBalanced
    RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder.build();
    }
}

@Service
class InventoryGateway {
    private final RestTemplate restTemplate;

    InventoryGateway(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public InventoryResponse getInventory(String sku) {
        return restTemplate.getForObject(
                "http://inventory-service/api/inventory/{sku}",
                InventoryResponse.class,
                sku
        );
    }
}

record InventoryResponse(String sku, int available) {}

4.5 Bus refresh endpoint

@RestController
class RefreshAdminController {

    private final ApplicationEventPublisher publisher;

    RefreshAdminController(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @PostMapping("/internal/config/refresh")
    public ResponseEntity<Void> refreshAll() {
        publisher.publishEvent(new RefreshRemoteApplicationEvent(this, null, null));
        return ResponseEntity.accepted().build();
    }
}

5. Trade-offok / Trade-offs

Előnyök

  • központi konfiguráció és jobb operációs kontroll;
  • service discovery miatt kisebb hostnév-függőség;
  • dinamikus skálázódásnál egyszerűbb service-to-service kommunikáció;
  • refresh és bus miatt kisebb konfigurációs deploy kényszer.

Hátrányok

  • több mozgó alkatrész: Config Server, registry, message bus;
  • bonyolultabb startup sorrend és hibaelemzés;
  • könnyű túlhasználni ott is, ahol platform már ad natív alternatívát;
  • a runtime mágikusabbnak tűnik, ezért erősebb platformismeret kell.

Mikor használd? Ha több service-ed van, több környezeted, központi config igényed és változó infrastruktúrád.

Mikor ne? Ha egyetlen egyszerű monolitod van, vagy Kubernetes natív service discovery/config megoldásai teljesen lefedik az igényeidet, és csak felesleges plusz réteget adnál hozzá.

6. Gyakori hibák / Common Mistakes

6.1 Secret Gitben tárolása

A Config Server Git backend nagyon kényelmes, de sok csapat elkezd jelszót, tokeneket, private key-t is ugyanabba a repóba tenni. Erre jobb Vault vagy legalább titkosított megoldás.

6.2 `@RefreshScope` félreértése

A @RefreshScope nem varázspálca. Nem minden bean viselkedik jól refresh után, és nem oldja meg a már cache-elt vagy constructorba egyszer beégetett állapotot. Ha egy config kritikus, tervezz explicit újraolvasható szolgáltatást vagy refresh-aware komponenst.

6.3 Túl rövid Eureka timeoutok

Ha agresszíven állítod a lease intervallumokat, instabil hálózatnál a registry túl hamar kidobhat egészséges példányokat. Ez flappinget okoz, ami még rosszabb terheléselosztást ad.

6.4 Service discovery mindenre

Sok rendszerben külső SaaS endpointokra, adatbázisokra vagy fix infrastruktúra elemekre is service discovery logikát próbálnak ráhúzni. Nem kell mindent registryben keresni. A discovery service-to-service dinamikus endpointokra való.

6.5 Bootstrap és Config Data keverése

Legacy példákban bootstrap property-ket látsz, új projektekben spring.config.import alapú megoldást. Ha a csapat nem egységes, könnyen félrenéztek egy property loading problémát.

7. Senior szintű meglátások / Senior-level Insights

A Spring Cloud nem cél, hanem platformdöntés. Senior szinten az a kérdés, hogy mely problémát oldod meg vele, és melyiket oldja meg helyetted már a futtatási platform. Kubernetes mellett például service discovery és config sokszor részben natív, de a Spring Cloud ökoszisztéma még így is adhat értéket a kliensoldali integrációk, refresh modellek vagy circuit breaker absztrakciók miatt.

A Config Servernél a legfontosabb production wisdom: a konfiguráció is release artifact. Kódreviewzd, verziózd, legyen rollback stratégiád, és tudd, hogy egy config change ugyanúgy okozhat incidest, mint egy rossz deploy.

Eureka esetén figyelj arra, hogy a registry eventual consistency jellegű. Nem szabad úgy gondolni rá, mintha tranzakcionális service inventory lenne. Rövid ideig lehetnek régi instance információk, ezért a kliens oldali retry, timeout és resilience ugyanúgy fontos marad.

A refresh eseményeknél mindig mérlegeld a blast radiust. Jó dolog, hogy egy bus refresh sok service-et átállít, de ez egyben kockázat is. Nem minden property-t érdemes runtime frissíthetővé tenni. Egyes változásoknál biztonságosabb a kontrollált rollout.

8. Szószedet / Glossary

  • Config Server: központi szolgáltatás, amely távoli konfigurációt szolgál ki.
  • Config Client: alkalmazás, amely induláskor vagy refreshkor konfigurációt kér le.
  • Eureka: service registry és discovery megoldás.
  • Heartbeat: periodikus életjel a registry felé.
  • Client-side load balancing: a kliens választ példányt a rendelkezésre álló instance-ok közül.
  • @RefreshScope: Spring scope, amely refresh után újrainicializálhat beaneket.
  • Bootstrap context: korai context a távoli konfiguráció betöltéséhez.
  • Spring Cloud Bus: üzenetalapú mechanizmus config refresh és események terítésére.

9. Gyorsreferencia / Cheatsheet

Téma Mire való? Fontos annotáció / elem Tipikus buktató
Config Server Központi config @EnableConfigServer Secret Gitben
Config Client Távoli property betöltés spring.config.import Rossz betöltési sorrend
Eureka Service discovery @EnableDiscoveryClient Flapping timeoutok
LoadBalancer Példányválasztás @LoadBalanced Ne keverd fix URL-ekkel
Refresh Runtime config update @RefreshScope Stateful beanek problémái
Bus Tömeges refresh busrefresh endpoint Túl nagy blast radius

🎮 Játékok

8 kérdés