Programare reactiva java. Programare reactivă în Objective-C

Vreau să vă vorbesc despre o disciplină modernă de programare care răspunde cerințelor tot mai mari de scalabilitate, toleranță la erori și răspuns rapid și este indispensabilă atât în ​​mediile multi-core, cât și în cloud computing, precum și să prezinte un curs online deschis despre acesta, care va începe în doar câteva zile.

Dacă nu ai auzit de programare reactiva, totul e bine. Aceasta este o disciplină în dezvoltare rapidă, care combină concurența cu asincronul și bazat pe evenimente. Reactivitatea este inerentă oricărui serviciu web și sistem distribuit și este nucleul multor sisteme de înaltă performanță, cu un grad ridicat de paralelism. Pe scurt, autorii cursului propun să considere programarea reactivă ca o extensie naturală a programării funcționale (cu funcții de ordin superior) la sisteme paralele cu o stare distribuită, coordonate și orchestrate prin fluxuri de date asincrone schimbate de subiecții activi, sau actori.

Cu cuvinte mai ușor de înțeles, acest lucru este descris în Manifestul Reactiv, mai jos voi repovesti principalele prevederi din acesta, iar traducerea integrală este publicată pe Habré. După cum spune Wikipedia, termenul programare reactiva există de mult timp și aplicații practice diferite grade de exotism, dar a primit un nou impuls pentru dezvoltare și distribuție destul de recent, datorită eforturilor autorilor Reactive Manifesto, un grup de inițiativă de la Typesafe Inc. Typesafe este cunoscută în comunitatea de programare funcțională ca fiind compania fondată de autorii excelentului limbaj Scala și a platformei paralele revoluționare Akka. Acum își poziționează compania ca fiind creatorul primei platforme cu jet din lume, concepută pentru a dezvolta o nouă generație. Platforma lor vă permite să dezvoltați rapid complexe interfețe cu utilizatorulși oferă un nou nivel de abstractizare peste calculul paralel și multithreading, reducând riscurile lor inerente prin scalare previzibilă garantată. Acesta pune în practică ideile Manifestului Reactiv și permite dezvoltatorului să înțeleagă și să creeze aplicații care să răspundă nevoilor actuale.

Puteți cunoaște această platformă și programarea reactivă participând la cursul online Massive Open Reactive Principles. Acest curs este o continuare a cursului „Principii de programare funcțională la Scala” al lui Martin Odersky, care a avut peste 100.000 de participanți și a demonstrat una dintre cele mai mari rate de succes pentru un curs online masiv deschis de către participanții săi din lume. Împreună cu creatorul limbajului Scala, noul curs este predat de Eric Meyer, care a dezvoltat cadrul Rx pentru programare reactivă sub .NET, și Roland Kuhn, care conduce în prezent echipa de dezvoltare Akka la Typesafe. Cursul acoperă elementele cheie ale programării reactive și arată cum acestea sunt aplicate la proiectarea sistemelor bazate pe evenimente care sunt scalabile și tolerante la erori. Materialul didactic este ilustrat programe scurteși este însoțit de un set de sarcini, fiecare dintre ele fiind un proiect software. În cazul îndeplinirii cu succes a tuturor sarcinilor, participanții primesc certificate (desigur, participarea și certificatele sunt gratuite). Cursul durează 7 săptămâni și începe luni, 4 noiembrie. O schiță detaliată, precum și un videoclip introductiv sunt disponibile pe pagina cursului: https://www.coursera.org/course/reactive.

Pentru cei care sunt interesați sau au îndoieli, ofer un rezumat concis al conceptelor de bază ale Manifestului Reactiv. Autorii săi notează schimbări semnificative în cerințele pentru aplicații pentru anul trecut. Astăzi aplicațiile sunt implementate în orice mediu din dispozitive mobile la clustere cloud cu mii de procesoare multi-core. Aceste medii impun noi cerințe software si tehnologii. Arhitecturile din generația anterioară s-au concentrat pe servere și containere gestionate, scalare prin hardware suplimentar costisitor, soluții proprietare și calcul paralel prin multithreading. În prezent, este în curs de dezvoltare o nouă arhitectură care are patru caracteristici cheie care sunt din ce în ce mai răspândite atât în ​​mediile industriale de consum, cât și în cele corporative. Sistemele cu această arhitectură sunt: ​​bazate pe evenimente, scalabile, tolerante la erori (rezistente) și au un răspuns rapid, de ex. responsive (Responsive). Acest lucru oferă o experiență de utilizator fără întreruperi, în timp real, susținută de o stivă de aplicații scalabilă și auto-vindecare, care este gata să fie implementată în medii multi-core și cloud. Fiecare dintre cele patru caracteristici ale unei arhitecturi reactive se aplică întregului stivă de tehnologie, ceea ce le diferențiază de legăturile din arhitecturile stratificate. Să le luăm în considerare puțin mai detaliat.


Event Driver aplicațiile presupun comunicații cu componente asincrone și implementează designul lor slab cuplat: emițătorul și receptorul mesajului nu trebuie să cunoască nici unul despre celălalt, nici despre metoda de transmitere a mesajului, ceea ce le permite să se concentreze asupra conținutului comunicațiilor. Pe lângă faptul că componentele slab cuplate îmbunătățesc semnificativ mentenabilitatea, extensibilitatea și evoluția sistemului, natura asincronă și neblocantă a interacțiunii lor poate, de asemenea, elibera o parte semnificativă a resurselor, poate reduce timpul de răspuns și poate oferi despre Mai mult debitului comparativ cu aplicațiile tradiționale. Datorită naturii bazate pe evenimente, restul caracteristicilor arhitecturii reactive sunt posibile.

Scalabilitateîn contextul programării reactive, aceasta este reacția sistemului la o modificare a sarcinii, adică elasticitate, obținută prin capacitatea de a adăuga sau elibera noduri de calcul după cum este necesar. Cu o cuplare redusă, mesagerie asincronă și transparență a locației, metoda de implementare și topologia aplicației devin o decizie de timp de implementare și supuse configurației care răspund la încărcare și algoritmilor adaptivi. În acest fel, rețea de calculatoare devine parte a aplicației, având inițial un caracter distribuit explicit.

