Alcune aziende, incluso il nostro cliente, sviluppano il prodotto attraverso una rete di partner. Ad esempio, i grandi negozi online sono integrati con i servizi di consegna: ordini il prodotto e ricevi presto il numero di tracciamento del pacco. Un altro esempio è l'acquisto di un'assicurazione o di un biglietto per l'aeroexpress insieme al biglietto aereo.
Per questo si utilizza un'API che deve essere fornita ai partner tramite un API Gateway. Questo è l'obiettivo che abbiamo raggiunto. In questo articolo parleremo nei dettagli.
Abbiamo come base: un ecosistema e un portale API con un'interfaccia in cui gli utenti sono registrati, ricevono informazioni, ecc. Dobbiamo creare un API Gateway comodo e affidabile. Durante il processo abbiamo dovuto garantire
- registrazione,
- controllo della connessione all'API,
- monitoraggio dell'utilizzo del sistema finale da parte degli utenti,
- considerazione degli indicatori aziendali.

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 di origine,
- proxy della richiesta,
- post-elaborazione della risposta.
Esistono due tipi di gestione delle API:
1. Standard, che funziona nel seguente modo. Prima della connessione, l'utente testa le possibilità, poi paga e integra nel proprio sito. È principalmente utilizzato da piccole e medie imprese.
2. Grande API Management B2B, quando un'azienda prima prende una decisione commerciale sulla connessione, diventa partner dell'azienda con un obbligo contrattuale, dopodiché si connette all'API. Solo dopo aver sistemato tutte le formalità, l'azienda ottiene accesso di prova, esegue il testing e va in produzione. Ma questo non è possibile senza una decisione di gestione sulla connessione.

La nostra soluzione
In questa sezione parleremo della creazione di un API Gateway.
Gli utenti finali del gateway API che stiamo creando sono i partner del nostro cliente. Per ciascuno di loro abbiamo già i contratti necessari. Dobbiamo solo espandere la funzionalità, registrando l'accesso fornito al gateway. Pertanto, è necessario un processo controllato per la connessione e la gestione.
Certamente, si potrebbe optare per qualche soluzione già pronta per affrontare la questione della gestione delle API e della creazione di un API Gateway in particolare. Ad esempio, una di queste potrebbe essere. Non ci ha soddisfatto, perché nel nostro caso avevamo già un portale API e un'enorme ecosistema costruita attorno ad esso. Tutti gli utenti erano già registrati e sapevano già dove e come ottenere le informazioni di cui avevano bisogno. Nel portale API esistevano già le interfacce necessarie, ci serviva solo un API Gateway. Infatti, è a questo progetto che ci siamo dedicati.
Ciò che chiamiamo API Gateway è una sorta di proxy. Qui avevamo di nuovo una scelta: potevamo scrivere il nostro proxy o scegliere qualcosa di già pronto. In questo caso, abbiamo optato per la seconda opzione e scelto la combinazione nginx+Lua. Perché? Avevamo bisogno di software affidabile e collaudato, in grado di supportare la scalabilità. Non volevamo, dopo l'implementazione, verificare sia la correttezza della logica aziendale che il funzionamento del proxy.
Ogni server web ha una catena di elaborazione delle richieste. Nel caso di nginx, essa appare come segue:

