«Walking in my shoes» — fermati, ma sono contrassegnati?

Dal 2019, in Russia, è in vigore una legge sulla marcatura obbligatoria. Questa legge non si applica a tutti i gruppi di prodotti e le scadenze per l'entrata in vigore della marcatura obbligatoria variano per i diversi gruppi di merce. I primi prodotti soggetti a marcatura obbligatoria includono tabacco, calzature e farmaci; in seguito, saranno inclusi anche altri articoli, come profumi, tessuti e latte. Questa innovazione legislativa ha incentivato lo sviluppo di nuove soluzioni IT per tracciare l'intero ciclo di vita del prodotto, dalla produzione fino all'acquisto da parte del consumatore finale, per tutti i soggetti coinvolti: sia lo stato che tutte le aziende che vendono beni con marcatura obbligatoria.

In X5, il sistema che traccerà i prodotti con marcatura e scambierà dati con lo stato e i fornitori si chiama “Marcus”. Vi racconteremo con ordine come e chi l'ha sviluppato, qual è la sua architettura tecnologica e perché possiamo esserne orgogliosi.

«Walking in my shoes» — fermati, ma sono contrassegnati?

Un vero HighLoad

“Marcus” affronta molte sfide, la principale è l'integrazione tra i sistemi informatici di X5 e il sistema informatico statale per la tracciabilità dei prodotti marcati (GIS MP) per seguire il movimento dei prodotti marcati. Inoltre, la piattaforma memorizza tutti i codici di marcatura ricevuti e l'intera storia del movimento di questi codici tra i diversi oggetti, aiutando a prevenire errori nella classificazione dei prodotti marcati. Prendendo ad esempio i prodotti del tabacco, che sono stati inclusi nei primi gruppi di beni marcati, un singolo camion di sigarette contiene circa 600.000 pacchetti, ognuno dei quali ha un codice unico. Il compito del nostro sistema è monitorare e verificare la legalità dei movimenti di ogni pacchetto tra magazzini e negozi, e alla fine garantire la legittimità della loro vendita al consumatore finale. Registriamo circa 125.000 operazioni di cassa all'ora, e dobbiamo anche tenere traccia di come ogni pacchetto sia arrivato nel negozio. Considerando tutti i movimenti tra gli oggetti, prevediamo decine di miliardi di registrazioni all'anno.

Il team M

Nonostante “Marcus” sia considerato un progetto di X5, viene implementato con un approccio di prodotto. Il team lavora secondo la metodologia Scrum. Il progetto è iniziato la scorsa estate, ma i primi risultati sono arrivati solo in ottobre: è stato formato un team completo, è stata sviluppata l'architettura del sistema e acquistato l'hardware. Attualmente, la squadra è composta da 16 persone, di cui sei si occupano dello sviluppo backend e frontend, tre dell'analisi sistematica. Altre sei persone si occupano di test manuali, di carico, di automazione e del supporto del prodotto. Inoltre, abbiamo uno specialista SRE.

Non scrivono codice solo gli sviluppatori nel nostro team; praticamente tutti i ragazzi sanno programmare e scrivono autovalutazioni, script di carico e script di automazione. Prestiamo particolare attenzione a questo, poiché anche il supporto del prodotto richiede un alto livello di automazione. Cerchiamo sempre di consigliare e aiutare i colleghi che non hanno mai programmato, assegnando loro compiti brevi.

A causa della pandemia di coronavirus, abbiamo trasferito l'intero team al lavoro remoto; la disponibilità di tutti gli strumenti per la gestione dello sviluppo e il workflow stabilito in Jira e GitLab hanno reso facile affrontare questa fase. I mesi trascorsi in remoto hanno dimostrato che la produttività del team non è calata; per molti il comfort lavorativo è aumentato, l'unica cosa che manca è la comunicazione di persona.

Incontro del team prima del lavoro remoto

«Walking in my shoes» — fermati, ma sono contrassegnati?

Incontri durante il lavoro remoto

«Walking in my shoes» — fermati, ma sono contrassegnati?

Il stack tecnologico della soluzione

Il repository standard e lo strumento CI/CD per X5 è GitLab. Lo utilizziamo per memorizzare codice, test continuo e distribuzione su server di test e produzione. Adottiamo anche la pratica della revisione del codice, dove almeno due colleghi devono approvare le modifiche apportate dal programmatore. Gli analizzatori statici di codice SonarQube e JaCoCo ci aiutano a mantenere il codice pulito e garantire il livello di copertura richiesto per i test unitari. Tutte le modifiche al codice devono obbligatoriamente superare questi controlli. Tutti gli scenari di test eseguiti manualmente vengono successivamente automatizzati.

Per il successo dei processi aziendali di “Marcus”, abbiamo dovuto risolvere una serie di sfide tecnologiche, una per una.

Compito 1. Necessità di scalabilità orizzontale del sistema.