toleranta la greseli Arhitectura reactivă devine, de asemenea, parte a designului, ceea ce o face semnificativ diferită de abordările tradiționale de a asigura disponibilitatea continuă a sistemului prin redundanță și failover pe server. Reziliența unui astfel de sistem se realizează prin capacitatea sa de a răspunde corect la defecțiunile componentelor individuale, de a izola aceste defecțiuni prin stocarea contextului lor sub forma mesajelor care le-au cauzat și de a transmite aceste mesaje unei alte componente care poate lua decizii cu privire la modul de a le face. gestionează eroarea. Această abordare vă permite să păstrați logica de afaceri a aplicației curată, separând logica de gestionare a defecțiunilor de aceasta, care este formulată într-o formă declarativă explicită pentru înregistrarea, izolarea și gestionarea defecțiunilor prin intermediul sistemului însuși. Pentru a construi astfel de sisteme de auto-vindecare, componentele sunt ordonate ierarhic, iar problema este escaladată la nivelul care o poate rezolva.

Și, în sfârșit receptivitatea- aceasta este capacitatea sistemului de a răspunde la intrarea utilizatorului, indiferent de sarcină și defecțiuni, astfel de aplicații implică utilizatorul în interacțiune, creează un sentiment legătură strânsă cu un sistem și echipamente suficiente pentru a îndeplini sarcinile curente. Capacitatea de reacție este relevantă nu numai în sistemele în timp real, ci este și necesară pentru o gamă largă de aplicații. Mai mult, un sistem care nu este capabil să răspundă rapid chiar și în momentul defecțiunii nu poate fi considerat tolerant la erori. Receptivitatea este obținută prin utilizarea modelelor observabile, a fluxurilor de evenimente și a clienților cu state. Modelele observabile emit evenimente atunci când starea lor se schimbă și oferă interacțiune în timp real între utilizatori și sisteme, în timp ce fluxurile de evenimente oferă abstractizarea pe care se construiește această interacțiune prin transformări și comunicații asincrone neblocante.

Astfel, aplicațiile reactive reprezintă o abordare echilibrată pentru rezolvarea unei game largi de probleme ale dezvoltării software moderne. Construite pe o bază bazată pe evenimente, ele oferă instrumentele necesare pentru a garanta scalabilitatea și toleranța la erori și pentru a susține o experiență de utilizator receptivă cu funcții complete. Autorii se așteaptă la toate Mai mult sistemele vor adera la principiile manifestului reactiv.

În plus, dau planul de curs fără traducere. Doar în cazul în care ați citit până aici și sunteți încă curios.

Saptamana 1: Revizuirea principiilor programării funcționale: model de substituție, expresii pentru și modul în care acestea se raportează la monade. Introduce o nouă implementare a expresiilor for: generatoare de valori aleatorii. Arată cum aceasta poate fi utilizată în testarea randomizată și oferă o imagine de ansamblu asupra ScalaCheck, un instrument care implementează această idee.

Săptămâna 2: Programare funcțională și stare mutabilă. Ce face ca un obiect să fie mutabil? Cum afectează acest lucru modelul de substituție. Exemplu extins: simulare circuit digital

Săptămâna 3: viitor. Introduce viitorul ca o altă monada, cu expresii pentru ca sintaxă concretă. Arată cum pot fi compuse futures pentru a evita blocarea firelor. Discută despre tratarea erorilor încrucișate.

Săptămâna 4: Procesarea fluxului reactiv. Generalizarea futures la calcule reactive peste fluxuri. operatori de flux.

Săptămâna 5:Actori. Prezintă Modelul Actor, actorii ca unități încapsulate de consistență, transmitere asincronă a mesajelor, discută despre diferite semantici de livrare a mesajelor (cel mult o dată, cel puțin o dată, exact o dată) și eventuala consistență.

Săptămâna 6: supraveghere. Introduce reificarea eșecului, gestionarea eșecului ierarhic, modelul Error Kernel, monitorizarea ciclului de viață, discută starea tranzitorie și persistentă.

Săptămâna 7: Modele de conversație. Discută despre gestionarea stării conversaționale între actori și modele pentru controlul fluxului, rutarea mesajelor către grupuri de actori pentru reziliență sau echilibrarea sarcinii, recunoașterea recepției pentru a obține o livrare fiabilă.

Lumea dezvoltării OOP în general și limbajul Java în special trăiesc o viață foarte activă. Are propriile tendințe de modă, iar astăzi vom analiza una dintre principalele tendințe ale sezonului - framework-ul ReactiveX. Dacă ești încă departe de acest val - promit că o să-ți placă! Cu siguranta este mai bun decat blugii cu talie inalta :).

Programare reactiva

De îndată ce limbile OOP s-au maturizat pentru a fi utilizate în masă, dezvoltatorii și-au dat seama cât de mult lipsesc uneori capacitățile limbajelor asemănătoare C. Deoarece scrierea codului în stilul programării funcționale distruge serios calitatea codului OOP și, prin urmare, mentenabilitatea proiectului, a fost inventat un hibrid - programare reactiva.

Paradigma de dezvoltare reactivă se bazează pe ideea de a urmări constant schimbările în starea unui obiect. Dacă au avut loc astfel de modificări, atunci toate obiectele interesate ar trebui să primească date deja actualizate și să lucreze numai cu ele, uitând de cele vechi.

Un bun exemplu de idee de programare reactivă este o foaie de calcul Excel. Dacă legați mai multe celule cu o singură formulă, rezultatul calculului se va schimba de fiecare dată când datele din aceste celule se schimbă. Pentru contabilitate, o astfel de schimbare dinamică a datelor este un lucru comun, dar pentru programatori este mai degrabă o excepție.

A=3; b=4; c=a+b; F1(c); a=1; F2(c);

În acest exemplu, funcțiile F1 și F2 vor funcționa cu valori diferite ale variabilei C. Este adesea necesar ca ambele funcții să aibă doar cele mai actualizate date - programarea reactivă vă va permite să apelați imediat F1 cu noi parametrii fără a modifica logica funcțiilor în sine. Această structură de cod oferă aplicației capacitatea de a răspunde instantaneu la orice modificări, ceea ce o va face rapidă, flexibilă și receptivă.

ReactiveX

Implementarea ideilor de programare reactivă de la zero poate fi destul de supărătoare - există capcane și va dura o perioadă decentă de timp. Prin urmare, pentru mulți dezvoltatori, această paradigmă a rămas doar material teoretic până la apariția ReactiveX.

Cadrul ReactiveX este un instrument de programare reactiv care funcționează cu toate limbajele OOP populare. Creatorii înșiși îl numesc un API multi-platformă pentru dezvoltare asincronă, bazat pe modelul Observer.

