Tavaszi felhő - Bootstrapping

1. Áttekintés

A Spring Cloud egy keretrendszer robusztus felhőalkalmazások építéséhez. A keretrendszer megkönnyíti az alkalmazások fejlesztését azáltal, hogy megoldásokat kínál az elosztott környezetbe való áttérés során felmerülő gyakori problémákra.

A mikroszolgáltatások architektúrájával futó alkalmazások célja a fejlesztés, a telepítés és a karbantartás egyszerűsítése. Az alkalmazás bomlott jellege lehetővé teszi a fejlesztők számára, hogy egyszerre egyetlen problémára koncentráljanak. A fejlesztések bevezethetők anélkül, hogy a rendszer más részeire hatással lennének.

Másrészt különböző kihívások merülnek fel, ha mikroszolgáltatási megközelítést alkalmazunk:

  • A konfiguráció külsővé tétele oly módon, hogy rugalmas legyen, és a változtatáshoz ne legyen szükség a szolgáltatás újjáépítésére
  • Szolgáltatás felfedezése
  • A különböző gazdagépeken telepített szolgáltatások összetettségének elrejtése

Ebben a cikkben öt mikroszolgáltatást építünk fel: egy konfigurációs kiszolgálót, egy felfedező kiszolgálót, egy átjárókiszolgálót, egy könyvszolgáltatást és végül egy minősítési szolgáltatást. Ez az öt mikroszolgáltatás szilárd alapalkalmazást jelent a felhő fejlesztésének megkezdéséhez és a fent említett kihívások kezeléséhez.

2. Config Server

A felhőalkalmazás fejlesztésekor egyik kérdés a konfiguráció fenntartása és terjesztése szolgáltatásaink számára. Tényleg nem akarunk időt tölteni az egyes környezetek konfigurálásával, mielőtt vízszintesen méreteznénk szolgáltatásainkat, vagy a biztonsági megsértéseket kockáztatjuk azzal, hogy az alkalmazásunkba sütjük a konfigurációnkat.

Ennek megoldása érdekében összes konfigurációnkat egyetlen Git-tárba tömörítjük, és összekapcsoljuk azt egy olyan alkalmazással, amely az összes alkalmazásunk konfigurációját kezeli. Nagyon egyszerű megvalósítást fogunk létrehozni.

További részletek és egy összetettebb példa megtekintéséhez olvassa el a Spring Cloud Configuration cikkünket.

2.1. Beállít

Navigáljon ide //start.spring.io és válassza a Maven and Spring Boot 2.2.x elemet.

Állítsa a műtárgyat a „config. A függőségek részben keresse meg a „config server” kifejezést, és adja hozzá azt a modult. Ezután nyomja meg a gombot generál gombra, és letölthetünk egy zip fájlt, benne egy előre konfigurált projekttel és indulásra készen.

Alternatív megoldásként létrehozhatunk egy Tavaszi csizma projektet, és manuálisan adjon hozzá néhány függőséget a POM fájlhoz.

Ezeket a függőségeket megosztják az összes projekt között:

 org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-dependencies Hoxton.SR4 pom import org.springframework.boot spring-boot-maven-plugin 

Adjunk hozzá egy függőséget a konfigurációs kiszolgálóhoz:

 org.springframework.cloud spring-cloud-config-server 

Referenciaként megtalálhatjuk a Maven Central legújabb verzióját (tavasz-felhő-függőségek, teszt, config-szerver).

2.2. Tavaszi konfigur

A konfigurációs kiszolgáló engedélyezéséhez néhány megjegyzést hozzá kell adnunk a fő alkalmazásosztályhoz:

@SpringBootApplication @EnableConfigServer nyilvános osztály ConfigApplication {...}

@EnableConfigServer alkalmazásunkat konfigurációs szerverré változtatja.

2.3. Tulajdonságok

Tegyük hozzá a alkalmazás.tulajdonságok ban ben src / main / resources:

server.port = 8081 spring.application.name = config spring.cloud.config.server.git.uri = fájl: // $ {user.home} / application-config

