"Camminare con le mie scarpe" - aspetta, sono segnate?

Dal 2019, la Russia ha una legge sull’etichettatura obbligatoria. La legge non si applica a tutti i gruppi di merci e le date di entrata in vigore dell'etichettatura obbligatoria per i gruppi di prodotti sono diverse. Tabacco, scarpe e medicinali saranno i primi ad essere soggetti ad etichettatura obbligatoria; successivamente si aggiungeranno altri prodotti, ad esempio profumi, tessili e latte. Questa innovazione legislativa ha spinto allo sviluppo di nuove soluzioni informatiche che consentiranno di tracciare l'intera catena di vita di un prodotto dalla produzione all'acquisto da parte del consumatore finale, a tutti i partecipanti al processo: sia lo Stato stesso che tutte le organizzazioni che vendono beni con etichettatura obbligatoria.

In X5, il sistema che traccerà le merci etichettate e scambierà dati con Stato e fornitori si chiama “Marcus”. Vi raccontiamo in ordine come e chi lo ha sviluppato, qual è il suo stack tecnologico e perché abbiamo qualcosa di cui essere orgogliosi.

"Camminare con le mie scarpe" - aspetta, sono segnate?

Vero carico elevato

“Marcus” risolve molti problemi, il principale è l'integrazione dell'interazione tra i sistemi informativi X5 e il sistema informativo statale per i prodotti etichettati (GIS MP) per tracciare la circolazione dei prodotti etichettati. La piattaforma memorizza inoltre tutti i codici di etichettatura ricevuti da noi e l'intera cronologia del movimento di tali codici tra gli oggetti e aiuta a eliminare la riclassificazione dei prodotti etichettati. Prendendo l'esempio dei prodotti del tabacco, che sono stati inclusi nei primi gruppi di prodotti etichettati, un solo camion di sigarette contiene circa 600 pacchetti, ciascuno dei quali ha il proprio codice univoco. E il compito del nostro sistema è tracciare e verificare la legalità dei movimenti di ciascuno di questi pacchi tra magazzini e negozi e, infine, verificare l'ammissibilità della loro vendita all'acquirente finale. E registriamo circa 000 transazioni in contanti all'ora e dobbiamo anche registrare come ciascuno di questi pacchi è entrato nel negozio. Pertanto, tenendo conto di tutti i movimenti tra gli oggetti, ci aspettiamo decine di miliardi di registrazioni all’anno.

Squadra M

Nonostante Marcus sia considerato un progetto all'interno di X5, viene implementato utilizzando un approccio di prodotto. Il team lavora secondo Scrum. Il progetto è iniziato l'estate scorsa, ma i primi risultati sono arrivati ​​solo in ottobre: ​​il nostro team è stato completamente riunito, è stata sviluppata l'architettura del sistema e sono state acquistate le attrezzature. Ora il team conta 16 persone, sei delle quali sono coinvolte nello sviluppo del backend e del frontend, tre delle quali sono coinvolte nell'analisi del sistema. Altre sei persone sono coinvolte nei test manuali, di carico, automatizzati e nella manutenzione del prodotto. Inoltre, abbiamo uno specialista SRE.

Non solo gli sviluppatori scrivono codice nel nostro team; quasi tutti i ragazzi sanno come programmare e scrivere autotest, caricare script e script di automazione. Prestiamo particolare attenzione a questo, poiché anche il supporto del prodotto richiede un elevato livello di automazione. Cerchiamo sempre di consigliare e aiutare i colleghi che non hanno mai programmato prima e di dare loro alcuni piccoli compiti su cui lavorare.

A causa della pandemia di coronavirus, abbiamo trasferito l’intero team al lavoro da remoto; la disponibilità di tutti gli strumenti per la gestione dello sviluppo, il flusso di lavoro costruito in Jira e GitLab hanno permesso di superare facilmente questa fase. I mesi trascorsi a distanza hanno dimostrato che la produttività del team non ne ha risentito; per molti il ​​comfort sul lavoro è aumentato, mancava solo la comunicazione dal vivo.

Riunione del team remoto

"Camminare con le mie scarpe" - aspetta, sono segnate?

Riunioni durante il lavoro a distanza

"Camminare con le mie scarpe" - aspetta, sono segnate?

Stack tecnologico della soluzione

Il repository standard e lo strumento CI/CD per X5 è GitLab. Lo utilizziamo per l'archiviazione del codice, i test continui e la distribuzione su server di test e di produzione. Utilizziamo anche la pratica della revisione del codice, quando almeno 2 colleghi devono approvare le modifiche apportate dallo sviluppatore al codice. Gli analizzatori di codice statico SonarQube e JaCoCo ci aiutano a mantenere il nostro codice pulito e a garantire il livello richiesto di copertura dei test unitari. Tutte le modifiche al codice devono passare attraverso questi controlli. Tutti gli script di test eseguiti manualmente vengono successivamente automatizzati.

Per implementare con successo i processi aziendali da parte di “Marcus”, abbiamo dovuto risolvere una serie di problemi tecnologici, ciascuno in ordine.