Dacă termenul „programare reactivă” este un fel de model teoretic, atunci modelul Observer este un mecanism gata făcut pentru urmărirea modificărilor dintr-un program. Și trebuie să le urmăriți destul de des: încărcarea și actualizarea datelor, notificări de evenimente și așa mai departe.

Modelul Observer există de aproximativ atâta timp cât însuși OOP. Un obiect a cărui stare se poate schimba se numește editor (o traducere populară a termenului Observabil). Toți ceilalți participanți care sunt interesați de aceste modificări sunt abonați (Observator, Abonat). Pentru a primi notificări, abonații se înregistrează la editor specificând în mod explicit ID-ul lor. Editorul generează din când în când notificări, care sunt trimise de către editor către lista de abonați înregistrați.

De fapt, creatorii lui ReactiveX nu au venit cu nimic revoluționar, ci doar au implementat convenabil modelul. Și deși multe limbaje OOP, și în special Java, au implementări gata făcute ale modelului, acest cadru are „ajustare” suplimentară care transformă „Observer” într-un instrument foarte puternic.

RxAndroid

Portul bibliotecii ReactiveX pentru lumea Android se numește rxAndroid și este conectat, ca întotdeauna, prin Gradle.

Compilați „io.reactivex:rxandroid:1.1.0”

Editorul care generează notificări este specificat aici folosind clasa Observable. Un editor poate avea mai mulți abonați; pentru a le implementa, vom folosi clasa Abonat. Comportamentul implicit pentru un Observable este să emită unul sau mai multe mesaje către abonați și apoi să părăsească sau să emită un mesaj de eroare. Mesajele pot fi atât variabile, cât și obiecte întregi.

Rx.Observable myObserv = rx.Observable.create(new rx.Observable.OnSubscribe () ( @Override public void call(Abonatabonat) ( subscriber.onNext(„Bună ziua”); subscriber.onNext(„lume”); subscriber.onCompleted(); ) ));

În acest caz, editorul myObserv va trimite mai întâi șirurile de salut și mesaje, apoi un mesaj de succes. Editorul poate apela metodele onNext() , onCompleted() și onEror(), astfel încât abonații trebuie să le aibă definite.

Abonat mySub = Abonat nou () (... @Override public void onNext(Valoare șir) (Log.e("got data", " " + value);) );

Totul este gata de lucru. Rămâne să conectați obiectele între ele - și „Bună, lume!” în programarea reactivă este gata!

MyObserv.subscribe(mySub);

Trebuie să spun că acesta a fost un exemplu foarte simplu. ReactiveX are multe opțiuni pentru comportamentul tuturor participanților la model: filtrare, grupare, tratare a erorilor. Beneficiile programării reactive pot fi simțite doar încercând-o în acțiune. Să trecem la sarcină mai serios.

Este disponibil în continuare numai pentru membri

Opțiunea 1. Alăturați-vă comunității „site” pentru a citi toate materialele de pe site

Calitatea de membru al comunității în perioada specificată vă va oferi acces la TOATE materialele Hacker, vă va crește reducerea cumulativă personală și vă va permite să acumulați un rating profesional Xakep Score!

Merge.

Programarea reactivă sună la început ca numele unei paradigme în curs de dezvoltare, dar se referă de fapt la o metodă de programare care utilizează o abordare bazată pe evenimente pentru a lucra cu fluxuri de date asincrone. Pe baza datelor constant actuale, sistemele reactive reacţionează la acestea executând o serie de evenimente.
Programarea reactivă urmează modelul de proiectare Observer, care poate fi definit după cum urmează: dacă are loc o schimbare de stare într-un obiect, atunci toate celelalte obiecte sunt notificate și actualizate în consecință. Deci, în loc să interogheze evenimentele pentru modificări, evenimentele sunt împinse asincron, astfel încât observatorii să le poată procesa. În acest exemplu, observatorii sunt funcții care sunt executate atunci când este trimis un eveniment. Și fluxul de date menționat este observabilul real.

Aproape toate limbile și cadrele folosesc această abordare în ecosistemul lor, iar cele mai recente versiuni de Java nu fac excepție. În acest articol, voi explica cum poate fi aplicată programarea reactivă folosind cea mai recentă versiune a JAX-RS în funcționalitatea Java EE 8 și Java 8.

Manifest reactiv

Manifestul reactiv enumeră patru aspecte fundamentale de care o aplicație trebuie să fie mai flexibilă, mai slab cuplată și ușor de scalat și, prin urmare, să poată fi reactivă. Se spune că aplicația trebuie să fie receptivă, flexibilă (și, prin urmare, scalabilă), rezistentă și bazată pe mesaje.

Scopul de bază este o aplicație cu adevărat receptivă. Să presupunem că aveți o aplicație care are un fir mare de procesare a cererilor utilizatorului și, când lucrul este încheiat, acel fir trimite răspunsuri înapoi solicitanților inițiali. Când o aplicație primește mai multe solicitări decât poate gestiona, acest flux devine un blocaj și aplicația își pierde capacitatea de răspuns anterior. Pentru a rămâne receptivă, aplicația trebuie să fie scalabilă și rezistentă. O aplicație rezistentă este una care are funcționalitate de recuperare automată. În experiența majorității dezvoltatorilor, doar o arhitectură bazată pe mesaje permite unei aplicații să fie scalabilă, robustă și receptivă.

Programarea reactivă a fost introdusă în Java 8 și Java EE 8. Limbajul Java a introdus concepte precum CompletionStage și implementarea sa CompletableFuture, iar Java a început să folosească aceste caracteristici în specificații precum Reactive Client API în JAX-RS.

API-ul client reactiv JAX-RS 2.1

Să ne uităm la modul în care programarea reactivă poate fi utilizată în aplicațiile Java EE 8. Pentru a înțelege procesul, aveți nevoie de cunoștințe de bază despre API-ul Java EE.

JAX-RS 2.1 a introdus o nouă modalitate de a crea un client REST reactiv. Implementarea implicită a invocatorului oferit de JAX-RS este sincronă, ceea ce înseamnă că clientul creat va trimite un apel de blocare către punctul final al serverului. Un exemplu de implementare este oferit în Lista 1.

Răspuns răspuns = ClientBuilder.newClient() .target("http://localhost:8080/service-url") .request() .get();
Începând cu versiunea 2.0, JAX-RS oferă suport pentru crearea unui invocator asincron pe API-ul client cu un simplu apel la metoda async(), așa cum se arată în Lista 2.

