Traduzione e adattamento dei microservizi Java: una guida pratica . Parti precedenti della guida:
Diamo un'occhiata ai problemi inerenti ai microservizi in Java, iniziando con cose astratte e finendo con le librerie concrete.
Un caso interessante da considerare è il caso del ritardo. Ad esempio, il disco rigido del microservizio del risponditore è pieno e invece di impiegare 50 ms, sono necessari 10 secondi per rispondere. Diventa ancora più interessante quando si verifica un certo carico tale che la mancata risposta del tuo BillingService inizia a riversarsi nel tuo sistema. Come esempio illustrativo, immagina una cucina che avvia lentamente un “blocco” di tutti i camerieri del ristorante. Questa sezione ovviamente non può fornire una panoramica esaustiva del tema della resilienza dei microservizi, ma serve a ricordare agli sviluppatori che questo è in realtà qualcosa che deve essere affrontato e non ignorato prima del primo rilascio (cosa che, per esperienza, accade più spesso di quanto si pensi). non).segue). Una libreria popolare che ti aiuta a pensare alla latenza e alla tolleranza agli errori è Hystrix di Netflix. Usa la sua documentazione per approfondire l'argomento.
Il problema, tuttavia, è che questi tempi (artificiali) di avviamento bare metal e di ridistribuzione difficilmente contribuiscono al successo complessivo del progetto. Almeno influenzano molto meno di una solida infrastruttura quadro, una solida documentazione, una comunità e forti capacità di sviluppo. Quindi è meglio vederla in questo modo: Se finora:
E sebbene questo sia solo un semplice esempio, seppure tratto da un progetto reale (!), dimostra anche il seguente problema: i supermercati sono diventati estremamente popolari. Questo perché con loro non devi andare in 10 posti diversi per comprare verdura, limonata, pizza surgelata e carta igienica. Invece, vai in un posto: è più facile e veloce. Lo stesso vale per gli sviluppatori front-end e di microservizi.
In realtà, questo approccio è estremamente problematico. Nei paragrafi successivi cercheremo di spiegare il perché.
Il punto principale è che, come nel caso di DevOps, un approccio completo ai microservizi in una grande azienda, forse anche internazionale, comporta una serie di ulteriori problemi di comunicazione. E l’azienda deve prepararsi seriamente a questo.
Ora deciditi. Se leggi fino alla fine, ti saremo molto grati!
Come rendere resiliente un microservizio Java?
Ricorda che quando crei microservizi, stai essenzialmente scambiando chiamate al metodo JVM con chiamate HTTP sincrone o messaggistica asincrona. Mentre il completamento di una chiamata al metodo è per lo più garantito (salvo un arresto imprevisto della JVM), una chiamata di rete è inaffidabile per impostazione predefinita. Potrebbe funzionare, ma potrebbe non funzionare per vari motivi: la rete è sovraccarica, è stata implementata una nuova regola del firewall e così via. Per vedere come questo fa la differenza, diamo un'occhiata all'esempio BillingService.Modelli di resilienza HTTP/REST
Supponiamo che i clienti possano acquistare e-book sul sito Web della tua azienda. Per fare ciò, hai appena implementato un microservizio di fatturazione in grado di chiamare il tuo negozio online per generare fatture PDF effettive. Per ora, effettueremo questa chiamata in modo sincrono, su HTTP (anche se ha più senso chiamare questo servizio in modo asincrono, poiché la generazione di PDF non deve essere istantanea dal punto di vista dell'utente. Utilizzeremo lo stesso esempio nel prossimo sezione e osservare le differenze).@Service
class BillingService {
@Autowired
private HttpClient client;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
httpClient.send(invoiceRequest(user.getEmail(), invoice), responseHandler());
// ...
}
}
Per riassumere, ecco tre possibili risultati di questa chiamata HTTP.
- OK: la chiamata è andata a buon fine, l'account è stato creato correttamente.
- RITARDO: la chiamata è andata a buon fine, ma il completamento ha richiesto troppo tempo.
- ERRORE. La chiamata non è riuscita, potresti aver inviato una richiesta incompatibile o il sistema potrebbe non funzionare.
![Una guida ai microservizi Java. Parte 3: domande generali - 2](https://cdn.javarush.com/images/article/b1fcc533-d15c-484f-bbd4-f67db6eceed3/800.webp)
Modelli di resilienza della messaggistica
Diamo uno sguardo più da vicino alla comunicazione asincrona. Il nostro programma BillingService potrebbe ora assomigliare a questo, presupponendo che stiamo utilizzando Spring e RabbitMQ per la messaggistica. Per creare un account, ora inviamo un messaggio al nostro broker di messaggi RabbitMQ, dove ci sono diversi lavoratori in attesa di nuovi messaggi. Questi lavoratori creano fatture PDF e le inviano agli utenti appropriati.@Service
class BillingService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void bill(User user, Plan plan) {
Invoice invoice = createInvoice(user, plan);
// преобразует счет, например, в json и использует его How тело messages
rabbitTemplate.convertAndSend(exchange, routingkey, invoice);
// ...
}
}
I potenziali errori ora appaiono leggermente diversi, poiché non ricevi più risposte OK o ERROR immediate come avveniva con una connessione HTTP sincrona. Invece, potremmo avere tre potenziali scenari che potrebbero andare storti, il che potrebbe sollevare le seguenti domande:
- Il mio messaggio è stato consegnato e utilizzato dal dipendente? Oppure è perduto? (L'utente non riceve fattura).
- Il mio messaggio è stato recapitato una sola volta? Oppure consegnato più di una volta ed elaborato una sola volta? (L'utente riceverà più fatture).
- Configurazione: da "Ho utilizzato le chiavi/nomi di instradamento corretti per lo scambio" a "Il mio broker di messaggi è configurato e gestito correttamente o le sue code sono piene?" (L'utente non riceve fattura).
- Se utilizzi implementazioni JMS come ActiveMQ, puoi scambiare la velocità con la garanzia di commit a due fasi (XA).
- Se utilizzi RabbitMQ, leggi prima questa guida, quindi pensa attentamente alle conferme, alla tolleranza agli errori e all'affidabilità dei messaggi in generale.
- Forse qualcuno è esperto nella configurazione di server Active o RabbitMQ, soprattutto in combinazione con clustering e Docker (qualcuno? ;))
Quale framework sarebbe la soluzione migliore per i microservizi Java?
Da un lato puoi installare un'opzione molto popolare come Spring Boot . Semplifica molto la creazione di file .jar, viene fornito con un server Web integrato come Tomcat o Jetty e può essere eseguito rapidamente e ovunque. Ideale per creare applicazioni di microservizi. Recentemente sono comparsi un paio di framework di microservizi specializzati, Kubernetes o GraalVM , parzialmente ispirati alla programmazione reattiva. Ecco alcuni contendenti più interessanti: Quarkus , Micronaut , Vert.x , Helidon . Alla fine dovrai scegliere tu stesso, ma possiamo darti un paio di consigli che potrebbero non essere del tutto standard: ad eccezione di Spring Boot, tutti i framework di microservizi sono generalmente commercializzati come incredibilmente veloci, con avvio quasi istantaneo , utilizzo ridotto della memoria, scalabilità all'infinito. I materiali di marketing in genere presentano una grafica impressionante che mostra la piattaforma accanto al colosso Spring Boot o l'una con l'altra. Questo, in teoria, risparmia i nervi agli sviluppatori che supportano progetti legacy, che a volte richiedono diversi minuti per essere caricati. Oppure sviluppatori che lavorano nel cloud e desiderano avviare/arrestare tutti i microcontenitori di cui hanno attualmente bisogno entro 50 ms.![Una guida ai microservizi Java. Parte 3: domande generali - 3](https://cdn.javarush.com/images/article/81482268-0c40-474e-aafc-89c9ed55a5f5/800.webp)
- Lasci che i tuoi ORM dilaghino, generando centinaia di query per flussi di lavoro semplici.
- Hai bisogno di infiniti gigabyte per gestire il tuo monolite moderatamente complesso.
- Hai così tanto codice e la complessità è così elevata (non stiamo parlando di avviatori potenzialmente lenti come Hibernate ora) che il caricamento della tua applicazione richiede diversi minuti.
Quali librerie sono le migliori per le chiamate REST Java sincrone?
Dal punto di vista tecnico di basso livello, probabilmente ti ritroverai con una delle seguenti librerie client HTTP: HttpClient nativo di Java (da Java 11), HttpClient di Apache o OkHttp . Tieni presente che dico "probabilmente" qui perché ci sono altre opzioni, che vanno dai buoni vecchi client JAX-RS ai moderni client WebSocket . In ogni caso, la tendenza è quella di generare un client HTTP, evitando di armeggiare personalmente con le chiamate HTTP. Per fare ciò, devi dare un'occhiata al progetto OpenFeign e alla sua documentazione come punto di partenza per ulteriori letture.Quali sono i migliori broker per la messaggistica Java asincrona?
Molto probabilmente incontrerai i popolari ActiveMQ (Classic o Artemis) , RabbitMQ o Kafka .- ActiveMQ e RabbitMQ sono broker di messaggi tradizionali e completi. Implicano l’interazione di un “broker intelligente” e di “utenti stupidi”.
- Storicamente, ActiveMQ ha avuto il vantaggio di un facile incorporamento (per i test), che può essere mitigato con le impostazioni RabbitMQ/Docker/TestContainer.
- Kafka non può essere definito un tradizionale broker “intelligente”. Si tratta invece di un archivio di messaggi relativamente "stupido" (file di registro) che richiede l'elaborazione da parte di consumatori intelligenti.
Quali librerie posso utilizzare per testare i microservizi?
Dipende dal tuo stack. Se disponi di un ecosistema Spring distribuito, sarebbe saggio utilizzare gli strumenti specifici del framework . Se JavaEE è qualcosa come Arquillian . Potrebbe valere la pena dare un'occhiata a Docker e all'ottima libreria Testcontainers , che aiuta in particolare a configurare facilmente e rapidamente un database Oracle per lo sviluppo locale o i test di integrazione. Per testare simulatamente interi server HTTP, controlla Wiremock . Per testare la messaggistica asincrona, prova a implementare ActiveMQ o RabbitMQ e quindi a scrivere i test utilizzando Awaitility DSL . Inoltre, vengono utilizzati tutti gli strumenti abituali: Junit , TestNG per AssertJ e Mockito . Tieni presente che questo non è un elenco completo. Se non trovi il tuo strumento preferito qui, pubblicalo nella sezione commenti.Come abilitare la registrazione per tutti i microservizi Java?
Il logging nel caso dei microservizi è un argomento interessante e piuttosto complesso. Invece di avere un file di registro che puoi manipolare con i comandi less o grep, ora hai n file di registro e non vuoi che siano troppo sparsi. Le caratteristiche dell'ecosistema del logging sono ben descritte in questo articolo (in inglese). Assicurati di leggerlo e presta attenzione alla sezione Registrazione centralizzata dal punto di vista dei microservizi . In pratica vi imbatterete in diversi approcci: l'amministratore di sistema scrive determinati script che raccolgono e uniscono file di registro da diversi server in un unico file di registro e li mette sui server FTP per il download. Esecuzione di combinazioni cat/grep/unig/sort in sessioni SSH parallele. Questo è esattamente ciò che fa Amazon AWS e puoi farlo sapere al tuo manager. Utilizza uno strumento come Graylog o ELK Stack (Elasticsearch, Logstash, Kibana)Come si trovano i miei microservizi?
Finora abbiamo dato per scontato che i nostri microservizi si conoscessero tra loro e conoscessero il corrispondente IPS. Parliamo di configurazione statica. Quindi, il nostro monolite bancario [ip = 192.168.200.1] sa che deve comunicare con il server dei rischi [ip = 192.168.200.2], che è codificato nel file delle proprietà. Tuttavia, puoi rendere le cose più dinamiche:- Utilizza un server di configurazione basato su cloud da cui tutti i microservizi estraggono le proprie configurazioni invece di distribuire file application.properties sui propri microservizi.
- Poiché le istanze del tuo servizio possono cambiare dinamicamente la loro posizione, vale la pena guardare i servizi che sanno dove risiedono i tuoi servizi, quali sono i loro IP e come instradarli.
- Ora che tutto è dinamico, sorgono nuovi problemi, come l’elezione automatica di un leader: chi è il maestro che lavora su determinati compiti, per non elaborarli due volte, ad esempio? Chi sostituisce un leader quando fallisce? Su quali basi avviene la sostituzione?
Come organizzare l'autorizzazione e l'autenticazione utilizzando i microservizi Java?
Anche questo argomento merita una storia a parte. Anche in questo caso, le opzioni vanno dall'autenticazione HTTPS di base codificata con framework di sicurezza personalizzati all'esecuzione di un'installazione Oauth2 con il proprio server di autorizzazione.Come posso assicurarmi che tutti i miei ambienti abbiano lo stesso aspetto?
Ciò che vale per le distribuzioni senza microservizio vale anche per le distribuzioni che ne hanno uno. Prova una combinazione di Docker/Testcontainers e Scripting/Ansible.Nessuna domanda: brevemente su YAML
Allontaniamoci per un momento dalle librerie e dai problemi correlati e diamo una rapida occhiata a Yaml. Questo formato di file viene utilizzato di fatto come formato per "scrivere la configurazione come codice". Viene utilizzato anche da strumenti semplici come Ansible e giganti come Kubernetes. Per provare il problema del rientro YAML, prova a scrivere un semplice file Ansible e vedi quanto devi modificare il file prima che funzioni come previsto. E questo nonostante il formato sia supportato da tutti i principali IDE! Dopodiché, torna per finire di leggere questa guida.Yaml:
- is:
- so
- great
E le transazioni distribuite? Test delle prestazioni? Altri argomenti?
Forse un giorno, nelle future edizioni del manuale. Per ora, questo è tutto. Resta con noi!Problemi concettuali con i microservizi
Oltre ai problemi specifici dei microservizi in Java, ci sono altri problemi, diciamo, che compaiono in qualsiasi progetto di microservizi. Si riferiscono principalmente all'organizzazione, al team e al management.Mancata corrispondenza di frontend e backend
La mancata corrispondenza di frontend e backend è un problema molto comune in molti progetti di microservizi. Cosa significa? Solo che nei buoni vecchi monoliti, gli sviluppatori dell'interfaccia web avevano una fonte specifica per ottenere dati. Nei progetti di microservizi, gli sviluppatori front-end improvvisamente hanno n fonti da cui ottenere dati. Immagina di creare una sorta di progetto di microservizi IoT (Internet of Things) in Java. Diciamo che gestisci macchine geodetiche e forni industriali in tutta Europa. E questi forni ti inviano aggiornamenti regolari con le loro temperature e simili. Prima o poi potresti voler trovare i forni nell'interfaccia utente di amministrazione, magari utilizzando i microservizi "ricerca forno". A seconda della severità con cui le controparti backend applicano le leggi sulla progettazione basata sul dominio o sui microservizi, un microservizio "trova forno" può restituire solo gli ID forno e non altri dati come tipo, modello o posizione. Per fare ciò, gli sviluppatori frontend dovranno effettuare una o n chiamate aggiuntive (a seconda dell'implementazione del paging) nel microservizio "ottieni dati forno" con gli ID ricevuti dal primo microservizio.![Una guida ai microservizi Java. Parte 3: domande generali - 4](https://cdn.javarush.com/images/article/e7b2ca26-43c9-4ed5-a3c2-103b0e34c052/800.webp)
Aspettative della gestione
Il management ha l’errata impressione di dover assumere un numero infinito di sviluppatori per un progetto (generale), poiché ora gli sviluppatori possono lavorare in modo completamente indipendente l’uno dall’altro, ciascuno sul proprio microservizio. Alla fine (poco prima del lancio) è necessario solo un piccolo lavoro di integrazione.![Una guida ai microservizi Java. Parte 3: domande generali - 5](https://cdn.javarush.com/images/article/6d63eedb-63e1-4ef6-94ae-d3405cafc594/800.webp)
I “pezzi più piccoli” non equivalgono a “pezzi migliori”
Sarebbe un grosso errore presumere che il codice diviso in 20 parti sarà necessariamente di qualità superiore rispetto a un pezzo intero. Anche se consideriamo la qualità da un punto di vista puramente tecnico, i nostri singoli servizi potrebbero comunque eseguire 400 query di Hibernate per selezionare un utente dal database, attraversando strati di codice non supportato. Ancora una volta, torniamo alla citazione di Simon Brown: se non si riescono a costruire correttamente i monoliti, sarà difficile costruire microservizi adeguati. Spesso è estremamente tardi per parlare di tolleranza agli errori nei progetti di microservizi. Tanto che a volte è spaventoso vedere come funzionano i microservizi nei progetti reali. La ragione di ciò è che gli sviluppatori Java non sono sempre pronti a studiare la tolleranza agli errori, il networking e altri argomenti correlati al livello adeguato. I “pezzi” stessi sono più piccoli, ma le “parti tecniche” sono più grandi. Immagina che al tuo team di microservizi venga chiesto di scrivere un microservizio tecnico per l'accesso a un sistema di database, qualcosa del genere:@Controller
class LoginController {
// ...
@PostMapping("/login")
public boolean login(String username, String password) {
User user = userDao.findByUserName(username);
if (user == null) {
// обработка варианта с несуществующим пользователем
return false;
}
if (!user.getPassword().equals(hashed(password))) {
// обработка неверного пароля
return false;
}
// 'Ю-ху, залогинorсь!';
// установите cookies, делайте, что угодно
return true;
}
}
Ora il tuo team potrebbe decidere (e forse anche convincere gli uomini d'affari) che questo è tutto troppo semplice e noioso, invece di scrivere un servizio di accesso, è meglio scrivere un microservizio UserStateChanged davvero utile senza alcuna implicazione aziendale reale e tangibile. E poiché alcune persone attualmente trattano Java come un dinosauro, scriviamo il nostro microservizio UserStateChanged nell'elegante Erlang. E proviamo a usare gli alberi rosso-neri da qualche parte, perché Steve Yegge ha scritto che devi conoscerli a fondo per candidarti a Google. Dal punto di vista dell'integrazione, della manutenzione e della progettazione complessiva, questo è altrettanto negativo quanto scrivere strati di codice spaghetti all'interno di un singolo monolite. Un esempio artificiale e banale? Questo è vero. Tuttavia, questo può accadere nella realtà.
Meno pezzi, meno comprensione
Quindi sorge naturalmente la domanda sulla comprensione del sistema nel suo insieme, dei suoi processi e flussi di lavoro, ma allo stesso tempo tu, come sviluppatore, sei responsabile solo di lavorare sul tuo microservizio isolato [95: login-101: updateUserProfile]. Si armonizza con il paragrafo precedente, ma a seconda dell'organizzazione, del livello di fiducia e di comunicazione, ciò può portare a molta confusione, alzate di spalle e colpe in caso di interruzione accidentale della catena dei microservizi. E non c’è nessuno che si assumerebbe la piena responsabilità di quanto accaduto. E non è affatto una questione di disonestà. In effetti, è molto difficile collegare le diverse parti e comprenderne il posto nel quadro generale del progetto.Comunicazioni e servizi
Il livello di comunicazione e di servizio varia notevolmente a seconda delle dimensioni dell'azienda. Tuttavia, la relazione generale è ovvia: più sono, più sono problematici.- Chi esegue il microservizio n. 47?
- Hanno appena distribuito una nuova versione incompatibile di un microservizio? Dove è stato documentato ciò?
- Con chi devo parlare per richiedere una nuova funzionalità?
- Chi supporterà quel microservizio a Erlang, dopo che l’unico che conosceva questa lingua lasciò l’azienda?
- Tutti i nostri team di microservizi lavorano non solo con linguaggi di programmazione diversi, ma anche con fusi orari diversi! Come coordiniamo correttamente tutto questo?
![Una guida ai microservizi Java. Parte 3: domande generali - 6](https://cdn.javarush.com/images/article/e600574d-255a-47a7-ae88-90390da6e8d9/800.webp)
conclusioni
Dopo aver letto questo articolo, potresti pensare che l'autore sia un ardente oppositore dei microservizi. Questo non è del tutto vero: in sostanza sto cercando di evidenziare punti a cui pochi prestano attenzione nella folle corsa alle nuove tecnologie.Microservizi o monolite?
L'utilizzo dei microservizi Java sempre e ovunque è un estremo. L'altro risulta essere qualcosa come centinaia di buoni vecchi moduli Maven in un monolite. Il tuo compito è trovare il giusto equilibrio. Ciò è particolarmente vero per i nuovi progetti. Non c’è nulla che ti impedisca di adottare un approccio più conservativo e “monolitico” e di creare meno moduli Maven validi, piuttosto che iniziare con venti microservizi pronti per il cloud.I microservizi generano ulteriore complessità
Tieni presente che più microservizi hai e meno DevOps veramente potenti hai (no, eseguire un paio di script Ansible o eseguire la distribuzione su Heroku non conta!), più problemi avrai più avanti nel processo. Anche solo leggere fino alla fine la sezione di questa guida dedicata alle domande generali sui microservizi Java è un compito piuttosto noioso. Pensa attentamente a implementare soluzioni a tutti questi problemi infrastrutturali e all'improvviso ti renderai conto che non si tratta più di programmazione aziendale (ciò per cui vieni pagato) ma piuttosto di sistemare più tecnologia su ancora più tecnologia. Shiva Prasad Reddy lo ha riassunto perfettamente nel suo blog : “Non hai idea di quanto sia terribile quando un team trascorre il 70% del tempo a lottare con questa moderna infrastruttura e solo il 30% del tempo è lasciato per eseguire la vera logica aziendale. " Shiva Prasad ReddyVale la pena creare microservizi Java?
Per rispondere a questa domanda, vorrei concludere questo articolo con un teaser molto sfacciato, simile a un'intervista di Google. Se conosci la risposta a questa domanda per esperienza, anche se non sembra avere nulla a che fare con i microservizi, potresti essere pronto per un approccio basato sui microservizi.Scenario
Immagina di avere un monolite Java in esecuzione da solo sul più piccolo server dedicato Hetzner . Lo stesso vale per il server del database, anch'esso è in esecuzione su una macchina Hetzner simile . E supponiamo anche che il tuo monolite Java sia in grado di gestire flussi di lavoro, ad esempio la registrazione dell'utente, e che tu non stia creando centinaia di query di database per flusso di lavoro, ma un numero più ragionevole (<10).Domanda
Quante connessioni al database dovrebbe aprire il tuo monolite Java (pool di connessioni) sul tuo server database? Perché? Quanti utenti attivi simultanei pensi che il tuo monolite possa (approssimativamente) scalare?Risposta
Lascia la tua risposta a queste domande nella sezione commenti. Aspetto con ansia tutte le risposte.![Una guida ai microservizi Java. Parte 3: domande generali - 8](https://cdn.javarush.com/images/article/cce2a448-fe19-4d78-8abf-b00a8f78a586/800.webp)
GO TO FULL VERSION