A konfigurációs kiszolgáló legjelentősebb beállítása a git.uri paraméter. Ez jelenleg egy relatív fájl elérési útra van állítva, amely általában feloldódik c: \ Felhasználók \ {felhasználónév} \ Windows rendszeren vagy / Felhasználók / {felhasználónév} / on * nix. Ez a tulajdonság egy Git-tárhelyre mutat, ahol az összes többi alkalmazás tulajdonságfájljai vannak tárolva. Szükség esetén abszolút fájl elérési útra állítható.

Tipp: Windows gépen az értéket a „file: ///” elé írja be, a * nix mezőben használja a „file: //” parancsot.

2.4. Git Repository

Keresse meg a mappát, amelyet spring.cloud.config.server.git.uri és adja hozzá a mappát application-config. CD-t ebbe a mappába, és írja be git init. Ez inicializálja a Git adattárat, ahol fájlokat tárolhatunk és nyomon követhetjük azok változását.

2.5. Fuss

Futtassuk a config szervert és ellenőrizzük, hogy működik-e. A parancssori típusból mvn spring-boot: fut. Ez elindítja a szervert.

Ezt a kimenetet kell látnunk, jelezve, hogy a szerver fut:

A Tomcat a következő port (ok) on indult: 8081 (http)

2.6. Bootstrapping konfiguráció

A következő szervereinken azt akarjuk, hogy az alkalmazás tulajdonságait ez a konfigurációs kiszolgáló kezelje. Ehhez valójában egy kis csirkét és tojást kell csinálnunk: Minden alkalmazásban konfigurálja azokat a tulajdonságokat, amelyek tudják, hogyan kell visszajelezni a szerverhez.

Ez egy bootstrap folyamat, és Ezen alkalmazások mindegyikének lesz egy neve bootstrap.tulajdonságok. Olyan tulajdonságokat tartalmaz, mint a alkalmazás.tulajdonságok de csavarral:

Szülő Tavasz ApplicationContext betölti a bootstrap.tulajdonságok első. Ez kritikus fontosságú ahhoz, hogy a Config Server elkezdhesse a tulajdonságok kezelését a alkalmazás.tulajdonságok. Ez különleges ApplicationContext ez visszafejt minden titkosított alkalmazás tulajdonságot.

Okos, ha ezeket a tulajdonságfájlokat megkülönböztetjük.bootstrap.tulajdonságok a konfigurációs szerver előkészítésére szolgál, és alkalmazás.tulajdonságok az alkalmazásunkra jellemző tulajdonságokra vonatkozik. Technikailag azonban lehetséges az alkalmazás tulajdonságainak elhelyezése bootstrap.tulajdonságok.

Végül, mivel a Config Server kezeli az alkalmazás tulajdonságait, felmerülhet a kérdés, hogy miért van egy alkalmazás.tulajdonságok egyáltalán? A válasz az, hogy ezek még mindig jól jönnek, mint alapértelmezett értékek, amelyek talán a Config Server nem rendelkeznek.

3. Felfedezés

Most, hogy gondoskodunk a konfigurációról, szükségünk van arra, hogy valamennyi szerverünk megtalálja egymást. Megoldjuk ezt a problémát a Eureka felfedező szerver fel. Mivel alkalmazásaink bármilyen ip / port kombináción futhatnak, szükségünk van egy központi címjegyzékre, amely alkalmazáscím-keresésként szolgálhat.

Amikor egy új szervert kiépítettek, kommunikálni fog a felderítő szerverrel és regisztrálja a címét, hogy mások is kommunikálhassanak vele. Így más alkalmazások felhasználhatják ezeket az információkat, amikor kéréseket tesznek.

Ha további részleteket szeretne megtudni, és egy összetettebb felfedezési megvalósítást szeretne megnézni, tekintse meg a Spring Cloud Eureka cikkét.

3.1. Beállít

Ismét navigálunk a start.spring.io oldalra. Állítsa a műtárgyat „felfedezésre”. Keressen rá az „eureka server” kifejezésre, és adja hozzá ezt a függőséget. Keressen rá a „config client” kifejezésre, és adja hozzá ezt a függőséget. Végül hozza létre a projektet.

Alternatív megoldásként létrehozhatunk egy Tavaszi csizma projekt, másolja a POM a config szerverről, és cserélje ki ezeket a függőségeket:

 org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka-server 