Viitor răspuns = ClientBuilder.newClient() .target("http://localhost:8080/service-url") .request() .async() .get();
Utilizarea unui invocator asincron pe client returnează o instanță Future de tip javax.ws.rs.core.Response . Acest lucru poate duce la interogarea unui răspuns, apelarea future.get() sau înregistrarea unui apel invers care va fi apelat atunci când există un răspuns HTTP disponibil. Ambele implementări sunt potrivite pentru programarea asincronă, dar lucrurile tind să devină mai complicate dacă doriți să grupați apeluri inverse sau să adăugați cazuri condiționate la acele minime de execuție asincronă.

JAX-RS 2.1 oferă o modalitate reactivă de a depăși aceste probleme cu noul API JAX-RS Reactive Client pentru asamblarea clientului. Este la fel de simplu ca apelarea metodei rx() în timpul construirii clientului. În Lista 3, metoda rx() returnează un invocator reactiv care există în timpul execuției clientului, iar clientul returnează un răspuns cu tipul CompletionStage.rx() , care permite trecerea de la un invocator sincron la un invocator asincron cu un simplu apel.

Etapa de finalizare răspuns = ClientBuilder.newClient() .target("http://localhost:8080/service-url") .request() .rx() .get();
Etapa de finalizare<Т>este o interfață nouă introdusă în Java 8. Reprezintă un calcul, care poate fi un pas în cadrul unui calcul mai mare, așa cum sugerează și numele. Aceasta este singura reactivitate Java 8 care a ajuns în JAX-RS.
După ce primesc instanța de răspuns, pot apela AcceptAsync() , unde pot furniza o bucată de cod care va fi executată asincron când răspunsul devine disponibil, așa cum se arată în Lista 4.

