{"id":80035,"date":"2020-05-02T13:42:54","date_gmt":"2020-05-02T11:42:54","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny"},"modified":"2020-05-02T13:42:54","modified_gmt":"2020-05-02T11:42:54","slug":"udobnye-arhitekturnye-patterny","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","title":{"rendered":"Modelli architetturali pratici","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Ciao, Habr!<\/p>\n<p><\/p>\n<p>Alla luce degli attuali eventi legati al coronavirus, molti servizi online hanno registrato un aumento del carico di lavoro. Ad esempio, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.independent.co.uk\/life-style\/gadgets-and-tech\/news\/coronavirus-ocado-down-app-website-stockpiling-food-online-delivery-a9402216.html\">una delle catene di distribuzione nel Regno Unito ha semplicemente arrestato il sito per gli ordini online<\/a><\/noindex>, poich\u00e9 non c'erano sufficienti risorse. E non \u00e8 sempre possibile aumentare la potenza del server semplicemente aggiungendo hardware pi\u00f9 potente, tuttavia \u00e8 necessario gestire le richieste dei clienti (o passeranno ai concorrenti).<\/p>\n<p><\/p>\n<p>In questo articolo parler\u00f2 brevemente delle pratiche popolari che consentono di creare un servizio veloce e resistente ai guasti. Tuttavia, tra le possibili schemi di sviluppo, ho selezionato solo quelli che attualmente <strong>sono facili da usare<\/strong>. Per ciascun punto, avete gi\u00e0 a disposizione librerie pronte o avete la possibilit\u00e0 di risolvere il problema tramite una piattaforma cloud.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1 id=\"gorizontalnoe-masshtabirovanie\">Scalabilit\u00e0 orizzontale<\/h1>\n<p><\/p>\n<p>Il punto pi\u00f9 semplice e noto a tutti. In generale, le due modalit\u00e0 di distribuzione del carico che si incontrano pi\u00f9 frequentemente sono la scalabilit\u00e0 orizzontale e verticale. <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9C%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D1%83%D0%B5%D0%BC%D0%BE%D1%81%D1%82%D1%8C#%D0%93%D0%BE%D1%80%D0%B8%D0%B7%D0%BE%D0%BD%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BC%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\">Nel primo caso<\/a><\/noindex> consentite ai servizi di lavorare in parallelo, distribuendo cos\u00ec il carico tra di essi. <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9C%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D1%83%D0%B5%D0%BC%D0%BE%D1%81%D1%82%D1%8C#%D0%92%D0%B5%D1%80%D1%82%D0%B8%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BC%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\">Nel secondo<\/a><\/noindex> ordinate server pi\u00f9 potenti o ottimizzate il codice.<\/p>\n<p><\/p>\n<p>Per esempio, prendo come riferimento un'astratta soluzione di archiviazione cloud, cio\u00e8 un qualche equivalente di OwnCloud, OneDrive e cos\u00ec via.<\/p>\n<p><\/p>\n<p>L'immagine standard di tale schema \u00e8 qui sotto, tuttavia essa dimostra solo la complessit\u00e0 del sistema. Dobbiamo infatti sincronizzare i servizi in qualche modo. Cosa succede se un utente ha salvato un file sul tablet e poi vuole visualizzarlo sul telefono?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/7512c6d8783f5e0a38cc0861b01e54c8.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nLa differenza tra gli approcci: nella scalabilit\u00e0 verticale siamo disposti ad aumentare la potenza dei nodi, mentre in quella orizzontale aggiungiamo nuovi nodi per distribuire il carico.<\/p>\n<p><\/p>\n<h1 id=\"cqrs\">CQRS<\/h1>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/martinfowler.com\/bliki\/CQRS.html\">Command Query Responsibility Segregation<\/a><\/noindex> \u00e8 un pattern piuttosto importante, poich\u00e9 consente a diversi clienti non solo di connettersi a diversi servizi, ma anche di ricevere flussi di eventi identici. I suoi vantaggi non sono cos\u00ec evidenti per una semplice applicazione, tuttavia \u00e8 estremamente importante (e semplice) per un servizio carico. La sua essenza: i flussi di dati in entrata e in uscita non devono incrociarsi. Questo significa che non puoi inviare una richiesta e aspettarti una risposta; invece, invii una richiesta al servizio A, ma ricevi la risposta nel servizio B.<\/p>\n<p><\/p>\n<p>Il primo vantaggio di questo approccio \u00e8 la possibilit\u00e0 di interrompere la connessione (in senso ampio) durante l'esecuzione di una lunga richiesta. Prendiamo come esempio una sequenza pi\u00f9 o meno standard:<\/p>\n<p><\/p>\n<ol>\n<li>Il client ha inviato una richiesta al server.<\/li>\n<li>Il server ha avviato un'elaborazione lunga.<\/li>\n<li>Il server ha risposto al client con il risultato.<\/li>\n<\/ol>\n<p><\/p>\n<p>Immaginiamo che nel punto 2 ci sia stata un'interruzione della connessione (o la rete si \u00e8 riconnessa, o l'utente ha navigato su un'altra pagina, interrompendo la connessione). In questo caso, sar\u00e0 difficile per il server inviare una risposta all'utente con le informazioni su cosa sia stato elaborato. Applicando CQRS, la sequenza sar\u00e0 leggermente diversa:<\/p>\n<p><\/p>\n<ol>\n<li>Il client si \u00e8 iscritto per ricevere aggiornamenti.<\/li>\n<li>Il client ha inviato una richiesta al server.<\/li>\n<li>Il server ha risposto 'richiesta ricevuta'.<\/li>\n<li>Il server ha risposto con il risultato attraverso il canale del punto '1'.<\/li>\n<\/ol>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/8a00ea93eecc7cc0754122857c5f90d0.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come si pu\u00f2 notare, lo schema \u00e8 leggermente pi\u00f9 complesso. Inoltre, l'approccio intuitivo di richiesta-risposta \u00e8 assente qui. Tuttavia, come si pu\u00f2 notare, l'interruzione della connessione durante l'elaborazione della richiesta non porter\u00e0 a un errore. Inoltre, se l'utente \u00e8 effettivamente connesso al servizio da pi\u00f9 dispositivi (ad esempio, da un telefono cellulare e da un tablet), \u00e8 possibile far s\u00ec che la risposta arrivi a entrambi i dispositivi.<\/p>\n<p><\/p>\n<p>Ci\u00f2 che \u00e8 interessante \u00e8 che il codice per elaborare i messaggi in arrivo diventa simile (non al 100%) sia per gli eventi influenzati dal client stesso, sia per gli altri eventi, compresi quelli di altri clienti.<\/p>\n<p><\/p>\n<p>Tuttavia, nella realt\u00e0 otteniamo vantaggi aggiuntivi poich\u00e9 il flusso unidirezionale pu\u00f2 essere elaborato in modo funzionale (utilizzando RX e analoghi). E questo \u00e8 gi\u00e0 un grande vantaggio, poich\u00e9 in sostanza l'applicazione pu\u00f2 diventare completamente reattiva, e ancor di pi\u00f9 grazie all'approccio funzionale. Per le applicazioni<\/p>\n<p><\/p>\n<p>Se si combina questo approccio con la scalabilit\u00e0 orizzontale, il vantaggio \u00e8 di poter inviare richieste a un server e ricevere risposte da un altro. In questo modo, il client pu\u00f2 scegliere il servizio pi\u00f9 conveniente per lui, mentre il sistema interno sar\u00e0 comunque in grado di gestire correttamente gli eventi.<\/p>\n<p><\/p>\n<h1 id=\"event-sourcing\">Event Sourcing<\/h1>\n<p><\/p>\n<p>Come sapete, una delle caratteristiche principali di un sistema distribuito \u00e8 l'assenza di tempo comune e di una sezione critica comune. Per un processo, \u00e8 possibile effettuare la sincronizzazione (sugli stessi mutex), all'interno della quale si \u00e8 certi che nessun altro stia eseguendo quel codice. Tuttavia, per un sistema distribuito, ci\u00f2 \u00e8 pericoloso, poich\u00e9 richieder\u00e0 delle sovraccarichi e si perder\u00e0 tutto il fascino della scalabilit\u00e0: comunque tutti i componenti dovranno aspettare uno.<\/p>\n<p><\/p>\n<p>Da qui otteniamo un fatto importante: un sistema distribuito veloce non pu\u00f2 essere sincronizzato, poich\u00e9 ci\u00f2 ridurrebbe le prestazioni. D'altra parte, spesso abbiamo bisogno di una certa coerenza dei componenti. E per questo possiamo utilizzare l'approccio con <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">coerenza finale<\/a><\/noindex>, dove \u00e8 garantito che, in assenza di modifiche ai dati, dopo un certo intervallo di tempo dall'ultimo aggiornamento (\u00abin definitiva\u00bb) tutte le richieste restituiranno l'ultimo valore aggiornato.<\/p>\n<p><\/p>\n<p>\u00c8 importante comprendere che per i database classici si applica abbastanza spesso <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Consistency_model#Strict_consistency\">coerenza rigorosa<\/a><\/noindex>, dove ogni nodo possiede la stessa informazione (ci\u00f2 si ottiene spesso quando una transazione \u00e8 considerata stabilita solo dopo la risposta di un secondo server). Qui ci sono alcune concessioni a causa dei livelli di isolamento, tuttavia il concetto generale rimane lo stesso: si pu\u00f2 vivere in un mondo completamente coerente.<\/p>\n<p><\/p>\n<p>Tornando per\u00f2 all'obiettivo iniziale. Se una parte del sistema pu\u00f2 essere costruita con <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">coerenza finale<\/a><\/noindex>, allora si pu\u00f2 costruire il seguente schema.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/33c80be6bc33bd897ca9b9e5525f9ae8.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Caratteristiche importanti di questo approccio:<\/p>\n<p><\/p>\n<ul>\n<li>Ogni richiesta in entrata viene inserita in una coda.<\/li>\n<li>Durante l'elaborazione di una richiesta, il servizio pu\u00f2 anche inserire compiti in altre code.<\/li>\n<li>Ogni evento in arrivo ha un identificatore (necessario per la deduplicazione).<\/li>\n<li>La coda funziona ideologicamente secondo lo schema \"append only\". Non \u00e8 possibile rimuovere o riordinare gli elementi.<\/li>\n<li>La coda opera secondo lo schema FIFO (scusate per la tautologia). Se \u00e8 necessario eseguire in parallelo, andr\u00e0 trasferito in una delle fasi gli oggetti in diverse code.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ricordo che stiamo considerando il caso di un archivio di file online. In questo caso, il sistema apparir\u00e0, all'incirca, in questo modo:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/b95efb29545dcde1db9432d12d00a1b1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>\u00c8 importante notare che i servizi nel diagramma non devono necessariamente significare server separati. Anche un singolo processo pu\u00f2 essere lo stesso. Ci\u00f2 che conta \u00e8 che, ideologicamente, queste cose sono separate in modo tale da poter facilmente applicare la scalabilit\u00e0 orizzontale.<\/p>\n<p><\/p>\n<p>E per due utenti, lo schema apparir\u00e0 cos\u00ec (i servizi destinati a utenti diversi sono contrassegnati con colori diversi):<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/4c4aadc8b9dd505c3ad743e69fee4fae.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>I vantaggi di una tale combinazione:<\/p>\n<p><\/p>\n<ul>\n<li>I servizi di elaborazione delle informazioni sono separati. Anche le code sono separate. Se dovessimo aumentare la capacit\u00e0 del sistema, baster\u00e0 avviare pi\u00f9 servizi su un numero maggiore di server.<\/li>\n<li>Quando riceviamo informazioni dall'utente, non \u00e8 necessario attendere il salvataggio completo dei dati. Al contrario, possiamo semplicemente rispondere \"ok\" e poi iniziare a lavorare gradualmente. Inoltre, la coda smussa i picchi, poich\u00e9 l'aggiunta di un nuovo oggetto avviene rapidamente e l'utente non deve necessariamente attendere il completamento dell'intero ciclo.<\/li>\n<li>Per esempio, ho aggiunto un servizio di deduplicazione che cerca di unire file identici. Se funziona a lungo nel 1% dei casi, il cliente praticamente non se ne accorge (vedi sopra), il che \u00e8 un grande vantaggio, poich\u00e9 non ci \u00e8 pi\u00f9 richiesta una velocit\u00e0 e un'affidabilit\u00e0 al 100%.<\/li>\n<\/ul>\n<p><\/p>\n<p>Tuttavia, si notano subito anche gli svantaggi:<\/p>\n<p><\/p>\n<ul>\n<li>La nostra sistema ha perso la stretta coerenza. Questo significa che, se ad esempio ci si iscrive a diversi servizi, teoricamente si pu\u00f2 ottenere uno stato diverso (poich\u00e9 uno dei servizi potrebbe non riuscire a ricevere la notifica dalla coda interna). Come ulteriore conseguenza, il sistema ora non dispone di un tempo comune. Cio\u00e8, non \u00e8 possibile, ad esempio, ordinare tutti gli eventi semplicemente per orario di arrivo, poich\u00e9 gli orologi tra i server potrebbero non essere sincronizzati (per di pi\u00f9, avere lo stesso orario su due server \u00e8 un'utopia).<\/li>\n<li>Nessun evento pu\u00f2 ora essere semplicemente annullato (come si potrebbe fare con un database). Invece, \u00e8 necessario aggiungere un nuovo evento \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/49451237\/compensating-events-on-cqrs-es-architecture\">compensation event<\/a><\/noindex>, che cambier\u00e0 l'ultimo stato in quello necessario. Come esempio da un'area simile: senza riscrivere la storia (che \u00e8 negativa in alcuni casi), in git non \u00e8 possibile annullare un commit, tuttavia \u00e8 possibile fare un <noindex><a rel=\"nofollow\" href=\"https:\/\/git-scm.com\/docs\/git-revert\">rollback commit<\/a><\/noindex>, che in sostanza ripristiner\u00e0 semplicemente lo stato precedente. Tuttavia, nella cronologia rimarr\u00e0 sia il commit errato sia il rollback.<\/li>\n<li>Lo schema dei dati pu\u00f2 cambiare da una versione all'altra, ma ora non \u00e8 possibile aggiornare i vecchi eventi al nuovo standard (poich\u00e9 in linea di principio non \u00e8 possibile modificare gli eventi).<\/li>\n<\/ul>\n<p><\/p>\n<p>Come si pu\u00f2 vedere, l'Event Sourcing si integra perfettamente con il CQRS. Inoltre, implementare un sistema con code efficienti e convenienti, ma senza separazione dei flussi di dati, \u00e8 gi\u00e0 di per s\u00e9 difficile, poich\u00e9 sar\u00e0 necessario aggiungere punti di sincronizzazione che annulleranno l'intero effetto positivo delle code. Applicando entrambi gli approcci contemporaneamente, \u00e8 necessario apportare lievi modifiche al codice del programma. Nel nostro caso, quando si invia un file al server, nella risposta si riceve solo 'ok', il che significa che 'l'operazione di aggiunta del file \u00e8 stata salvata'. Formalmente, ci\u00f2 non implica che i dati siano gi\u00e0 disponibili su altri dispositivi (ad esempio, il servizio di deduplicazione potrebbe ricostruire l'indice). Tuttavia, dopo un po', il cliente ricever\u00e0 una notifica del tipo 'il file X \u00e8 stato salvato'.<\/p>\n<p><\/p>\n<p>Come risultato:<\/p>\n<p><\/p>\n<ul>\n<li>Il numero di stati di invio dei file aumenta: invece del classico 'file inviato' otteniamo due stati: 'file aggiunto in coda sul server' e 'file salvato nello storage'. L'ultimo significa che altri dispositivi possono gi\u00e0 iniziare a ricevere il file (con la precisazione che le code funzionano a velocit\u00e0 diverse).<\/li>\n<li>Poich\u00e9 le informazioni sull'invio ora arrivano attraverso canali diversi, dobbiamo pensare a soluzioni per ricevere lo stato di elaborazione del file. Di conseguenza, a differenza della classica richiesta-risposta, il client pu\u00f2 essere riavviato durante il processo di elaborazione del file, ma lo stato di questa stessa elaborazione sar\u00e0 corretto. Inoltre, questo punto funziona sostanzialmente 'out of the box'. Come conseguenza: siamo ora pi\u00f9 tolleranti ai guasti.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"sharding\">Sharding<\/h1>\n<p><\/p>\n<p>Come gi\u00e0 descritto sopra, nei sistemi con event sourcing non c'\u00e8 una stretta coerenza. Ci\u00f2 significa che possiamo utilizzare pi\u00f9 repository senza alcuna sincronizzazione tra di essi. Avvicinandoci al nostro compito, possiamo:<\/p>\n<p><\/p>\n<ul>\n<li>Dividere i file in base ai tipi. Ad esempio, le immagini\/video possono essere decodificati e scegliere un formato pi\u00f9 efficiente.<\/li>\n<li>Separare gli account per paese. A causa di molte leggi, questo pu\u00f2 essere necessario, tuttavia, questo schema architetturale offre tale possibilit\u00e0 automaticamente.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/dd310df700bc39c014d0105a3425fece.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Se desideri trasferire dati da uno storage a un altro, qui le soluzioni standard non basteranno. Sfortunatamente, in questo caso \u00e8 necessario fermare la coda, eseguire la migrazione e poi riavviarla. In generale, non \u00e8 possibile trasferire i dati \"al volo\", tuttavia, se la coda degli eventi \u00e8 completamente mantenuta e hai delle istantanee dei precedenti stati dello storage, possiamo riprodurre gli eventi nel modo seguente:<\/p>\n<p><\/p>\n<ul>\n<li>In Event Source, ogni evento ha un proprio identificatore (ideale sarebbe che non diminuisca). Dunque, nello storage possiamo aggiungere un campo: id dell'ultimo elemento elaborato.<\/li>\n<li>Dupliciamo la coda affinch\u00e9 tutti gli eventi possano essere elaborati per pi\u00f9 storage indipendenti (il primo \u00e8 quello in cui i dati sono gi\u00e0 memorizzati, mentre il secondo \u00e8 nuovo, ma attualmente vuoto). La seconda coda, naturalmente, non \u00e8 ancora elaborata.<\/li>\n<li>Avviamo la seconda coda (cio\u00e8 iniziamo a riprodurre gli eventi).<\/li>\n<li>Quando la nuova coda sar\u00e0 relativamente vuota (cio\u00e8 la differenza media di tempo tra l'aggiunta di un elemento e il suo prelievo sar\u00e0 accettabile), si pu\u00f2 iniziare a passare i lettori allo nuovo storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>Come si pu\u00f2 vedere, nella nostra sistema non ci sono mai state n\u00e9 ci sono attualmente rigidit\u00e0 nella coerenza. Esiste solo una eventual constistency, cio\u00e8 la garanzia che gli eventi vengano elaborati nello stesso ordine (tuttavia, potrebbe esserci un ritardo diverso). E, approfittando di questo, possiamo trasferire relativamente facilmente i dati senza fermare il sistema fino all'altro lato del mondo.<\/p>\n<p><\/p>\n<p>Dunque, continuando il nostro esempio di storage online per files, tale architettura ci offre gi\u00e0 una serie di vantaggi:<\/p>\n<p><\/p>\n<ul>\n<li>Possiamo spostare oggetti pi\u00f9 vicino agli utenti, in modo dinamico. In questo modo possiamo migliorare la qualit\u00e0 del servizio. <\/li>\n<li>Possiamo conservare parte dei dati all'interno delle aziende. Ad esempio, gli utenti Enterprise di solito richiedono di salvare i loro dati in data center sotto controllo (per evitare perdite di dati). Grazie al sharding possiamo supportare facilmente questo. E il compito diventa ancora pi\u00f9 semplice se il cliente ha un cloud compatibile (ad esempio, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-gb\/azure-stack\/asdk\/asdk-what-is?view=azs-1910\">Azure self hosted<\/a><\/noindex>).<\/li>\n<li>E la cosa pi\u00f9 importante \u00e8 che possiamo non farlo. Infatti, per iniziare, ci basterebbe uno spazio di archiviazione per tutti gli account (per cominciare a lavorare pi\u00f9 rapidamente). E la caratteristica principale di questo sistema \u00e8 che, sebbene sia espandibile, all'inizio \u00e8 abbastanza semplice. Non bisogna subito scrivere codice che gestisca un milione di code indipendenti, ecc. Se necessario, questo pu\u00f2 essere fatto in futuro.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"static-content-hosting\">Hosting di Contenuti Statici<\/h1>\n<p><\/p>\n<p>Questo punto pu\u00f2 sembrare abbastanza evidente, tuttavia \u00e8 comunque necessario per un'applicazione standard e carica. La sua essenza \u00e8 semplice: tutto il contenuto statico viene distribuito non dallo stesso server dove si trova l'applicazione, ma da server speciali, dedicati proprio a questo scopo. Di conseguenza, queste operazioni vengono eseguite pi\u00f9 rapidamente (un ipotetico nginx restituisce i file in modo pi\u00f9 veloce e meno costoso rispetto a un server Java). Inoltre, l'architettura CDN (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Content_delivery_network\">Content Delivery Network<\/a><\/noindex>) consente di posizionare i nostri file pi\u00f9 vicini agli utenti finali, migliorando cos\u00ec l'esperienza di utilizzo del servizio.<\/p>\n<p><\/p>\n<p>L'esempio pi\u00f9 semplice e standard di contenuto statico \u00e8 un insieme di script e immagini per un sito web. Con essi \u00e8 tutto chiaro: sono noti in anticipo, poi l'archivio viene caricato sui server CDN, da dove viene distribuito agli utenti finali.<\/p>\n<p><\/p>\n<p>Tuttavia, in realt\u00e0 \u00e8 possibile applicare un approccio simile a un'architettura lambda per il contenuto statico. Torniamo al nostro compito (un archivio di file online), in cui dobbiamo distribuire file agli utenti. La soluzione pi\u00f9 semplice \u00e8 realizzare un servizio che, per ogni richiesta dell'utente, esegue tutte le verifiche necessarie (autenticazione, ecc.), e poi scarica il file direttamente dal nostro archivio. Il principale svantaggio di tale approccio \u00e8 che il contenuto statico (e un file con una determinata revisione \u00e8, in effetti, contenuto statico) viene distribuito dallo stesso server che contiene la logica di business. In alternativa, possiamo mettere in atto il seguente schema:<\/p>\n<p><\/p>\n<ul>\n<li>Il server fornisce un URL per il download. Pu\u00f2 essere del tipo file_id + key, dove key \u00e8 una mini-firma digitale che conferisce il diritto di accesso alla risorsa per le prossime 24 ore.<\/li>\n<li>La distribuzione del file \u00e8 gestita da un semplice nginx con le seguenti opzioni:\n<ul>\n<li>Caching dei contenuti. Poich\u00e9 questo servizio potrebbe trovarsi su un server separato, abbiamo lasciato uno spazio per il futuro con la possibilit\u00e0 di memorizzare tutti gli ultimi file scaricati su disco.<\/li>\n<li>Controllo della chiave al momento della creazione della connessione<\/li>\n<\/ul>\n<\/li>\n<li>Opzionale: elaborazione dei contenuti in streaming. Ad esempio, se stiamo comprimendo tutti i file nel servizio, \u00e8 possibile eseguire la decompressione direttamente in questo modulo. Di conseguenza: le operazioni di I\/O vengono eseguite dove \u00e8 pi\u00f9 appropriato. L'archiviatore in Java pu\u00f2 all'occorrenza occupare molta memoria inutile, ma riscrivere il servizio con logica di business in linguaggi come Rust o C++ potrebbe risultare altrettanto inefficace. Nel nostro caso, vengono utilizzati processi diversi (o persino servizi), quindi possiamo separare in modo abbastanza efficace la logica di business dalle operazioni di I\/O.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/4dead07d5824938e09b986192ccc8f5e.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Uno schema simile non assomiglia molto alla distribuzione di contenuti statici (poich\u00e9 non stiamo esportando l'intero pacchetto di statico in un altro luogo), tuttavia in realt\u00e0 questo approccio si occupa proprio della distribuzione di dati immutabili. Inoltre, questo schema pu\u00f2 essere generalizzato anche ad altri casi in cui il contenuto non \u00e8 statico, ma pu\u00f2 essere rappresentato come un insieme di blocchi immutabili e non eliminabili (anche se possono essere aggiunti).<\/p>\n<p><\/p>\n<p>Un altro esempio (per chiarire): se hai lavorato con Jenkins\/TeamCity, sai che entrambe le soluzioni sono scritte in Java. Entrambe rappresentano un processo Java che si occupa sia dell'orchestrazione delle build che della gestione dei contenuti. In particolare, entrambe hanno compiti del tipo \"trasferire un file\/cartella dal server\". Ad esempio: rilascio di artefatti, trasferimento del codice sorgente (quando l'agente non scarica il codice direttamente dal repository, ma \u00e8 il server a farlo), accesso ai log. Tutti questi compiti differiscono per il carico di I\/O. Quindi, il server responsabile della complessa logica di business deve anche essere in grado di gestire in modo efficiente grandi flussi di dati. E la cosa pi\u00f9 interessante \u00e8 che un'operazione del genere pu\u00f2 essere delegata allo stesso nginx seguendo esattamente lo stesso schema (a meno che non si debba aggiungere una chiave di dati alla richiesta).<\/p>\n<p><\/p>\n<p>Tuttavia, se torniamo al nostro sistema, risulta uno schema simile:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Modelli architetturali pratici\" src=\"\/wp-content\/uploads\/2020\/05\/97cfbf5daaa6678a9eecc3169692f0fc.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Come si pu\u00f2 vedere, il sistema \u00e8 radicalmente complicato. Ora non si tratta solo di un mini-processo che memorizza i file localmente. Adesso \u00e8 necessaria una gestione pi\u00f9 complessa, il controllo delle versioni API, ecc. Pertanto, dopo aver disegnato tutti i diagrammi, \u00e8 meglio valutare attentamente se la scalabilit\u00e0 giustifichi tali costi. Tuttavia, se desiderate avere la possibilit\u00e0 di espandere il sistema (incluso il lavoro con un numero maggiore di utenti), dovrete adottare tali soluzioni. D'altra parte, come risultato, il sistema \u00e8 architettonicamente pronto per aumentare il carico (praticamente ogni componente pu\u00f2 essere clonato per la scalabilit\u00e0 orizzontale). Il sistema pu\u00f2 essere aggiornato senza fermarsi (alcune operazioni rallenteranno semplicemente leggermente).<\/p>\n<p><\/p>\n<p>Come ho gi\u00e0 detto all'inizio, ora diversi servizi online stanno subendo un carico maggiore. E alcuni di essi hanno semplicemente smesso di funzionare correttamente. In sostanza, i sistemi hanno fallito proprio nel momento in cui il business avrebbe dovuto generare profitti. In altre parole, invece di una consegna posticipata, invece di proporre ai clienti \"pianificate la fornitura per i prossimi mesi\", il sistema ha semplicemente detto \"andate dai concorrenti\". Questa \u00e8, di fatto, la conseguenza di una bassa produttivit\u00e0: le perdite si verificheranno proprio quando il profitto sarebbe stato massimo.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Conclusione<\/h1>\n<p><\/p>\n<p>Tutti questi approcci erano gi\u00e0 noti in passato. Lo stesso VK utilizza da tempo l'idea di Static Content Hosting per la distribuzione delle immagini. Un sacco di giochi online adottano lo schema di Sharding per suddividere i giocatori per regione o per separare le aree di gioco (se il mondo \u00e8 unico). L'approccio Event Sourcing \u00e8 utilizzato attivamente nell'email. La maggior parte delle applicazioni per trader, dove i dati arrivano continuamente, sono in realt\u00e0 costruite sull'approccio CQRS, per poter filtrare i dati ricevuti. Inoltre, la scalabilit\u00e0 orizzontale \u00e8 gi\u00e0 stata applicata in molti servizi da tempo.<\/p>\n<p><\/p>\n<p>Tuttavia, ci\u00f2 che \u00e8 pi\u00f9 importante \u00e8 che tutti questi pattern sono diventati molto facili da applicare nelle moderne applicazioni (se sono appropriati, ovviamente). Le nuvole offrono sharding e scalabilit\u00e0 orizzontale subito, il che \u00e8 molto pi\u00f9 semplice rispetto a dover ordinare diversi server dedicati in diversi data center da soli. CQRS \u00e8 diventato molto pi\u00f9 facile grazie allo sviluppo di librerie come RX. Dieci anni fa, pochi siti web sarebbero stati in grado di supportare una cosa del genere. Anche l'event sourcing \u00e8 incredibilmente facile da configurare grazie a contenitori gi\u00e0 pronti come Apache Kafka. Dieci anni fa sarebbe stata un'innovazione, ora \u00e8 normalit\u00e0. Lo stesso vale per l'hosting di contenuti statici: grazie a tecnologie pi\u00f9 comode (anche per il fatto che c'\u00e8 documentazione dettagliata e una grande base di risposte), questo approccio \u00e8 diventato ancora pi\u00f9 semplice.<\/p>\n<p><\/p>\n<p>In sintesi, l'implementazione di diversi piuttosto complessi pattern architetturali \u00e8 diventata ora molto pi\u00f9 semplice, e quindi vale la pena esaminarli in anticipo. Se in un'applicazione di dieci anni fa si era rinunciato a una delle soluzioni sopra per via dell'alto costo di implementazione e gestione, adesso, in una nuova applicazione, o dopo un refactoring, \u00e8 possibile creare un servizio che architettonicamente sar\u00e0 sia scalabile (dal punto di vista delle prestazioni), sia pronto a nuove richieste da parte dei clienti (ad esempio, per la localizzazione dei dati personali).<\/p>\n<p><\/p>\n<p>E la cosa pi\u00f9 importante: per favore non utilizzate questi approcci se avete un'applicazione semplice. S\u00ec, sono belli e interessanti, tuttavia per un sito con un picco di visitatori di 100 persone, di solito si pu\u00f2 benissimo fare a meno di un classico monolite (almeno esternamente, internamente si pu\u00f2 suddividere tutto in moduli, ecc.).<\/p>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/dbtc\/blog\/499758\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0434\u043d\u0430 \u0438\u0437 \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u0441\u0435\u0442\u0435\u0439 \u0432 \u0412\u0435\u043b\u0438\u043a\u043e\u0431\u0440\u0438\u0442\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0430 \u0441\u0430\u0439\u0442 \u0441 \u043e\u043d\u043b\u0430\u0439\u043d-\u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u043b\u043e \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0435\u0439. \u0418 \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0431\u043e\u043b\u0435\u0435 \u043c\u043e\u0449\u043d\u043e\u0435 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435, \u043e\u0434\u043d\u0430\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0434\u043e (\u0438\u043b\u0438 \u043e\u043d\u0438 \u0443\u0439\u0434\u0443\u0442 \u043a \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u0430\u043c). \u0412 \u044d\u0442\u043e\u0439 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":80036,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-80035","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0423\u0434\u043e\u0431\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-05-02T11:42:54+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-02T11:42:54+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Pattern architetturali comodi | ProHoster","description":"Ciao, Habr! Alla luce degli attuali eventi a causa del coronavirus, diversi servizi online stanno ricevendo un carico maggiore.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0423\u0434\u043e\u0431\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b | ProHoster","og:description":"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-05-02T11:42:54+00:00","article:modified_time":"2020-05-02T11:42:54+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"80035","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:26:40","updated":"2022-09-28 01:38:29","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/80035","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=80035"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/80035\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media\/80036"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=80035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=80035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=80035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}