Referenciaként megtaláljuk a csomagokat a Maven Central-on (config-kliens, eureka-szerver).

3.2. Tavaszi konfigur

Adjuk hozzá a Java config-ot a fő osztályhoz:

@SpringBootApplication @EnableEurekaServer nyilvános osztály DiscoveryApplication {...}

@EnableEurekaServer a kiszolgálót felfedező szerverként konfigurálja a következővel: Netflix Eureka. Tavaszi csizma automatikusan észleli az osztályútvonal konfigurációs függőségét, és kikéri a konfigurációt a konfigurációs kiszolgálóról.

3.3. Tulajdonságok

Most két tulajdonságfájlt adunk hozzá:

Először hozzátesszük bootstrap.tulajdonságok -ba src / main / resources:

spring.cloud.config.name = felfedezés spring.cloud.config.uri = // localhost: 8081

Ezek a tulajdonságok lehetővé teszik, hogy a felfedező szerver indításkor lekérdezze a konfigurációs kiszolgálót.

Másodszor pedig hozzátesszük felfedezés.tulajdonságok a Git-tárunkba

spring.application.name = discovery server.port = 8082 eureka.instance.hostname = localhost eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka / eureka.client.register-with-eureka = false eureka.client. fetch-register = hamis

A fájlnévnek meg kell egyeznie a tavasz.alkalmazás.neve ingatlan.

Ezenkívül elmondjuk ennek a szervernek, hogy az alapértelmezett zónában működik, ez egyezik a config kliens régióbeállításával. Azt is mondjuk a szervernek, hogy ne regisztráljon egy másik felfedezési példánynál.

A gyártás során ezek közül egynél több rendelkeznénk redundancia biztosítására meghibásodás esetén, és ez a beállítás igaz lenne.

Vezessük át a fájlt a Git adattárba. Ellenkező esetben a fájl nem kerül felismerésre.

3.4. Adja hozzá a függőséget a Config szerverhez

Adja hozzá ezt a függőséget a konfigurációs kiszolgáló POM fájljához:

 org.springframework.cloud spring-cloud-starter-eureka 

Referenciaként megtalálhatjuk a csomagot a Maven Central-on (eureka-kliens).

Adja hozzá ezeket a tulajdonságokat a alkalmazás.tulajdonságok fájl src / main / resources a konfigurációs kiszolgáló

eureka.client.region = alapértelmezett eureka.client.registryFetchIntervalSeconds = 5 eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka /

3.5. Fuss

Indítsa el a felfedező kiszolgálót ugyanazzal a paranccsal, mvn spring-boot: fut. A parancssor kimenetének tartalmaznia kell:

Konfiguráció lekérése a kiszolgálóról: // localhost: 8081 ... A Tomcat elindult a 8082-es porton (portokon): (http)

Állítsa le és futtassa újra a konfigurációs szolgáltatást. Ha minden jó, akkor a kimenet a következőképpen néz ki:

DiscoveryClient_CONFIG / 10.1.10.235: config: 8081: szolgáltatás regisztrálása ... A Tomcat elindult a következő portokon: 8081 (http) DiscoveryClient_CONFIG / 10.1.10.235: config: 8081 - regisztráció állapota: 204

4. Átjáró

Most, hogy megoldódtak a konfigurációs és felfedezési problémáink, továbbra is problémát jelent az ügyfelek hozzáférése az összes alkalmazásunkhoz.

Ha mindent elosztott rendszerben hagyunk, akkor összetett CORS fejléceket kell kezelnünk, hogy lehetővé tegyük a kereszt-származási kérelmeket az ügyfeleken. Megoldhatjuk ezt egy átjárókiszolgáló létrehozásával. Ez visszafordított proxy szolgáltatásként továbbítja az ügyfelektől a háttérszervereinket.

Az átjárókiszolgáló kiváló alkalmazás a mikroszolgáltatás architektúrájában, mivel lehetővé teszi, hogy minden válasz egyetlen hosztból származzon. Ez kiküszöböli a CORS szükségességét, és kényelmes helyet biztosít számunkra az általános problémák, például a hitelesítés kezelésére.

