Alcune aziende, incluso il nostro cliente, sviluppano il prodotto attraverso una rete di partner. Ad esempio, grandi negozi online sono integrati con il servizio di consegna: ordini il prodotto e presto ricevi un numero di tracciamento del pacco. Un altro esempio è che insieme al biglietto aereo acquisti un'assicurazione o un biglietto per l'aeroexpress.
Per questo viene utilizzata un'unica API, che deve essere fornita ai partner tramite l'API Gateway. Questo compito lo abbiamo risolto. In questo articolo condivideremo i dettagli.
Dati: ecosistema e portale API con un'interfaccia, dove gli utenti sono registrati, ricevono informazioni e così via. Dobbiamo creare un API Gateway comodo e affidabile. Durante il processo dovevamo garantire
- la registrazione,
- il controllo della connessione all'API,
- il monitoraggio di come gli utenti utilizzano il sistema finale,
- il monitoraggio delle metriche di business.

In questo articolo parleremo della nostra esperienza nella creazione di un API Gateway, durante la quale abbiamo affrontato i seguenti compiti:
- autenticazione dell'utente,
- autorizzazione dell'utente,
- modifica della richiesta originale,
- proxy della richiesta,
- post-elaborazione della risposta.
Ci sono due tipi di gestione delle API:
1. Standard, che funziona nel seguente modo. Prima della connessione, l'utente testa le funzionalità, poi paga e integra sul proprio sito. È più usato nelle piccole e medie imprese.
2. Grande gestione delle API B2B, quando un'azienda prima prende una decisione commerciale sulla connessione, diventa partner dell'azienda con un obbligo contrattuale, dopodiché si connette all'API. E solo dopo aver sistemato tutte le formalità, l'azienda ottiene l'accesso di prova, supera i test e passa alla produzione. Ma ciò non è possibile senza una decisione di gestione sulla connessione.

La nostra soluzione
In questa parte parleremo della creazione di un API Gateway.
Gli utenti finali del gateway API creato sono i partner del nostro cliente. Per ognuno di loro abbiamo già i contratti necessari. Dobbiamo solo ampliare la funzionalità, segnando l'accesso fornito al gateway. Di conseguenza, è necessario un processo controllato di connessione e gestione.
Certamente, si sarebbe potuto adottare una qualche soluzione già pronta per affrontare il problema della gestione delle API e della creazione di un API Gateway in particolare. Ad esempio, una soluzione potrebbe essere. Non ci è andato bene, perché nel nostro caso avevamo già un portale API e un'enorme ecosistema costruita attorno ad esso. Tutti gli utenti erano già registrati, capivano già dove e come potevano ottenere le informazioni necessarie. Nel portale API esistevano già le interfacce necessarie, ci serviva solo un API Gateway. In realtà, è a questo sviluppo che ci siamo dedicati.
Quello che chiamiamo API Gateway è una sorta di proxy. Qui avevamo di nuovo una scelta: possiamo scrivere il nostro proxy, oppure scegliere qualcosa di già pronto. In questo caso, abbiamo optato per la seconda strada e scelto la combinazione nginx+Lua. Perché? Avevamo bisogno di un software affidabile e collaudato, che supportasse la scalabilità. Non volevamo controllare, dopo l'implementazione, né la correttezza della logica aziendale né la correttezza del funzionamento del proxy.
Qualsiasi server web ha una pipeline di elaborazione delle richieste. Nel caso di nginx, si presenta come segue:

(schema da )
Il nostro obiettivo era di integrarci in questa pipeline nel momento in cui possiamo modificare la richiesta originale.
Vogliamo creare un proxy trasparente, affinché la richiesta rimanga funzionalmente tale quale è arrivata. Controlliamo solo l'accesso all'API finale, aiutiamo la richiesta a raggiungerla. Nel caso in cui la richiesta fosse errata, l'errore deve essere segnalato dall'API finale, non da noi. L'unico motivo per cui possiamo rifiutare una richiesta è la mancanza di accesso da parte del cliente.
Per nginx esiste già in . Lua è un linguaggio di scripting, molto leggero e facile da imparare. Così, abbiamo implementato la logica necessaria utilizzando Lua.
La configurazione di nginx (analogia route dell'applicazione), dove viene eseguito tutto il lavoro, è abbastanza comprensibile. Da notare l'ultima direttiva: post_action.
location /middleware {
more_clear_input_headers Accept-Encoding;
lua_need_request_body on;
rewrite_by_lua_file 'middleware/rewrite.lua';
access_by_lua_file 'middleware/access.lua';
proxy_pass https://someurl.com;
body_filter_by_lua_file 'middleware/body_filter.lua';
post_action /process_session;
}
Consideriamo cosa succede in questa configurazione:
more_clear_input_headers — cancella il valore degli header indicati dopo la direttiva.
lua_need_request_body — controlla se è necessario leggere il corpo della richiesta originale prima di eseguire le direttive rewrite/access/access_by_lua o meno. Per impostazione predefinita, nginx non legge il corpo della richiesta del cliente e, se è necessario accedervi, questa direttiva deve avere valore on.
rewrite_by_lua_file — percorso verso gli script, in cui è descritta la logica per modificare la richiesta
access_by_lua_file — percorso verso lo script, in cui è descritta la logica che verifica la presenza di accesso alle risorse.
proxy_pass — url a cui verrà inoltrata la richiesta.
body_filter_by_lua_file — percorso verso lo script, in cui è descritta la logica per filtrare la richiesta prima di restituirla al cliente.
E, infine, post_action — direttiva ufficialmente non documentata, tramite la quale è possibile eseguire ulteriori azioni dopo che la risposta è stata data al cliente.
Dopo parleremo in ordine di come abbiamo risolto le nostre problematiche.
Autenticazione e modifica della richiesta
Autenticazione
Abbiamo costruito l'autenticazione e l'autenticazione utilizzando accessi basati su certificato. C'è un certificato radice. A ogni nuovo cliente del committente viene generato un certificato personale, con cui può accedere all'API. Questo certificato è configurato nella sezione server delle impostazioni nginx.
ssl on;
ssl_certificate /usr/local/openresty/nginx/ssl/cert.pem;
ssl_certificate_key /usr/local/openresty/nginx/ssl/cert.pem;
ssl_client_certificate /usr/local/openresty/nginx/ssl/ca.crt;
ssl_verify_client on;Modifica
Può sorgere una domanda legittima: cosa fare con un cliente certificato, se all'improvviso volessimo disconnetterlo dal sistema? Non si possono riemissionare i certificati per tutti gli altri clienti.
Così siamo passati naturalmente al compito successivo: la modifica della richiesta originale. La richiesta originale del cliente, a dire il vero, non è valida per il sistema finale. Uno degli obiettivi è completare la richiesta con le parti mancanti per renderla valida. Il punto è che i dati mancanti sono diversi per ogni cliente. Sappiamo che il cliente si presenta con un certificato, da cui possiamo prendere l'impronta e estrarre i dati necessari dal database.
Se a un certo punto sarà necessario disconnettere un cliente dal nostro servizio, i suoi dati scompariranno dal database e lui non potrà più fare nulla.
Gestione dei dati del cliente
Dovevamo garantire un'elevata disponibilità della soluzione, soprattutto per come otteniamo i dati del cliente. La difficoltà sta nel fatto che la fonte originale di questi dati è un servizio di terze parti, che non garantisce un funzionamento senza interruzioni e una velocità di lavoro sufficientemente alta.
Perciò dovevamo garantire un'elevata disponibilità dei dati dei clienti. Come strumento abbiamo scelto , che ci fornisce:
- accesso rapido ai dati,
- possibilità di organizzare un cluster di più nodi con dati replicati su nodi diversi.
Abbiamo adottato la strategia più semplice per la consegna dei dati nella cache:

Il lavoro con il sistema finale avviene nell'ambito delle sessioni e c'è un limite sul numero massimo di esse. Se il cliente non chiude la sessione, dovremo farlo noi.
I dati sulla sessione aperta provengono dal sistema finale e vengono inizialmente elaborati sul lato Lua. Abbiamo deciso di utilizzare Hazelcast per memorizzare questi dati tramite un job scritto in .NET. Poi, con una certa periodicità, controlliamo il diritto alla vita delle sessioni aperte e chiudiamo quelle scadute.
Accesso a Hazelcast sia da Lua che da .NET
Non ci sono clienti su Lua per lavorare con Hazelcast, ma Hazelcast ha un'API REST che abbiamo deciso di utilizzare. Per .NET invece c'è , attraverso il quale avevamo pianificato di accedere ai dati di Hazelcast sul lato .NET. Ma non è andata così.

Quando si salvano i dati tramite REST e si estraggono tramite il client .NET, vengono utilizzati diversi serializzatori e deserializzatori. Pertanto non è possibile inserire dati tramite REST e recuperarli tramite il client .NET, e viceversa.
Se ci saranno interessati, racconteremo più in dettaglio di questo problema in un articolo separato. Spoiler: nel diagramma.

Log e monitoraggio
Il nostro standard aziendale per la registrazione tramite .NET è Serilog, e tutti i log alla fine vengono inviate a Elasticsearch, la cui analisi viene effettuata tramite Kibana. Volevamo fare qualcosa di simile anche in questo caso. L'unico client trovato per lavorare con Elastic su Lua ha smesso di funzionare al primo require. E abbiamo usato Fluentd.
è una soluzione open source per fornire uno strato di registrazione centralizzato dell'applicazione. Permette di raccogliere log da diversi livelli dell'applicazione e poi di trasmetterli a una fonte unica.
Il gateway API funziona in K8S, quindi abbiamo deciso di aggiungere un contenitore con fluentd nella stessa pod, per scrivere i log nella porta tcp aperta di fluentd già esistente.
Abbiamo anche indagato su come si comporterà fluentd se non avrà connessione con Elasticsearch. Per due giorni, continue richieste sono arrivate al gateway, i log venivano inviati a fluentd, ma l'IP di Elastic era stato bloccato per fluentd. Dopo il ripristino della connessione, fluentd ha esaminato tutti i log in Elastic.
Conclusione
L'approccio scelto per l'implementazione ci ha permesso di consegnare un prodotto realmente funzionante in ambiente di produzione in soli 2,5 mesi.
Se un giorno vi capita di occuparvi di cose simili, vi consigliamo di capire chiaramente qual è il problema che state risolvendo e quali risorse avete già a disposizione. Fate attenzione alle complessità dell'integrazione con i sistemi di gestione API esistenti.
Capite cosa intendete sviluppare: solo la logica aziendale per la gestione delle richieste o, come nel nostro caso, un proxy completo. Non dimenticate che tutto ciò che farete da soli dovrà essere successivamente testato con attenzione.
Fonte: habr.com