(schema da )
Il nostro obiettivo era inserirci in questa catena nel momento in cui potevamo modificare la richiesta originale.
Vogliamo creare un proxy trasparente in modo che la richiesta mantenga la sua funzionalità originale. Ci limitiamo a controllare l'accesso all'API finale, aiutando la richiesta a raggiungerla. Nel caso in cui la richiesta fosse errata, l'errore dovrebbe essere mostrato dall'API finale, non da noi. L'unico motivo per cui possiamo rifiutare una richiesta è la mancanza di accesso da parte del cliente.
Esiste già per nginx con . Lua è un linguaggio di scripting, molto leggero e facile da imparare. Così, abbiamo implementato la logica necessaria utilizzando Lua.
La configurazione di nginx (analogamente a un'applicazione route), dove viene eseguito tutto il lavoro, è abbastanza chiara. Qui è interessante 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;
}
Analizziamo cosa succede in questa configurazione:
more_clear_input_headers — pulisce il valore degli header specificati dopo la direttiva.
lua_need_request_body — determina se il corpo della richiesta originale deve essere letto prima di eseguire le direttive rewrite/access/access_by_lua o meno. Per impostazione predefinita, nginx non legge il corpo della richiesta del client e, se è necessario accedervi, questa direttiva deve essere impostata su on.
rewrite_by_lua_file — percorso dello script che descrive la logica per la modifica della richiesta
access_by_lua_file — percorso dello script che descrive la logica che verifica l'accesso alle risorse.
proxy_pass — url a cui verrà inoltrata la richiesta.
body_filter_by_lua_file — percorso dello script che descrive la logica per filtrare la richiesta prima di restituirla al cliente.
E, infine, post_action — direttiva ufficialmente non documentata, che consente di eseguire altre azioni dopo che la risposta è stata consegnata al cliente.
Di seguito spiegheremo in ordine come abbiamo affrontato le nostre sfide.
Autenticazione/autenticazione e modifica della richiesta
Autenticazione
L'autenticazione e l'autenticazione sono state implementate tramite accessi basati su certificato. Esiste un certificato root. A ogni nuovo cliente del committente viene generato un certificato personale, che consente l'accesso all'API. Questo certificato viene configurato nella sezione server delle impostazioni di 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
Potrebbe sorgere una domanda legittima: cosa fare con un cliente certificato se all'improvviso decidiamo di disconnetterlo dal sistema? Non possiamo certamente riemissionare i certificati per tutti gli altri clienti.
Così siamo arrivati gradualmente al compito successivo: la modifica della richiesta originale. La richiesta originale del cliente, in realtà, non è valida per il sistema finale. Uno degli obiettivi è completare la richiesta con le parti mancanti per renderla valida. La chiave è che i dati mancanti sono diversi per ogni cliente. Sappiamo che il cliente arriva a noi con un certificato, da cui possiamo ottenere l'impronta e recuperare i dati necessari dal database.
Se in qualche momento sarà necessario disconnettere un cliente dal nostro servizio, i suoi dati verranno rimossi dal database e non potrà più fare nulla.
Gestione dei dati del cliente
Avevamo bisogno di garantire un'alta disponibilità della soluzione, soprattutto riguardo a come otteniamo i dati del cliente. La complessità sta nel fatto che la fonte primaria di questi dati è un servizio esterno, che non garantisce un funzionamento continuo e una velocità adeguata.
Perciò, abbiamo dovuto garantire un'alta 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 seguito la strategia più semplice per la consegna dei dati nella cache:

Il lavoro con il sistema finale avviene nell'ambito di sessioni e c'è un limite al numero massimo. Se il cliente non chiude la sessione, dovremo farlo noi.
I dati sulle sessioni aperte vengono ricevuti dal sistema finale e inizialmente elaborati dal lato Lua. Abbiamo deciso di utilizzare Hazelcast per conservare questi dati tramite un job scritto in .NET. Successivamente, verifichiamo periodicamente la validità delle sessioni aperte e chiudiamo quelle scadute.
Accesso a Hazelcast sia da Lua che da .NET
Non ci sono client Lua per lavorare con Hazelcast, ma Hazelcast ha un'API REST che abbiamo deciso di utilizzare. Per .NET, invece, esiste , attraverso il quale avevamo pianificato di accedere ai dati di Hazelcast dal lato .NET. Ma non è così semplice.

Durante il salvataggio dei dati tramite REST e l'estrazione 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, ne parleremo più dettagliatamente in un articolo a parte. Spoiler — nella tabella.

Logging e monitoraggio
Il nostro standard aziendale per il logging tramite .NET è Serilog, tutti i log vanno a finire in Elasticsearch e li analizziamo tramite Kibana. Qualcosa di simile volevamo realizzare anche in questo caso. L'unico per lavorare con Elastic su Lua, che è stato trovato, si è bloccato al primo require. E abbiamo usato Fluentd.
è una soluzione open source per garantire uno strato unico di registrazione dell'applicazione. Consente di raccogliere log da diversi livelli dell'applicazione e poi trasmetterli a un'unica fonte.
API Gateway funziona in K8S, quindi abbiamo deciso di aggiungere un container con fluentd nello stesso pod, per registrare i log nella porta tcp aperta di fluentd.
Abbiamo anche esplorato come si sarebbe comportato fluentd se non avesse avuto connessione con Elasticsearch. Per due giorni, il gateway ha ricevuto richieste continuamente, i log sono stati inviati a fluentd, ma l'IP di Elastic era bloccato. Dopo il ripristino della connessione, fluentd ha trasferito perfettamente tutti i log in Elastic.
Conclusione
L'approccio selezionato per l'implementazione ci ha permesso di portare un prodotto realmente funzionante in produzione in appena 2,5 mesi.
Se mai vi troverete a gestire cose simili, vi consigliamo di capire chiaramente quale problema state risolvendo e quale risorse avete già a disposizione. Fate attenzione alle complessità dell'integrazione con i sistemi di gestione API esistenti.
Comprendi per te stesso cosa hai intenzione di sviluppare: solo la logica di business per la gestione delle richieste o, come nel nostro caso, l'intero proxy. Non dimenticare che tutto ciò che realizzi da solo deve essere accuratamente testato in seguito.
Fonte: habr.com
