Nel nostro precedente articolo su Mutiny abbiamo coperto alcune basi, ma oggi andiamo più a fondo. Probabilmente già sapete che Mutiny ruota attorno a due elementi chiave: Uni
e Multi
. Pensate a Uni
come al vostro alleato per operazioni asincrone singole, mentre Multi
è il re della festa, gestendo flussi di eventi come un professionista.
Uni<String> singleResult = Uni.createFrom().item("Hello, Mutiny!");
Multi<Integer> numberStream = Multi.createFrom().range(1, 10);
Composizione degli Eventi: Quando i Flussi Asincroni si Incontrano
Ora, passiamo alla parte interessante: combinare più flussi asincroni. È come essere un DJ, ma invece di mixare tracce, orchestrate flussi di dati.
La Tecnica del Combine
Immaginate di recuperare i dati di un utente e il suo ultimo ordine contemporaneamente. Ecco come farlo con Mutiny:
Uni<User> userUni = fetchUser(userId);
Uni<Order> orderUni = fetchLatestOrder(userId);
Uni<String> combined = Uni.combine()
.all().unis(userUni, orderUni)
.asTuple()
.map(tuple -> tuple.getItem1().getName() + " ha ordinato " + tuple.getItem2().getProductName());
Fantastico, vero? Abbiamo appena creato una sinfonia di operazioni asincrone.
La Manovra Zip
Ma aspettate, c'è di più! Quando avete bisogno di accoppiare elementi da due flussi, zip
viene in soccorso:
Multi<Integer> numbers = Multi.createFrom().range(1, 5);
Multi<String> letters = Multi.createFrom().items("a", "b", "c", "d", "e");
Multi<String> zipped = numbers.zip(letters, (n, l) -> n + l);
// Risultato: 1a, 2b, 3c, 4d, 5e
Gestione degli Errori: Quando l'Asincrono va Storto
Ammettiamolo, nel mondo asincrono, gli errori sono come ospiti indesiderati a una festa. Ma non temete! Mutiny vi copre le spalle con alcuni trucchi per la gestione degli errori.
La Danza del Recupero e del Riprova
Uni<String> flakeyService = callUnreliableService()
.onFailure().retry().atMost(3)
.onFailure().recoverWithItem("Piano di Riserva Attivato!");
Questo snippet prova il vostro servizio instabile tre volte prima di ricorrere a un piano di riserva. È come avere una rete di sicurezza... per la vostra rete di sicurezza.
Il Ribaltamento del Fallback
A volte, avete solo bisogno di un piano B:
Uni<WeatherReport> weatherReport = getWeatherReport()
.onFailure().call(this::logError)
.onFailure().fallback(WeatherReport::getDefaultReport);
Se il recupero del bollettino meteorologico fallisce, registriamo l'errore e restituiamo un rapporto predefinito. Perché, diciamocelo, "soleggiato con possibilità di eccezioni" non è una vera previsione.
Controllo del Flusso: Domare il Torrente di Dati
Quando si gestiscono flussi di dati ad alta velocità, è cruciale evitare di affogare in un mare di eventi. Mutiny offre diversi strumenti per mantenervi a galla.
La Valvola di Regolazione
Multi<String> throttledStream = sourceStream
.throttle().iterations(10).per(Duration.ofSeconds(1))
.onOverflow().drop();
Questo codice assicura che non elaboriamo più di 10 elementi al secondo, scartando l'eccesso. È come avere un buttafuori per il vostro nightclub di dati.
Il Supporto alla Contropressione
Quando avete bisogno di più controllo sulla contropressione:
Multi<Data> controlledStream = sourceStream
.onOverflow().buffer(100)
.onItem().transformToUniAndMerge(this::processSlowly);
Qui, accumuliamo fino a 100 elementi e li elaboriamo uno per uno, assicurandoci che le nostre operazioni a valle non siano sopraffatte. È l'equivalente asincrono di "chi va piano va sano e va lontano".
Maestria Multi: Streaming come un Pro
Lavorare con Multi
è dove Mutiny brilla davvero. È come avere un coltellino svizzero per i flussi di dati. (Accidenti, l'ho fatto di nuovo con il coltellino svizzero. Diciamo che è come avere un'officina completamente attrezzata per i flussi di dati.)
La Trifecta Filter-Map-Collect
Multi.createFrom().range(1, 100)
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.collect().asList()
.subscribe().with(
list -> System.out.println("Quadrati pari: " + list),
error -> System.err.println("Oops: " + error)
);
Questo snippet filtra i numeri pari, li eleva al quadrato, raccoglie i risultati in una lista e poi li stampa. È come una catena di montaggio per i vostri dati, ma molto più cool.
Transazioni Asincrone: Il Modo di Mutiny
Gestire le transazioni in un mondo asincrono può essere complicato, ma Mutiny lo rende semplice come una passeggiata nel parco.
Uni<TransactionResult> txResult = Uni.createFrom().item(() -> beginTransaction())
.chain(tx -> performOperation1(tx)
.chain(() -> performOperation2(tx))
.chain(() -> commitTransaction(tx))
.onFailure().call(() -> rollbackTransaction(tx))
);
Questo codice avvia una transazione, esegue due operazioni e la conferma o annulla in base al risultato. È come avere un'imbracatura di sicurezza mentre camminate su una fune.
Colmare il Divario tra Sincrono e Asincrono: Il Meglio di Entrambi i Mondi
A volte, è necessario mescolare codice sincrono e asincrono. Mutiny vi copre:
CompletableFuture<String> future = someAsyncOperation();
Uni<String> uni = Uni.createFrom().completionStage(future);
// O viceversa
Uni<Integer> uni = Uni.createFrom().item(() -> 42);
CompletableFuture<Integer> future = uni.subscribeAsCompletionStage();
È come essere bilingue nel mondo della programmazione: potete passare senza sforzo tra i dialetti sincroni e asincroni.
Universo Parallelo: Concorrenza con Mutiny
Quando avete bisogno di accelerare le cose, il parallelismo è il vostro amico:
Multi<String> results = Multi.createFrom().iterable(hugeListOfUrls)
.onItem().transformToUniAndMerge(url ->
Uni.createFrom().item(() -> fetchData(url))
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool())
)
.collect().asList();
Questo codice recupera dati da più URL in parallelo, utilizzando il pool di lavoratori di Quarkus. È come avere una squadra di ninja che recupera i vostri dati: veloce ed efficiente.
API REST Reattive: L'Edizione Mutiny
Costruire API REST reattive con Mutiny è un gioco da ragazzi. Ecco un esempio veloce:
@Path("/users")
public class UserResource {
@Inject
UserService userService;
@GET
@Path("/{id}")
public Uni<Response> getUser(@PathParam("id") Long id) {
return userService.findById(id)
.onItem().transform(user -> Response.ok(user).build())
.onFailure().recoverWithItem(Response.status(Status.NOT_FOUND).build());
}
}
Questo endpoint restituisce un utente per ID, gestendo il caso di non trovato con eleganza. È reattivo, è pulito ed è pronto per alta concorrenza.
Conclusione: Le Migliori Pratiche di Mutiny
Concludendo il nostro viaggio nel meraviglioso mondo asincrono di Mutiny, ecco alcuni pensieri finali:
- Abbracciate il paradigma reattivo: pensate in termini di flussi ed eventi.
- Usate
Uni
per operazioni asincrone singole eMulti
per flussi. - Gestite gli errori in modo proattivo: la catena
onFailure()
è vostra amica. - Controllate il flusso: usate meccanismi di contropressione per evitare di sovraccaricare il sistema.
- Combinate codice sincrono e asincrono con attenzione: Mutiny fornisce strumenti, ma usateli con giudizio.
- Testate a fondo: il codice asincrono può essere complicato, quindi test approfonditi sono cruciali.
Ricordate, con grande potere viene grande responsabilità. Mutiny vi dà gli strumenti per costruire applicazioni altamente concorrenti ed efficienti, ma sta a voi usarli saggiamente.
Ora andate e conquistate il mondo asincrono con Mutiny! E ricordate, nel mondo della programmazione reattiva, l'unica costante è il cambiamento. Buona programmazione!