Compito 1. La necessità di scalabilità orizzontale del sistema

Per risolvere questo problema, abbiamo scelto un approccio all'architettura basato sui microservizi. Allo stesso tempo, era molto importante comprendere le aree di responsabilità dei servizi. Abbiamo cercato di suddividerli in operazioni aziendali, tenendo conto delle specificità dei processi. Ad esempio, l'accettazione in un magazzino non è un'operazione molto frequente, ma su larga scala, durante la quale è necessario ottenere rapidamente dall'ente regolatore statale informazioni sulle unità di merce accettate, il cui numero in una consegna raggiunge le 600000 , verificare l'ammissibilità dell'accettazione di questo prodotto nel magazzino e restituire tutte le informazioni necessarie per il sistema di automazione del magazzino. Ma la spedizione dai magazzini ha un'intensità molto maggiore, ma allo stesso tempo opera con piccoli volumi di dati.

Implementiamo tutti i servizi su base stateless e proviamo anche a dividere le operazioni interne in fasi, utilizzando quelli che chiamiamo self-topic di Kafka. Questo è quando un microservizio invia un messaggio a se stesso, che consente di bilanciare il carico su operazioni che richiedono più risorse e semplifica la manutenzione del prodotto, ma ne parleremo più avanti.

Abbiamo deciso di separare i moduli per l'interazione con i sistemi esterni in servizi separati. Ciò ha permesso di risolvere il problema del cambiamento frequente delle API dei sistemi esterni, praticamente senza alcun impatto sui servizi con funzionalità aziendali.

"Camminare con le mie scarpe" - aspetta, sono segnate?

Tutti i microservizi sono distribuiti in un cluster OpenShift, che risolve sia il problema della scalabilità di ciascun microservizio sia ci consente di non utilizzare strumenti di Service Discovery di terze parti.

Compito 2. La necessità di mantenere un carico elevato e uno scambio di dati molto intenso tra i servizi della piattaforma: Nella sola fase di avvio del progetto vengono eseguite circa 600 operazioni al secondo. Prevediamo che questo valore aumenti fino a 5000 operazioni/sec man mano che i punti vendita si collegano alla nostra piattaforma.

Questo problema è stato risolto implementando un cluster Kafka e abbandonando quasi completamente l’interazione sincrona tra i microservizi della piattaforma. Ciò richiede un'analisi molto attenta dei requisiti di sistema, poiché non tutte le operazioni possono essere asincrone. Allo stesso tempo, non solo trasmettiamo eventi tramite il broker, ma trasmettiamo anche tutte le informazioni aziendali richieste nel messaggio. Pertanto, la dimensione del messaggio può raggiungere diverse centinaia di kilobyte. Il limite della dimensione dei messaggi in Kafka ci impone di prevedere con precisione la dimensione dei messaggi e, se necessario, di dividerli, ma la divisione è logica, correlata alle operazioni aziendali.
Ad esempio, dividiamo in scatole le merci che arrivano in macchina. Per le operazioni sincrone vengono allocati microservizi separati e vengono eseguiti test di carico approfonditi. L'utilizzo di Kafka ci ha presentato un'altra sfida: testare il funzionamento del nostro servizio tenendo conto dell'integrazione di Kafka rende asincroni tutti i nostri test unitari. Abbiamo risolto questo problema scrivendo i nostri metodi di utilità utilizzando Embedded Kafka Broker. Ciò non elimina la necessità di scrivere test unitari per i singoli metodi, ma preferiamo testare casi complessi utilizzando Kafka.

È stata prestata molta attenzione al tracciamento dei log in modo che il loro TraceId non andasse perso quando si verificano eccezioni durante il funzionamento dei servizi o quando si lavora con il batch Kafka. E se non si sono verificati problemi particolari con il primo, nel secondo caso siamo costretti a registrare tutti i TraceId con cui è arrivato il batch e selezionarne uno per continuare la traccia. Quindi, effettuando la ricerca in base al TraceId originale, l'utente scoprirà facilmente con quale è proseguita la tracciatura.

Attività 3. Necessità di archiviare una grande quantità di dati: Più di 1 miliardo di etichette all'anno solo per il tabacco arrivano a X5. Richiedono un accesso costante e rapido. In totale, il sistema deve elaborare circa 10 miliardi di registrazioni della cronologia dei movimenti di queste merci etichettate.

Per risolvere il terzo problema è stato scelto il database NoSQL MongoDB. Abbiamo creato un frammento di 5 nodi e ogni nodo ha un set di replica di 3 server. Ciò consente di ridimensionare il sistema orizzontalmente, aggiungendo nuovi server al cluster e garantirne la tolleranza agli errori. Qui abbiamo riscontrato un altro problema: garantire la transazionalità nel cluster mongo, tenendo conto dell'uso di microservizi scalabili orizzontalmente. Ad esempio, uno dei compiti del nostro sistema è identificare i tentativi di rivendita di prodotti con gli stessi codici di etichettatura. Qui compaiono sovrapposizioni con scansioni errate o operazioni errate da parte dei cassieri. Abbiamo scoperto che tali duplicati possono verificarsi sia all'interno di un batch Kafka elaborato, sia all'interno di due batch elaborati in parallelo. Pertanto, il controllo dei duplicati interrogando il database non ha dato nulla. Per ciascun microservizio abbiamo risolto il problema separatamente in base alla logica aziendale di questo servizio. Ad esempio, per gli assegni, abbiamo aggiunto un assegno all'interno del batch e un'elaborazione separata per la comparsa di duplicati durante l'inserimento.