Response.thenAcceptAsync(res -> ( Temperature t = res.readEntity(Temperature.class); //face lucruri cu t ));
Adăugarea de reactivitate la un punct final REST

Abordarea reactivă nu se limitează la partea clientului în JAX-RS; poate fi folosit și pe partea serverului. De exemplu, voi crea mai întâi un script simplu în care pot solicita o listă de locații pentru o singură destinație. Pentru fiecare poziție, voi efectua un apel separat cu datele locației către alt punct pentru a obține valorile temperaturii. Interacțiunea destinațiilor va fi așa cum se arată în Figura 1.

Figura 1. Interacțiunea dintre destinații

Mai întâi definesc doar modelul de domeniu și apoi serviciile pentru fiecare model. Lista 5 arată cum este definită clasa Prognoză, care include clasele Locație și Temperatură.

Clasa publică Temperatură ( private Temperatură dublă; private String scale; // getters & setters ) public class Locație ( String name; public Location() () public Location( String name) ( this.name = name; ) // getters & setters ) public class Forecast ( private Location location; private Temperature temperature; public Forecast(Location location) ( this.location = location; ) public Forecast setTemperature (temperatura finală a temperaturii) ( this.temperature = temperatură; return this; ) // getters )
Pentru a încheia lista de predicții, clasa ServiceResponse este implementată în Lista 6.

Răspuns serviciu de clasă publică (Timp de procesare lungă privată; Listă privată previziuni = noua ArrayList<>(); public void setProcessingTime(long processingTime) ( this.processingTime = processingTime; ) public ServiceResponse previziuni(Lista previziuni) ( this.forecasts = prognoze; return this; ) // getters )
LocationResource , prezentată în Lista 7, definește trei modele de locații returnate cu calea /location .

@Path("/location") public class LocationResource ( @GET @Produces(MediaType.APPLICATION_JSON) public Response getLocations() ( Listă locații = nou ArrayList<>(); locations.add(new Location("Londra")); locations.add(new Location("Istanbul")); locations.add(new Location("Praga")); return Response.ok(new GenericEntity >(locatii)()).build(); ) )
TemperatureResource , afișat în Lista 8, returnează o valoare de temperatură generată aleatoriu între 30 și 50 pentru locația dată. O întârziere de 500 ms a fost adăugată implementării pentru a simula citirea senzorului.

@Path("/temperature") public class TemperatureResource ( @GET @Path("/(city)") @Produces(MediaType.APPLICATION_JSON) public Response getAverageTemperature(@PathParam("city") String cityName) ( Temperatura temperaturii = nou Temperature(); temperature.setTemperature((double) (nou Random().nextInt(20) + 30)); temperature.setScale("Celsius"); try ( Thread.sleep(500); ) catch (InterruptedException ignorat) ( ignored.printStackTrace(); ) return Response.ok(temperature).build(); ) )
Mai întâi, voi arăta o implementare a unei Resurse de prognoză sincrone (vezi Lista 9) care returnează toate locațiile. Apoi, pentru fiecare poziție, apelează serviciul de temperatură pentru a obține valorile în grade Celsius.

@Path("/forecast") clasă publică ForecastResource ( @Uri ("locație") private WebTarget locationTarget; @Uri ("temperature/(city)") private WebTarget temperatureTarget; @GET @Produces(MediaType.APPLICATION_JSON) public Response getLocationsWithTemperature () ( lung startTime = System.currentTimeMillis(); Răspuns ServiceResponse = nou ServiceResponse(); Listă locații = locationTarget .request() .get(new GenericType >()()); forEach(location -> ( Temperature temperature = temperatureTarget .resolveTemplate("oraș", location.getName()) .request() .get(Temperature.class); response.getForecasts().add(new Forecast(locație) .setTemperature (temperatura)); )); long endTime = System.currentTimeMillis(); response.setProcessingTime(endTime - startTime); returnează Response.ok(răspuns).build(); ) )
Când destinația prognozei este solicitată ca /forecast , veți obține rezultate similare cu Lista 10. Rețineți că cererea a durat 1,533 ms pentru procesare, ceea ce este logic, deoarece solicitarea temperaturilor din trei locații diferite în mod sincron adaugă până la 1,5 ms.

( „prognoze”: [ ( „locație”: ( „nume”: „Londra”), „temperatură”: ( „scale”: „Celsius”, „temperatură”: 33 ) ), ( „locație”: ( „nume” „: „Istanbul” ), „temperatură”: ( „scan”: „Celsius”, „temperature”: 38) ), ( „locație”: ( „nume”: „Praga” ), „temperature”: ( „scale ": "Celsius", "temperatură": 46 ) ) ], "processingTime": 1533 )
Până acum totul decurge conform planului. Este timpul să introduceți programarea reactivă pe partea de server, unde apelurile către fiecare locație pot fi efectuate în paralel după ce toate locațiile sunt primite. Acest lucru poate îmbunătăți în mod clar fluxul sincron prezentat mai devreme. Acest lucru se face în Lista 11, care arată definiția unei versiuni reactive a serviciului de prognoză.

@Path("/reactiveForecast") clasă publică ForecastReactiveResource ( @Uri ("locație") private WebTarget locationTarget; @Uri ("temperature/(city)") private WebTarget temperatureTarget; @GET @Produces(MediaType.APPLICATION_JSON) public void getLocationsWithTemperature (@Suspended final AsyncResponse async) (long startTime = System.currentTimeMillis(); // Creați o etapă pentru a prelua locațiile CompletionStage > locationCS = locationTarget.request() .rx() .get(new GenericType >() ()); // Prin crearea unei etape separate în etapa de locații, // descrisă mai sus, colectați lista de predicții // ca într-o etapă finală mare de CompletionStage > forecastCS = locationCS.thenCompose(locations -> ( // Creați o etapă pentru a obține previziuni // ca o listă CompletionStage > forecastList = // Transmite locații și procesează fiecare // separat locații.stream().map(location -> ( // Creați un pas pentru a obține // valorile temperaturii unui singur oraș // după numele său final CompletionStage tempCS = temperatureTarget .resolveTemplate("oraș", location.getName()) .request() .rx() .get(Temperature.class); // Apoi creați un CompletableFuture care // conține o instanță de prognoză cu // o locație și o valoare de temperatură returnată CompletableFuture.completedFuture(new Forecast(location)) .thenCombine(tempCS, Forecast::setTemperature); )).collect(Collectors.toList()); // Returnează instanța finală CompletableFuture în care // toate obiectele viitoare care pot fi completate prezentate sunt // finalizate returnează CompletableFuture.allOf(forecastList.toArray(new CompletableFuture)) .thenApply(v -> forecastList.stream() .map(CompletionStage::toCompletableFuture) ) .map(CompletableFuture::join) .collect(Collectors.toList())); )); // Creați o instanță ServiceResponse care // conține lista completă de predicții // împreună cu timpul de procesare. // Creați viitorul și combinați-l cu // forecastCS pentru a obține previziuni // și introduceți în răspunsul serviciului CompletableFuture.completedFuture(new ServiceResponse()) .thenCombine(forecastCS, ServiceResponse::forecasts) .whenCompleteAsync((response, throwable) - > ( response.setProcessingTime(System.currentTimeMillis() - startTime); async.resume(response); )); ) )
Implementarea reactivă poate părea complicată la prima vedere, dar după o privire mai atentă, veți observa că este destul de simplă. În implementarea ForecastReactiveResource, fac mai întâi un apel de client către serviciile de localizare folosind API-ul JAX-RS Reactive Client. După cum am menționat mai sus, aceasta este o adăugare pentru Java EE 8 și ajută la crearea unui apel reactiv pur și simplu cu metoda rx().

Acum creez o nouă etapă bazată pe locație pentru a colecta lista de predicții. Acestea vor fi stocate ca o listă de prognoze într-o etapă mare de finalizare numită forecastCS. În cele din urmă, voi crea un răspuns la apelul de serviciu folosind doar forecastCS .

Acum să colectăm previziunile ca o listă de etape de finalizare definite în variabila forecastList . Pentru a crea o etapă de finalizare pentru fiecare prognoză, trec datele de locație și apoi creez o variabilă tempCS, folosind din nou API-ul JAX-RS Reactive Client, care apelează serviciul de temperatură cu numele orașului. Aici, folosesc metoda resolveTemplate() pentru a construi clientul, iar acest lucru îmi permite să trec numele orașului constructorului ca parametru.

Ca ultim pas al streaming, fac un apel la CompletableFuture.completedFuture() , trecând noua instanță Forecast ca parametru. Combin acest viitor cu etapa tempCS, astfel încât să am valoarea temperaturii pentru locațiile repetate.

Metoda CompletableFuture.allOf() din Lista 11 convertește o listă de etape de finalizare în forecastCS . Executarea acestui pas returnează un viitor mare care poate fi finalizat atunci când toate futuresurile care se pot completa furnizate au fost finalizate.

Răspunsul serviciului este o instanță a clasei ServiceResponse, așa că creez un viitor finalizat și apoi concatenez etapa de finalizare a prognozeiCS cu lista de previziuni și calculez timpul de răspuns al serviciului.

Desigur, programarea reactivă forțează doar partea serverului să se execute asincron; partea client se va bloca până când serverul trimite un răspuns înapoi către solicitant. Pentru a depăși această problemă, Server Sent Events (SSE) poate fi folosit pentru a trimite un răspuns parțial de îndată ce acesta este disponibil, astfel încât valorile temperaturii pentru fiecare locație să fie trimise clientului unul câte unul. Ieșirea ForecastReactiveResource va fi similară cu Lista 12. După cum se arată în rezultat, timpul de procesare este de 515 ms, care este timpul ideal de execuție pentru obținerea valorilor temperaturii dintr-o singură locație.

( „prognoze”: [ ( „locație”: ( „nume”: „Londra”), „temperatură”: ( „scale”: „Celsius”, „temperatura”: 49 ) ), ( „locație”: ( „nume” „: „Istanbul” ), „temperatură”: ( „scale”: „Celsius”, „temperature”: 32 ) ), ( „locație”: ( „nume”: „Praga”) ), „temperature”: ( „scale „: „Celsius”, „temperatură”: 45 ) ) ], „processingTime”: 515 )
Concluzie

În exemplele din acest articol, am arătat mai întâi o modalitate sincronă de a obține prognoze folosind serviciile de locație și temperatură. Apoi, am trecut la o abordare reactivă pentru a avea procesarea asincronă între apelurile de serviciu. Când utilizați API-ul JAX-RS Reactive Client în Java EE 8 împreună cu clasele CompletionStage și CompletableFuture disponibile în Java 8, puterea procesării asincrone este dezlănțuită prin programarea reactivă.

Programarea reactivă este mai mult decât implementarea unui model asincron de la unul sincron; de asemenea, simplifică concepte precum etapa de cuibărit. Cu cât este folosit mai mult, cu atât va fi mai ușor să gestionați scenarii complexe în programare paralelă.

Vă mulțumim pentru atenție. Ca întotdeauna, așteptăm comentariile și întrebările dvs.

Puteți ajuta și transfera niște fonduri pentru dezvoltarea site-ului

Verificați informațiile. Este necesar să se verifice acuratețea faptelor și fiabilitatea informațiilor prezentate în acest articol. Ar trebui să existe explicații pe pagina de discuții... Wikipedia

Interactivitatea este un concept care dezvăluie natura și gradul de interacțiune dintre obiecte. Folosit în domenii: teoria informației, informatică și programare, sisteme de telecomunicații, sociologie, design industrial și altele. În ...... Wikipedia

Acest articol ar trebui să fie wikificat. Vă rugăm să formatați-l conform regulilor de formatare a articolelor. Acest termen are alte semnificații, vezi Electromash (sensuri) ... Wikipedia

tehnici psihoterapeutice străine- TEHNICI PROFUNDE Psihoterapie activa (Fromm Reichmann). Analiza ființei (Binswanger). Analiza destinului (Sondi). Analiza caracterului (W. Reich). Analiza I (H. Kohut, E. Erickson). Terapia analitică prin joc (M. Klein). Terapie analitică de familie (Richter).… … Marea Enciclopedie Psihologică

Cărți

  • Programare reactivă în C++. Proiectarea aplicațiilor paralele și asincrone, Pai Praseed, Abraham Peter. Proiectarea aplicațiilor paralele și asincrone folosind biblioteca RxCpp și C++ modern 17 Instrumente de programare paralelă acceptate Colaborativ...
  • , Nurkevich T., Christensen B.. În aceste zile, când programele sunt asincrone, iar răspunsul rapid este cea mai importantă proprietate, programarea reactivă va ajuta la scrierea unui cod mai fiabil, mai scalabil și mai rapid...
  • Programare reactivă cu RxJava, Tomas Nurkevich, Ben Christensen. În aceste zile, când programele sunt asincrone și răspunsul rapid este cea mai importantă proprietate, programarea reactivă vă va ajuta să scrieți cod mai fiabil, mai scalabil și mai rapid...

Principiile programării reactive nu sunt noi și pot fi urmărite până la lucrările fundamentale ale lui Jim Gray și Pat Helland asupra sistemului tandem în anii 70 și 80.

Acești oameni erau cu mult înaintea timpului lor. Doar în ultimii 5-10 ani industria tehnologică a fost nevoită să revizuiască „cele mai bune practici” existente pentru dezvoltarea sistemului de întreprindere. Ea a învățat să aplice cunoștințele despre principiile reactive ale lumii de astăzi a multi-core și cloud computing.

Baza unui sistem reactiv este transmiterea mesajelor, care creează o limită de timp între componente, le permite să fie decuplate în timp folosind paralelismul și spațiul, care distribuie sarcina și asigură mobilitate. Această decuplare este o cerință pentru izolarea completă între componente și formează baza atât pentru stabilitatea, cât și pentru rezistența sistemelor.

Fundamentele programării reactive

Această programare se concentrează pe fluxul de informații și pe propagarea modificărilor datelor. Când utilizați limbaje de programare, este ușor să separați fluxurile statice și dinamice, în timp ce modelul de bază va propaga automat modificările prin toate fluxurile de date. În termeni simpli, în programarea Rx, emisă de o componentă, și structura de bază furnizată de bibliotecile Rx, vor propaga acele modificări către o altă componentă înregistrată pentru a primi acele modificări. Programarea reactivă Rx constă din trei puncte cheie.

Principalele funcții ale componentelor:

  1. Observabilele nu sunt altceva decât fluxuri de date. Un observabil împachetează date care pot fi transmise de la un flux la altul. Practic emit date periodic sau o singură dată în ciclul lor de viață pe baza configurațiilor. Există diverși operatori care pot ajuta observatorul să trimită niște date specifice bazate pe anumite evenimente.
  2. Observatorii consumă fluxul de date emis de observabil. Observatorii se abonează folosind metoda de programare reactivă subscribeOn() pentru a primi date care trec către observabile. Ori de câte ori un observabil trimite date, toți observatorii înregistrați primesc datele în apel invers onNext(). Aici pot efectua diverse operațiuni, cum ar fi analizarea unui răspuns JSON sau actualizarea interfeței cu utilizatorul. Dacă există o eroare cauzată de observabil, observatorul o va primi în onError().
  3. Schedulers (schedule) este o componentă din Rx care le spune observatorilor și observatorilor pe ce fir ar trebui să ruleze. Puteți folosi metoda observOn() pentru a le spune observatorilor pe ce fir ar trebui să urmărească. Alternativ, schedOn() poate fi folosit pentru a spune observabilului pe ce fir ar trebui să ruleze.

În programarea reactivă, folosind firele principale implicite RxJava, cum ar fi Schedulers.newThread() va crea un fundal nou. Schedulers.io() va executa codul pe firul I/O.

Principalele beneficii ale Rx sunt utilizarea sporită a resurselor de calcul pe hardware multi-core și multi-procesor, performanță îmbunătățită prin reducerea punctelor și performanță îmbunătățită prin reducerea punctelor de serializare, conform Legii lui Amdahl și Legii scalabilității universale a lui Günther.

Al doilea beneficiu este productivitatea ridicată pentru dezvoltatori, deoarece paradigmele tradiționale de programare s-au străduit să ofere o abordare simplă și care poate fi întreținută pentru a face față calculelor și IO asincrone și neblocante. Programarea reactivă funcțională se ocupă de aceste sarcini, deoarece de obicei elimină necesitatea unei coordonări explicite între componentele active.

Acolo unde apare Rx, este creat procesul de creare a componentelor și compoziția fluxurilor de lucru. Pentru a profita din plin de execuția asincronă, activarea contrapresiunii este esențială pentru a evita suprasolicitarea sau mai degrabă consumul nerestricționat de resurse. Pentru a asigura o stare de echilibru în ceea ce privește fluxul de date, contrapresiunea bazată pe sarcină trimite cererea care curge în amonte și primește mesaje.

Astfel, principalele avantaje ale sistemului sunt:

  1. Performanță sporită - datorită capacității de a procesa rapid și constant cantități uriașe de date.
  2. UX îmbunătățit - datorită faptului că aplicația este mai receptivă la utilizator.
  3. Modificări și actualizări simplificate - datorită codului mai lizibil și mai ușor de prezis.

Dar chiar dacă Programarea Reactivă este un lucru foarte util atunci când construiești software modern, pentru a te gândi la un sistem la un nivel superior, trebuie să folosești un alt instrument - Arhitectura Reactivă pentru procesul de proiectare a sistemelor reactive. De asemenea, este important să ne amintim că există multe paradigme de programare și Rx este doar una dintre ele, ca orice instrument, nu este conceput pentru toate cazurile de utilizare.

Stabilitatea sistemului reactiv

Reziliența este capacitatea de răspuns la eșec și este o proprietate funcțională inerentă a unui sistem. Are nevoie de dezvoltare, nu doar de adăugare retroactivă la sistem. Reziliența programării javascript reactive depășește toleranța la erori și nu se datorează degradării, dar în caz de eșec, se poate corecta complet.

Acest lucru necesită izolarea componentelor și limitarea defecțiunilor pentru a evita propagarea defecțiunilor la componentele adiacente, ceea ce poate duce la scenarii catastrofale în cascadă. Astfel, cheia construirii sistemelor Resiliente este că acestea pot fi caracterizate ca mesaje trimise către alte componente care acționează ca supraveghetori și sunt gestionate dintr-un context securizat în afara componentei eșuate.

Aici, fiind bazate pe mesaje, aceste facilități se îndepărtează de lanțurile de apeluri sincrone strâns cuplate, fragile, profund imbricate, care sunt în mare parte ignorate. Ideea este de a separa gestionarea defecțiunilor de lanțul de apeluri, de exemplu, eliberând clientul de responsabilitatea gestionării defecțiunilor serverului.

Deoarece majoritatea sistemelor sunt în mod inerent complexe, unul dintre cele mai importante aspecte este să se asigure că arhitectura sistemului asigură o degradare minimă a performanței atât în ​​​​dezvoltare, cât și în întreținerea componentelor, reducând în același timp complexitatea aleatorie la minimum. Acest lucru este important deoarece pe parcursul ciclului de viață al unui sistem, dacă acesta nu este proiectat corespunzător, va deveni din ce în ce mai dificil să-l mențineți în funcțiune și va fi nevoie de mai mult timp și efort pentru a înțelege pentru a izola și rezolva problemele.

Sistemele reactive reprezintă cea mai productivă arhitectură de sistem, în contextul arhitecturilor multi-core, cloud și mobile:

  1. Izolarea defecțiunilor oferă pereți etanși între componente, prevenind defecțiunile în cascadă și limitând domeniul și gravitatea defecțiunilor.
  2. Ierarhiile de supraveghere oferă mai multe niveluri de protecție combinate cu capabilități de auto-vindecare, care elimină multe eșecuri temporare ale oricărui cost operațional de investigat.
  3. Omiterea mesajelor și transparența locației vă permit să dezactivați și să înlocuiți componente fără a afecta experiența utilizatorului final. Acest lucru reduce costul defecțiunilor, urgența lor relativă și resursele necesare pentru a le diagnostica și remedia.
  4. Replicarea reduce riscul pierderii datelor și reduce impactul eșecului asupra disponibilității recuperării și stocării informațiilor.
  5. Elasticitatea permite conservarea resurselor pe măsură ce utilizarea fluctuează, minimizând costurile operaționale la sarcină redusă și riscul de eșec sau investiții urgente în scalabilitate pe măsură ce sarcina crește.

Aplicațiile web pot beneficia foarte mult de stilul de dezvoltare Rx, care vă permite să compuneți fluxuri de lucru cerere-răspuns care includ ramificarea în apeluri de serviciu, preluarea asincronă a resurselor și compoziția răspunsului și sortarea ulterioară pentru client. Mai recent, evenimentele push-to-server și socket-urile web au devenit din ce în ce mai obișnuite în practică, iar realizarea acestui lucru la scară necesită o modalitate eficientă de a stoca multe conexiuni deschise și unde IO nu este blocat.

Există instrumente pentru aceasta, cum ar fi Streams și Futures, care facilitează conversiile neblocante și asincrone și le împing către clienți. Data Access Layer Reactive Programming - Actualizează și interogează datele într-o resursă eficientă, de preferință folosind baze de date SQL sau NoSQL cu drivere asincrone.

Aplicațiile web beneficiază și de dezvoltarea unui sistem reactiv pentru lucruri precum stocarea în cache distribuită, consistența datelor și notificările cu mai multe noduri. Aplicațiile web tradiționale folosesc de obicei noduri permanente. Dar de îndată ce programatorii încep să folosească Server-Sent-Events (SSE) și WebSockets, aceste noduri devin operaționale, deoarece, cel puțin, mențin starea conexiunii client, iar notificările push le sunt trimise în consecință. Acest lucru necesită dezvoltarea unui sistem reactiv, deoarece acesta este un domeniu în care adresarea destinatarilor prin mesagerie este importantă.

Esența programării reactive Java

Nu este obligatorie utilizarea Rx în sistemele reactive. Pentru că programarea Rx și sistemele reactive nu sunt același lucru. Deși sunt adesea folosite interschimbabil, nu sunt sinonime exacte și reflectă lucruri diferite. Sistemele reprezintă următorul nivel de „reactivitate”. Acest nivel implică decizii specifice de design și arhitectură care vă permit să creați aplicații robuste și flexibile.

Cu toate acestea, o idee foarte bună - o combinație de metode - aduce și mai multe beneficii aplicațiilor, deoarece le face și mai conectate, permite o utilizare mai eficientă a resurselor și oferă o latență mai mică. Când vine vorba de cantități uriașe de date sau multitasking, procesarea asincronă este adesea necesară pentru a menține sistemele rapide și receptive.

În Java, moștenirea vechii programari orientate pe obiecte, asincronia poate deveni foarte complicată și poate face codul greu de înțeles și de întreținut. Astfel, Rx este util în special pentru acest mediu „pur” orientat pe obiect, deoarece facilitează lucrul cu fire asincrone.

Cu cele mai recente versiuni, începând cu Java 8, Java însuși a făcut câteva încercări de a implementa reactivitatea nativă, dar aceste încercări nu sunt foarte populare în rândul dezvoltatorilor de astăzi. Cu toate acestea, există câteva implementări live și actualizate regulat de la terți pentru programarea reactivă Java care pot salva ziua și, prin urmare, sunt apreciate în special de dezvoltatorii Java.

Într-o aplicație tipică, este obișnuit să efectuați în mod repetat unele operațiuni de programare reactivă folosind RxJava, așa că trebuie să comparați viteza, CPU și utilizarea memoriei cu aceleași operațiuni care au fost implementate atât cu coroutine Kotlin, cât și cu RxJava. Acesta este un test de performanță inițial.

Ori de câte ori se aplică un instrument nou care va fi utilizat pe scară largă în întregul cod, este important să înțelegeți dacă va afecta performanța generală a aplicației înainte de a decide dacă merită să o utilizați. Practica de utilizare oferă răspunsul scurt: în majoritatea cazurilor, utilizatorii ar trebui să ia în considerare înlocuirea RxJava cu coroutine Kotlin, în special în Android.

Programarea reactivă folosind RxJava poate fi folosită în continuare într-un număr limitat de cazuri, iar în aceste cazuri, atât RxJava, cât și coroutine pot fi amestecate.

Motive simple:

  1. Ele oferă mult mai multă flexibilitate decât un Rx obișnuit.
  2. Oferă un set bogat de operatori pe colecții care vor arăta la fel ca și cu operatorii RxJava.
  3. Programarea reactivă Kotlin poate interacționa atunci când este necesar folosind rxjava.
  4. Sunt foarte ușoare și eficiente, având în vedere utilizarea mai mare a procesorului pentru colectarea gunoiului din toate obiectele create de RxJava.

Extensii reactive

Reactive Extensions (ReactiveX sau RX) este o bibliotecă care urmează principiile Rx, adică alcătuirea de programe asincrone și bazate pe evenimente folosind o secvență observabilă. Aceste biblioteci oferă multe interfețe și metode care ajută dezvoltatorii să scrie cod curat și simplu.

Extensiile reactive sunt disponibile în mai multe limbi. Programatorii sunt interesați în special de RxJava și RxAndroid, deoarece Android este zona cea mai concentrată.

Programarea reactivă folosind RxJava este o implementare a extensiei reactive Java de la Netflix. Practic este o bibliotecă care compune evenimente asincrone urmând modelul observatorului.

Este posibil să se creeze trafic asincron, să le transforme și să-l consume de către observator în diferite fluxuri de date. Biblioteca oferă o gamă largă de operatori uimitoare, cum ar fi hartă, unire și filtru, care pot fi aplicate fluxului de date. Pe măsură ce programatorul începe să folosească exemple de cod reale, el va afla mai multe despre operatori și conversii.

Multithreading în aplicațiile Android

Android" class="if uuid-2938324" src="/misc/i/gallery/73564/2938324.jpg" />

Programarea reactivă Android (RxAndroid) este specifică platformei Android, cu câteva clase adăugate peste RxJava. Mai precis, programatorii sunt introduși în RxAndroid (AndroidSchedulers.mainThread()), care joacă un rol important în susținerea conceptului de multithreading în aplicațiile Android.

Printre altele, experții recomandă utilizarea numai a bibliotecii RxJava. Chiar și datorită numărului mare de programatoare utilizate în programarea Android.

Mai jos este o listă de programatori și rezumatul acestora:

  1. Schedulers.io() - Folosit pentru a efectua operațiuni neintensive, cum ar fi apeluri de rețea, citiri de disc/fișier, operațiuni cu baze de date și care menține un pool de fire.
  2. AndroidSchedulers.mainThread() - Oferă acces la tema principală Thread/UI. De obicei, operațiunile au loc în acest fir, cum ar fi actualizarea interfeței cu utilizatorul, interacțiunea cu utilizatorul. Experții îi sfătuiesc pe utilizatori să nu efectueze operațiuni intensive pe acest flux, deoarece acest lucru poate provoca o blocare a aplicației sau un dialog ANR.
  3. Schedulers.newThread() - Folosind aceasta, un fir nou va fi creat de fiecare dată când este programată o sarcină. În general, se recomandă să nu folosiți programul pentru lucrări foarte lungi. Firele create cu newThread() nu vor fi reutilizate.
  4. Schedulers.computation() - Acest program poate fi folosit pentru a efectua operațiuni intensive de CPU, pentru a procesa date uriașe ale centrului de programare reactiv, pentru a procesa hărți de biți. Numărul de fire create folosind acest planificator depinde în întregime de numărul de nuclee CPU disponibile.
  5. Schedulers.single() - Acest planificator va executa toate sarcinile în următoarea ordine, care poate fi folosită atunci când este necesară necesitatea execuției secvențiale.
  6. Schedulers.immediate() - Acest planificator execută sarcina imediat prin blocarea sincronă a firului principal.
  7. Schedulers.trampoline() - Execută sarcini în modul First In-First Out. Toate sarcinile programate vor rula una după alta, limitând numărul de fire de fundal la unul.
  8. Schedulers.from () - vă permite să creați un planificator de la un executor, limitând numărul de fire create. Când pool-ul de fire este ocupat, sarcinile vor fi puse în coadă.

Acum că aveți un fundal bun în RxJava și RxAndroid, puteți trece la câteva exemple de cod pentru a înțelege mai bine conceptul. Pentru a începe, trebuie să adăugați dependențele RxJava și RxAndroid la proiectele build.gradle și să sincronizați proiectul.

Programare.

Observatorul este abonat la un Observable, astfel încât să poată începe să primească date folosind două metode:

  1. SubscribeOn(Schedulers.io()) - Spune observatorului să înceapă o sarcină pe un thread de fundal.
  2. ObservOn(AndroidSchedulers.mainThread()) - Spune observatorului să primească date pe firul de execuție Android UI.

Asta e tot, astfel încât programatorul să poată scrie primul său program de programare reactivă cu RxJava.

Întreprinderile și furnizorii de middleware au început să folosească Reactive, iar 2016-2018 a înregistrat o creștere uriașă a interesului corporativ pentru adoptarea acestei paradigme.

Rx oferă performanță dezvoltatorului prin eficiența resurselor la nivel de componente pentru transformarea logicii interne și a fluxului de date, în timp ce sistemele reactive oferă performanță pentru arhitecți și DevOps prin reziliența și elasticitatea la nivel de sistem. Sunt folosite pentru a crea „Cloud Native” și alte sisteme distribuite la scară largă. În practică, cărțile de programare reactivă Java sunt, de asemenea, utilizate pe scară largă cu metode care vă permit să combinați principiile proiectării sistemului reactiv.



Se încarcă...
Top