Per affrontare questa sfida, abbiamo scelto un approccio architetturale basato su microservizi. Era molto importante comprendere le aree di responsabilità dei servizi. Abbiamo cercato di suddividerli in base alle operazioni aziendali, tenendo conto delle specificità dei processi. Ad esempio, l'accettazione in magazzino è un'operazione non molto frequente ma molto intensa, durante la quale è necessario ottenere rapidamente dall'ente regolatorio informazioni sulle unità di prodotto accettate, il cui numero in una singola spedizione può arrivare fino a 600.000, verificare l'ammissibilità dell'accettazione di questi beni in magazzino e fornire tutte le informazioni necessarie al sistema di automazione del magazzino. D'altra parte, le spedizioni dai magazzini sono molto più frequenti, ma operano su volumi di dati più piccoli.

Tutti i nostri servizi sono realizzati secondo il principio stateless e cerchiamo di suddividere anche le operazioni interne in fasi, utilizzando, come li chiamiamo, self-topic Kafka. Questo avviene quando un microservizio invia un messaggio a se stesso, permettendo di bilanciare il carico nelle operazioni più intensive e semplificando la manutenzione del prodotto, ma ne parleremo più avanti.

Abbiamo deciso di separare i moduli di interazione con i sistemi esterni in servizi autonomi. Questo ha permesso di risolvere il problema delle API esterne in continua evoluzione senza impattare i servizi con funzionalità aziendale.

«Walking in my shoes» — fermati, ma sono contrassegnati?

Tutti i microservizi vengono distribuiti all'interno di un cluster OpenShift, il quale 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 supportare un elevato carico e un intenso scambio di dati tra i servizi della piattaforma: solo nella fase di avvio del progetto vengono eseguite circa 600 operazioni al secondo. Ci aspettiamo un incremento di questo valore fino a 5000 op/sec man mano che i punti vendita si collegano alla nostra piattaforma.

Abbiamo affrontato questa sfida distribuendo un cluster Kafka e rinunciando praticamente all'interazione sincrona tra i microservizi della piattaforma. Ciò richiede un'analisi molto attenta dei requisiti del sistema, poiché non tutte le operazioni possono essere asincrone. Inoltre, non ci limitiamo a trasmettere eventi tramite un broker, ma inviamo nel messaggio tutte le informazioni aziendali necessarie. Pertanto, il peso del messaggio può arrivare a diverse centinaia di kilobyte. Il limite sul volume dei messaggi in Kafka richiede una previsione accurata delle dimensioni dei messaggi e, se necessario, li dividiamo, ma tale divisione è logica e legata alle operazioni aziendali.
Ad esempio, un prodotto arrivato in un veicolo viene suddiviso in base alle scatole. Per le operazioni sincrone vengono dedicati microservizi separati e viene condotta un'attenta prova di carico. L'uso di Kafka ci ha posto una nuova sfida: il collaudo della funzionalità del nostro servizio, considerando l'integrazione con Kafka, rende tutti i nostri test unitari asincroni. Abbiamo risolto questo compito scrivendo metodi utili personalizzati utilizzando un Broker Kafka Embedded. Questo non elimina la necessità di scrivere test unitari per metodi separati, ma preferiamo testare casi complessi utilizzando Kafka.

Abbiamo prestato molta attenzione alla tracciabilità dei log, affinché il loro TraceId non andasse perso durante l'insorgere di eccezioni durante il funzionamento dei servizi o durante l'interazione con il batch di Kafka. Se per il primo caso non ci sono stati problemi particolari, nel secondo caso siamo stati costretti a registrare nel log tutti i TraceId con cui è arrivato il batch e scegliere uno per continuare la tracciabilità. Così, cercando tramite il TraceId originale, l'utente può facilmente scoprire con quale è proseguita la tracciabilità.

Compito 3. La necessità di memorizzare un grande volume di dati: più di 1 miliardo di etichette all'anno, solo per il tabacco, entra in X5. Queste richiedono accesso costante e veloce. L'intero sistema deve elaborare circa 10 miliardi di registrazioni sulla storia dei movimenti dei prodotti etichettati.

Per affrontare la terza sfida, è stata scelta una base NoSQL MongoDB. Abbiamo costruito uno shard composto da 5 nodi, e in ogni nodo un Replica Set di 3 server. Questo consente di scalare il sistema orizzontalmente, aggiungendo nuovi server nel cluster e garantire la sua resilienza. Qui ci siamo trovati di fronte a un’altra problematica: garantire la transazionalità nel cluster Mongo tenendo conto dell'uso di microservizi scalabili orizzontalmente. Ad esempio, uno degli obiettivi del nostro sistema è identificare tentativi di rivendita di prodotti con codici di marcatura identici. Qui si presentano conflitti dovuti a scansioni errate o a operazioni errate da parte dei cassieri. Abbiamo scoperto che tali duplicati possono verificarsi sia all'interno di un batch Kafka elaborato che tra due batch elaborati in parallelo. Pertanto, la verifica della presenza di duplicati tramite query al database non forniva risultati. Abbiamo risolto il problema separatamente per ciascuno dei microservizi sulla base della logica di business specifica di ciascun servizio. Ad esempio, per le ricevute abbiamo aggiunto un controllo all'interno del batch e un’elaborazione separata per identificare i duplicati durante l'inserimento.