4.1. Beállít

Mostanra már ismerjük a gyakorlatot. Navigáljon ide //start.spring.io. Állítsa a műtárgyat „átjáróra”. Keressen rá a „zuul” kifejezésre, és adja hozzá ezt a függőséget. Keressen rá a „config client” kifejezésre, és adja hozzá ezt a függőséget. Keressen rá az „eureka discovery” kifejezésre, és adja hozzá ezt a függőséget. Végül hozza létre azt a projektet.

Alternatív megoldásként létrehozhatunk egy Tavaszi csizma alkalmazás a következő függőségekkel:

 org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-zuul 

Referenciaként megtalálhatjuk a csomagot a Maven Central-on (config-kliens, eureka-kliens, zuul).

4.2. Tavaszi konfigur

Adjuk hozzá a konfigurációt a fő osztályhoz:

@SpringBootApplication @EnableZuulProxy @EnableEurekaClient nyilvános osztály GatewayApplication {...}

4.3. Tulajdonságok

Most két tulajdonságfájlt adunk hozzá:

bootstrap.tulajdonságok ban ben src / main / resources:

spring.cloud.config.name = gateway spring.cloud.config.discovery.service-id = config spring.cloud.config.discovery.enabled = true eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka /

átjáró.tulajdonságok a Git-tárunkban

spring.application.name = átjáró szerver.port = 8080 eureka.client.region = alapértelmezett eureka.client.registryFetchIntervalSeconds = 5 zuul.routes.book-service.path = / book-service / ** zuul.routes.book-service .sensitive-headers = Set-Cookie, Engedélyezés hystrix.command.book-service.execution.isolation.thread.timeoutInMilliseconds = 600000 zuul.routes.rating-service.path = / rating-service / ** zuul.routes.rating- service.sensitive-headers = Set-Cookie, Engedélyezés hystrix.command.rating-service.execution.isolation.thread.timeoutInMilliseconds = 600000 zuul.routes.discovery.path = / discovery / ** zuul.routes.discovery.sensitive-headers = Set-Cookie, Engedélyezés zuul.routes.discovery.url = // localhost: 8082 hystrix.command.discovery.execution.isolation.thread.timeoutInMilliseconds = 600000

A zuul.routes tulajdonság lehetővé teszi számunkra, hogy meghatározzunk egy alkalmazást bizonyos kérelmek továbbítására egy hangya URL-egyező alapján. Vagyonunk azt mondja Zuulnak, hogy irányítson minden beérkező kérést / book-service / ** alkalmazáshoz a tavasz.alkalmazás.neve nak,-nek könyv-szolgáltatás. Ezután Zuul megkeresi a gazdagépet a felfedező szerverről az alkalmazás nevével, és továbbítja a kérést az adott kiszolgálónak.

Ne felejtse el végrehajtani a változásokat az adattárban!

4.4. Fuss

Futtassa a konfigurációs és a felfedező alkalmazásokat, és várja meg, amíg a konfigurációs alkalmazás regisztrálódik a felderítő kiszolgálón. Ha már futnak, akkor nem kell újraindítanunk őket. Miután ez befejeződött, futtassa az átjárókiszolgálót. Az átjárókiszolgálónak el kell indulnia a 8080-as porton, és regisztrálnia kell magát a felfedező kiszolgálón. A konzol kimenetének tartalmaznia kell:

Konfiguráció lekérése a szerverről: //10.1.10.235:8081/ ... DiscoveryClient_GATEWAY / 10.1.10.235: átjáró: 8080: szolgáltatás regisztrálása ... DiscoveryClient_GATEWAY / 10.1.10.235: átjáró: 8080 - regisztráció állapota: 204 A Tomcat elindult a porton 8080 (http)

Az egyik könnyen elkövethető hiba a szerver indítása, mielőtt a konfigurációs kiszolgáló regisztrált volna az Eurekánál. Ebben az esetben egy naplót fogunk látni ezzel a kimenettel:

Konfiguráció lekérése a kiszolgálóról: // localhost: 8888

