Uvod u proljetno uklanjanje pomoću HTTP pozivača

1. Pregled

U nekim slučajevima sustav moramo razgraditi na nekoliko procesa, od kojih svaki preuzima odgovornost za drugačiji aspekt naše aplikacije. U tim scenarijima nije neuobičajeno da jedan od procesa treba sinkronizirano dobiti podatke iz drugog.

Spring Framework nudi niz alata koji se sveobuhvatno nazivaju Remoting proljeća to nam omogućuje pozivanje na udaljene usluge kao da su, barem donekle, dostupne lokalno.

U ovom ćemo članku postaviti aplikaciju temeljenu na Spring-u HTTP pozivač, koji koristi izvornu Java serializaciju i HTTP za pružanje udaljenog poziva metode između klijenta i poslužiteljske aplikacije.

2. Definicija usluge

Pretpostavimo da moramo implementirati sustav koji omogućava korisnicima da rezerviraju vožnju u taksiju.

Pretpostavimo također da smo odlučili graditi dvije različite primjene za postizanje ovog cilja:

  • zahtjev za rezervacijskim motorom da provjeri može li se dostaviti zahtjev za kabinom i
  • prednja web aplikacija koja omogućava kupcima da rezerviraju svoje vožnje, osiguravajući da je potvrđena dostupnost kabine

2.1. Uslužno sučelje

Kad koristimo Remoting proljeća s HTTP pozivač, moramo definirati našu uslugu koja se može daljinski pozivati ​​putem sučelja kako bi Spring mogao stvoriti proxyje i na klijentskoj i na poslužiteljskoj strani koji obuhvaćaju tehničke značajke udaljenog poziva. Pa krenimo sa sučeljem usluge koja nam omogućuje da rezerviramo taksi:

javno sučelje CabBookingService {Booking bookRide (String pickUpLocation) baca BookingException; }

Kada je služba u mogućnosti dodijeliti taksi, vraća a Rezervacija objekt s kodom rezervacije. Rezervacija mora biti moguće serializirati jer Springov HTTP pozivatelj mora svoje instance prenijeti s poslužitelja na klijenta:

javna klasa Rezervacije implementira Serializable {private String bookingCode; @Override public String toString () {return format ("Vožnja potvrđena: kôd '% s'.", BookingCode); } // standardni getteri / postavljači i konstruktor}

Ako služba ne može rezervirati taksi, a BookingException baca se. U ovom slučaju nema potrebe označavati razred kao Serijalizirati jer Iznimka već ga provodi:

javna klasa BookingException proširuje Exception {public BookingException (niz poruka) {super (poruka); }}

2.2. Pakiranje usluge

Sučelje usluge, zajedno sa svim prilagođenim klasama koje se koriste kao argumenti, vrste povratka i iznimke, moraju biti dostupne i u stazi klasa klijenta i poslužitelja. Jedan od najučinkovitijih načina za to je spakiranje svih njih u .jar datoteka koja se kasnije može uključiti kao ovisnost na poslužitelju i klijentu pom.xml.

Stavimo tako sav kôd u namjenski Maven modul, nazvan "api"; za ovaj ćemo primjer upotrijebiti sljedeće Mavenove koordinate:

com.baeldung api 1.0-SNAPSHOT

3. Poslužiteljska aplikacija

Izgradimo aplikaciju booking engine da bismo izložili uslugu pomoću Spring Boot-a.

3.1. Ovisnosti Mavena

Prvo morate biti sigurni da vaš projekt koristi Spring Boot:

 org.springframework.boot spring-boot-starter-parent 2.2.2.Opusti 

Posljednju verziju Spring Boot možete pronaći ovdje. Tada nam treba modul Web starter:

 org.springframework.boot spring-boot-starter-web 

I trebamo modul definicije usluge koji smo sastavili u prethodnom koraku:

 com.baeldung api 1.0-SNAPSHOT 

3.2. Provedba usluge

Prvo definiramo klasu koja implementira sučelje usluge:

javna klasa CabBookingServiceImpl implementira CabBookingService {@Override public Booking bookPickUp (String pickUpLocation) baca BookingException {if (random () <0.3) throw new BookingException ("Cab nedostupan"); vratiti novu rezervaciju (randomUUID (). toString ()); }}

Pretvarajmo se da je ovo vjerojatna provedba. Korištenjem testa sa slučajnom vrijednošću moći ćemo reproducirati oba uspješna scenarija - kada se pronađe dostupna kabina i vrati kôd rezervacije - i neuspjeli scenariji - kada se izbaci BookingException koji označava da nema dostupne kabine.

3.3. Izlaganje Usluge

Zatim moramo definirati aplikaciju s grahom tipa HttpInvokerServiceExporter u kontekstu. Pobrinut će se za izlaganje HTTP ulazne točke u web aplikaciji koju će klijent kasnije pozvati:

@Configuration @ComponentScan @EnableAutoConfiguration Server javne klase {@Bean (name = "/ booking") HttpInvokerServiceExporter accountService () {HttpInvokerServiceExporter izvoznik = novi HttpInvokerServiceExporter (); exporter.setService (novi CabBookingServiceImpl ()); exporter.setServiceInterface (CabBookingService.class); povratni izvoznik; } public static void main (String [] args) {SpringApplication.run (Server.class, args); }}

Vrijedno je napomenuti da je Spring's HTTP pozivač koristi naziv HttpInvokerServiceExporter bean kao relativni put za URL HTTP krajnje točke.

Sada možemo pokrenuti poslužiteljsku aplikaciju i nastaviti je raditi dok postavljamo klijentsku aplikaciju.

4. Prijava klijenta

Napišimo sada klijentsku aplikaciju.

4.1. Ovisnosti Mavena

Upotrijebit ćemo istu definiciju usluge i istu verziju Spring Boot-a koju smo koristili na strani poslužitelja. I dalje nam je potrebna ovisnost o web pokretaču, ali budući da ne trebamo automatski pokretati ugrađeni spremnik, Tomcat starter možemo izuzeti iz ovisnosti:

 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-tomcat 

4.2. Implementacija klijenta

Provedimo klijenta:

Klijent javne klase @Configuration {@Bean public HttpInvokerProxyFactoryBean invoker () {HttpInvokerProxyFactoryBean invoker = new HttpInvokerProxyFactoryBean (); invoker.setServiceUrl ("// localhost: 8080 / booking"); invoker.setServiceInterface (CabBookingService.class); povratnik koji se poziva; } public static void main (String [] args) baca BookingException {CabBookingService service = SpringApplication .run (Client.class, args) .getBean (CabBookingService.class); out.println (service.bookRide ("13 Seagate Blvd, Key Largo, FL 33037")); }}

The @Grah anotirani zazivač() metoda stvara instancu HttpInvokerProxyFactoryBean. Moramo navesti URL na koji udaljeni poslužitelj odgovara putem setServiceUrl () metoda.

Slično onome što smo učinili za poslužitelj, trebali bismo pružiti i sučelje usluge koju želimo pozivati ​​na daljinu putem setServiceInterface () metoda.

HttpInvokerProxyFactoryBean provodi Spring's FactoryBean. A FactoryBean definira se kao grah, ali Spring IoC spremnik ubrizgat će objekt koji stvara, a ne tvornicu samu. Možete pronaći više detalja o FactoryBean u našem članku o tvorničkom grahu.

The glavni() metoda pokreće samostalnu aplikaciju i dobiva primjerak CabBookingService iz konteksta. Ispod haube ovaj je objekt samo proxy stvoren od strane HttpInvokerProxyFactoryBean koja se brine o svim tehničkim značajkama uključenim u izvršavanje daljinskog poziva. Zahvaljujući njemu sada možemo lako koristiti proxy kao što bismo to učinili da je implementacija usluge dostupna lokalno.

Pokrenimo aplikaciju više puta kako bismo izvršili nekoliko udaljenih poziva kako bismo provjerili kako se klijent ponaša kada je kabina dostupna, a kada nije.

5. Caveat Emptor

Kada radimo s tehnologijama koje omogućuju daljinsko pozivanje, postoje neke zamke kojih bismo trebali biti dobro svjesni.

5.1. Čuvajte se iznimki povezanih s mrežom

Uvijek bismo trebali očekivati ​​neočekivano kada radimo s nepouzdanim resursima kao što je mreža.

Pretpostavimo da klijent poziva poslužitelj dok ga nije moguće dobiti - bilo zbog mrežnog problema ili zato što poslužitelj ne radi - tada će Spring Remoting podići RemoteAccessException to je RuntimeException.

Kompajler nas tada neće prisiliti da uključimo poziv u blok try-catch, ali uvijek bismo trebali razmotriti da to učinimo kako bismo pravilno upravljali mrežnim problemima.

5.2. Predmeti se prenose prema vrijednosti, a ne prema referenci

Proljetno uklanjanje HTTP-a argumenti metode maršala i vraćene vrijednosti za njihov prijenos na mrežu. To znači da poslužitelj djeluje na kopiji navedenog argumenta, a klijent na kopiji rezultata koji je stvorio poslužitelj.

Stoga ne možemo očekivati, na primjer, da će pozivanje metode na rezultirajućem objektu promijeniti status istog objekta na strani poslužitelja jer između klijenta i poslužitelja nema zajedničkog objekta.

5.3. Čuvajte se fino zrnastih sučelja

Pozivanje metode preko mrežnih granica znatno je sporije od pozivanja na objekt u istom procesu.

Iz tog razloga, obično je dobra praksa definirati usluge na koje bi se trebalo daljinski pozivati ​​grublje zrnastim sučeljima koja mogu dovršiti poslovne transakcije koje zahtijevaju manje interakcija, čak i na štetu glomaznijeg sučelja.

6. Zaključak

Ovim primjerom vidjeli smo kako je lako pomoću Spring Remoting-a pozvati udaljeni proces.

Rješenje je nešto manje otvoreno od ostalih raširenih mehanizama poput REST-a ili web usluga, ali u scenarijima kada su sve komponente razvijene s Springom, može predstavljati održivu i daleko bržu alternativu.

Kao i obično, izvore ćete pronaći na GitHubu.