Per garantire che l'interazione degli utenti con la cronologia delle operazioni non influisse sul funzionamento dei nostri processi aziendali, abbiamo isolato tutti i dati storici in un servizio separato con un proprio database, che riceve informazioni tramite Kafka. Così gli utenti lavorano con un servizio isolato, senza influenzare i servizi che elaborano i dati delle operazioni correnti.

Compito 4. Rielaborazione delle code e monitoraggio:

Nelle architetture distribuite, sorgono inevitabilmente problemi e errori di disponibilità dei database, delle code e delle fonti di dati esterne. Nel caso di "Markus", la fonte di tali errori è l'integrazione con sistemi esterni. Era necessario trovare una soluzione che permettesse di eseguire richieste ripetute in caso di risposte errate con un timeout specificato, senza interrompere l'elaborazione delle richieste riuscite nella coda principale. Pertanto è stata scelta la cosiddetta concezione "topic based retry". Per ogni topic principale, vengono creati uno o più topic di retry, nei quali vengono indirizzati i messaggi errati, escludendo nel contempo ritardi nell'elaborazione dei messaggi dal topic principale. Lo schema di interazione è

«Walking in my shoes» — fermati, ma sono contrassegnati?

Per implementare tale schema avevamo bisogno di integrare questa soluzione con Spring e di evitare la duplicazione del codice. Navigando nella rete, ci siamo imbattuti in una soluzione simile basata su Spring BeanPostProcessor, ma l'abbiamo trovata eccessivamente complessa. La nostra squadra ha creato una soluzione più semplice, che permette di integrarsi nel ciclo di creazione dei consumer di Spring e aggiungere ulteriori Retry Consumer. Abbiamo presentato il prototipo della nostra soluzione al team di Spring, può essere visualizzato qui. Il numero di Retry Consumer e il numero di tentativi di ciascun consumer sono configurabili tramite parametri, in base alle esigenze del processo aziendale, e per far funzionare tutto ciò, è sufficiente aggiungere l'annotazione familiare a tutti i programmatori Spring org.springframework.kafka.annotation.KafkaListener.

Nel caso in cui un messaggio non possa essere elaborato dopo tutti i tentativi di retry, esso viene inviato al DLT (dead letter topic) tramite Spring DeadLetterPublishingRecoverer. Su richiesta del supporto, abbiamo ampliato questa funzionalità creando un servizio separato che consente di visualizzare i messaggi che hanno raggiunto il DLT, il stackTrace, il traceId e altre informazioni utili su di essi. Inoltre, sono stati aggiunti monitoraggi e avvisi per tutti i topic DLT, e attualmente, l'apparizione di un messaggio nel topic DLT è un motivo di analisi e di apertura di un difetto. Questo è molto utile: dal nome del topic comprendiamo immediatamente in quale fase del processo si è verificato il problema, il che accelera notevolmente la ricerca della causa principale.

«Walking in my shoes» — fermati, ma sono contrassegnati?

Poco tempo fa, abbiamo implementato un'interfaccia che consente di reinviare messaggi tramite il nostro supporto dopo la risoluzione delle loro cause (ad esempio, il ripristino della funzionalità di un sistema esterno) e, ovviamente, l'apertura del difetto corrispondente per l'analisi. Qui sono stati utili i nostri self-topic, così da non dover riavviare l'intera catena di elaborazione; è possibile riavviarla dal passo richiesto.

«Walking in my shoes» — fermati, ma sono contrassegnati?

Utilizzo della piattaforma

La piattaforma è già in produzione, ogni giorno effettuiamo consegne e scarichi, collegando nuovi centri di distribuzione e negozi. Nel quadro del progetto pilota, il sistema lavora con gruppi di prodotti "Tabacco" e "Calzature".

Tutta la nostra squadra partecipa alla conduzione dei piloti, analizza i problemi emergenti e propone miglioramenti per il nostro prodotto, dall'ottimizzazione dei log a modifiche nei processi.

Per evitare di ripetere gli stessi errori, tutti i casi trovati durante il pilota sono riflessi nei test automatizzati. La presenza di un gran numero di test automatici e unitari consente di effettuare test di regressione e di implementare hotfix nel giro di poche ore.

Attualmente continuiamo a sviluppare e perfezionare la nostra piattaforma e ci troviamo costantemente di fronte a nuove sfide. Se siete interessati, saremo felici di condividere le nostre soluzioni nei prossimi articoli.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS 🔥 Acquista hosting affidabile per siti web con protezione DDoS, server VPS VDS | ProHoster