Per garantire che il lavoro degli utenti con la cronologia delle operazioni non influisca in alcun modo sulla cosa più importante: il funzionamento dei nostri processi aziendali, abbiamo separato tutti i dati storici in un servizio separato con un database separato, che riceve informazioni anche tramite Kafka . In questo modo, gli utenti lavorano con un servizio isolato senza influenzare i servizi che elaborano i dati per le operazioni in corso.

Attività 4: rielaborazione e monitoraggio della coda:

Nei sistemi distribuiti sorgono inevitabilmente problemi ed errori nella disponibilità di database, code e fonti di dati esterne. Nel caso di Marcus la fonte di tali errori è l’integrazione con sistemi esterni. Era necessario trovare una soluzione che consentisse ripetute richieste di risposte errate con un certo timeout specificato, ma allo stesso tempo non interrompesse l'elaborazione delle richieste riuscite nella coda principale. A questo scopo è stato scelto il cosiddetto concetto di “topic based retry”. Per ogni argomento principale vengono creati uno o più argomenti di ripetizione ai quali vengono inviati messaggi errati e allo stesso tempo viene eliminato il ritardo nell'elaborazione dei messaggi dall'argomento principale. Schema di interazione -

"Camminare con le mie scarpe" - aspetta, sono segnate?

Per implementare tale schema, avevamo bisogno di quanto segue: integrare questa soluzione con Spring ed evitare la duplicazione del codice. Navigando sul web ci siamo imbattuti in una soluzione simile basata su Spring BeanPostProccessor, ma ci è sembrata inutilmente macchinosa. Il nostro team ha creato una soluzione più semplice che ci consente di integrarci nel ciclo primaverile per creare consumatori e aggiungere inoltre i consumatori Riprova. Abbiamo offerto un prototipo della nostra soluzione al team Spring, potete vederlo qui. Il numero di Retry Consumers e il numero di tentativi per ciascun consumatore sono configurati tramite parametri, a seconda delle esigenze del processo aziendale, e affinché tutto funzioni, non resta che aggiungere l'annotazione org.springframework.kafka.annotation.KafkaListener , che è familiare a tutti gli sviluppatori Spring.

Se non è stato possibile elaborare il messaggio dopo tutti i tentativi, passa a DLT (argomento lettera morta) utilizzando Spring DeadLetterPublishingRecoverer. Su richiesta di supporto, abbiamo ampliato questa funzionalità e creato un servizio separato che consente di visualizzare i messaggi inclusi in DLT, stackTrace, traceId e altre informazioni utili su di essi. Inoltre, sono stati aggiunti il ​​monitoraggio e gli avvisi a tutti gli argomenti DLT e ora, infatti, la comparsa di un messaggio in un argomento DLT è un motivo per analizzare e correggere un difetto. Questo è molto conveniente: dal nome dell'argomento capiamo immediatamente in quale fase del processo si è verificato il problema, il che accelera notevolmente la ricerca della sua causa principale.

"Camminare con le mie scarpe" - aspetta, sono segnate?

Più recentemente, abbiamo implementato un'interfaccia che ci consente di inviare nuovamente i messaggi utilizzando il nostro supporto dopo aver eliminato le loro cause (ad esempio, ripristinando la funzionalità del sistema esterno) e, ovviamente, stabilendo il difetto corrispondente per l'analisi. È qui che tornano utili i nostri self-topic: per non riavviare una lunga catena di lavorazione, è possibile riavviarla dal passaggio desiderato.

"Camminare con le mie scarpe" - aspetta, sono segnate?

Funzionamento della piattaforma

La piattaforma è già in funzione produttiva, ogni giorno effettuiamo consegne e spedizioni, colleghiamo nuovi centri di distribuzione e negozi. Nell'ambito del progetto pilota, il sistema funziona con i gruppi di prodotti "Tabacco" e "Scarpe".

Tutto il nostro team partecipa alla conduzione di progetti pilota, analizza i problemi emergenti e fornisce suggerimenti per migliorare il nostro prodotto, dal miglioramento dei registri alla modifica dei processi.

Per non ripetere i nostri errori, tutti i casi riscontrati durante il progetto pilota si riflettono in test automatizzati. La presenza di un gran numero di test automatici e test unitari consente di condurre test di regressione e installare un hotfix letteralmente in poche ore.

Ora continuiamo a sviluppare e migliorare la nostra piattaforma e affrontiamo costantemente nuove sfide. Se sei interessato, parleremo delle nostre soluzioni nei seguenti articoli.

Fonte: habr.com

Aggiungi un commento