Ez a konfigurációs kiszolgáló alapértelmezett URL-je és portja, és azt jelzi, hogy a felfedezési szolgáltatásunknak nem volt címe a konfigurációs kérelem benyújtásakor. Várjon csak néhány másodpercet, és próbálkozzon újra, miután a konfigurációs szerver regisztrált az Eurekánál, a probléma megoldódik.

5. Könyvszolgáltatás

A mikroszolgáltatási architektúrában szabadon készíthetünk annyi alkalmazást, amely megfelel egy üzleti célnak. A mérnökök gyakran felosztják szolgáltatásaikat tartományok szerint. Ezt a mintát követjük, és létrehozunk egy könyvszolgáltatást, amely az alkalmazásunkban szereplő könyvek összes műveletét kezeli.

5.1. Beállít

Még egyszer. Navigáljon ide //start.spring.io. Állítsa a műtárgyat „könyv-szolgáltatás” értékre. Keressen rá a „web” kifejezésre, és adja hozzá ezt a függőséget. Keressen rá a „config client” kifejezésre, és adja hozzá ezt a függőséget. Keressen rá az „eureka discovery” kifejezésre, és adja hozzá ezt a függőséget. Generálja azt a projektet.

Alternatív megoldásként adja hozzá ezeket a függőségeket egy projekthez:

 org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web 

Referenciaként megtalálhatjuk a csomagot a Maven Central-on (config-kliens, eureka-kliens, web).

5.2. Tavaszi konfigur

Módosítsuk fő osztályunkat:

@SpringBootApplication @EnableEurekaClient @RestController @RequestMapping ("/ books") public class BookServiceApplication {public static void main (String [] args) {SpringApplication.run (BookServiceApplication.class, args); } private List bookList = Arrays.asList (új könyv (1L, "Baeldung megy a piacra", "Tim Schimandle"), új könyv (2L, "Baeldung megy a parkba", "Slavisa"); @GetMapping ("") public List findAllBooks () {return bookList; } @GetMapping ("/ {bookId}") nyilvános Book findBook (@PathVariable Long bookId) {return bookList.stream (). Filter (b -> b.getId (). Egyenlő (bookId)). FindFirst (). VagyElse (nulla); }}

Hozzáadtunk egy REST vezérlőt és egy tulajdonságfájlunk által beállított mezőt is, hogy visszaadjuk a konfigurálás során beállított értéket.

Tegyük hozzá a POJO könyvet:

nyilvános osztály Könyv {private Long id; magánhúr-szerző; privát húr cím; // szabványos mérőeszközök és beállítók}

5.3. Tulajdonságok

Most csak hozzá kell adnunk a két tulajdonságfájlt:

bootstrap.tulajdonságok ban ben src / main / resources:

spring.cloud.config.name = book-service spring.cloud.config.discovery.service-id = config spring.cloud.config.discovery.enabled = true eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka /

könyv-szolgáltatás.tulajdonságok a Git-tárunkban:

spring.application.name = book-service server.port = 8083 eureka.client.region = alapértelmezett eureka.client.registryFetchIntervalSeconds = 5 eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka /

Végezzük el a változásokat az adattárban.

5.4. Fuss

Miután az összes többi alkalmazás elindult, elindíthatjuk a könyvszolgáltatást. A konzol kimenetének a következőképpen kell kinéznie:

DiscoveryClient_BOOK-SERVICE / 10.1.10.235: könyv-szolgáltatás: 8083: szolgáltatás regisztrálása ... DiscoveryClient_BOOK-SERVICE / 10.1.10.235: könyv-szolgáltatás: 8083 - regisztráció állapota: 204 A Tomcat elindult a következő portokon: 8083 (http)

Ha ez elkészült, a böngészőnkkel elérhetjük az imént létrehozott végpontot. Keresse meg a // localhost: 8080 / book-service / books címet, és visszakapunk egy JSON objektumot két könyvvel, amelyeket a vezérlőbe adtunk. Figyelje meg, hogy nem a könyvszolgáltatást érjük el közvetlenül a 8083-as porton, hanem az átjárókiszolgálón megyünk keresztül.

6. Minősítési szolgáltatás

A könyv szolgáltatásunkhoz hasonlóan a minősítési szolgáltatásunk is egy domain által vezérelt szolgáltatás lesz, amely a minősítésekkel kapcsolatos műveleteket kezeli.

6.1. Beállít

Még egyszer. Navigáljon ide //start.spring.io. Állítsa a műtárgyat „minősítés-szolgáltatás” értékre. Keressen rá a „web” kifejezésre, és adja hozzá ezt a függőséget. Keressen rá a „config client” kifejezésre, és adja hozzá ezt a függőséget. Keressen eureka felfedezés és add hozzá azt a függőséget. Ezután generálja azt a projektet.

Alternatív megoldásként adja hozzá ezeket a függőségeket egy projekthez:

 org.springframework.cloud spring-cloud-starter-config org.springframework.cloud spring-cloud-starter-eureka org.springframework.boot spring-boot-starter-web 

Referenciaként megtalálhatjuk a csomagot a Maven Central-on (config-kliens, eureka-kliens, web).

6.2. Tavaszi konfigur

Módosítsuk fő osztályunkat:

@SpringBootApplication @EnableEurekaClient @RestController @RequestMapping ("/ ratings") public class RatingServiceApplication {public static void main (String [] args) {SpringApplication.run (RatingServiceApplication.class, args); } privát lista ratingList = tömbök. asList (új besorolás (1L, 1L, 2), új minősítés (2L, 1L, 3), új minősítés (3L, 2L, 4), új minősítés (4L, 2L, 5)); @GetMapping ("") public List findRatingsByBookId (@RequestParam Long bookId) bookId.equals (0L)? Gyűjtemények.EMPTY_LIST: ratingList.stream (). Filter (r -> r.getBookId (). Egyenlő (bookId)). Gyűjt (Collectors.toList ()); @GetMapping ("/ all") public List findAllRatings () {return ratingList; }}

Hozzáadtunk egy REST vezérlőt és egy tulajdonságfájlunk által beállított mezőt is, hogy visszaadjuk a konfigurálás során beállított értéket.

Adjuk hozzá a POJO értékelést:

nyilvános osztály besorolása {private Long id; privát Hosszú könyvId; privát int csillagok; // szabványos mérőeszközök és beállítók}

6.3. Tulajdonságok

Most csak hozzá kell adnunk a két tulajdonságfájlt:

bootstrap.tulajdonságok ban ben src / main / resources:

spring.cloud.config.name = minősítési szolgáltatás spring.cloud.config.discovery.service-id = config spring.cloud.config.discovery.enabled = true eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka /

minősítés-szolgáltatás.tulajdonságok a Git-tárunkban:

spring.application.name = rating-service server.port = 8084 eureka.client.region = alapértelmezett eureka.client.registryFetchIntervalSeconds = 5 eureka.client.serviceUrl.defaultZone = // localhost: 8082 / eureka /

Végezzük el a változásokat az adattárban.

6.4. Fuss

Miután az összes többi alkalmazás elindult, elindíthatjuk a minősítési szolgáltatást. A konzol kimenetének a következőképpen kell kinéznie:

DiscoveryClient_RATING-SERVICE / 10.1.10.235: minősítési szolgáltatás: 8083: szolgáltatás regisztrálása ... DiscoveryClient_RATING-SERVICE / 10.1.10.235: minősítési szolgáltatás: 8083 - regisztráció állapota: 204 A Tomcat elindult a következő portokon: 8084 (http)

Ha ez elkészült, a böngészőnkkel elérhetjük az imént létrehozott végpontot. Navigáljon ide // localhost: 8080 / rating-service / ratings / all és visszakapjuk az összes értékelésünket tartalmazó JSON-t. Figyelje meg, hogy nem közvetlenül a 8084-es porton érjük el a minősítési szolgáltatást, hanem az átjárókiszolgálón megyünk keresztül.

7. Következtetés

Most összekapcsolhatjuk a Spring Cloud különféle darabjait egy működő mikroszolgáltatási alkalmazásba. Ez egy olyan bázist képez, amelyet felhasználhatunk bonyolultabb alkalmazások építésének megkezdéséhez.

Mint mindig, itt is megtalálhatjuk ezt a forráskódot a GitHubon.


$config[zx-auto] not found$config[zx-overlay] not found