{"id":1139,"date":"2020-01-24T12:36:41","date_gmt":"2020-01-24T11:36:41","guid":{"rendered":"https:\/\/blog.besharp.it\/?p=1139"},"modified":"2021-03-17T15:09:41","modified_gmt":"2021-03-17T14:09:41","slug":"disaccoppiare-servizi-con-sqs-e-lambda-trigger","status":"publish","type":"post","link":"https:\/\/blog.besharp.it\/it\/disaccoppiare-servizi-con-sqs-e-lambda-trigger\/","title":{"rendered":"Disaccoppiare servizi con SQS e Lambda trigger"},"content":{"rendered":"
La progettazione di un’applicazione basata su microservizi presenta alcune importanti sfide da affrontare come implementare sistemi di service discovery, la standardizzazione della comunicazione interna tra di essi, la loro sincronizzazione e molto altro ancora.<\/p>\n
Uno degli scenari pi\u00f9 comuni \u00e8 il disaccoppiamento di due servizi quando il primo si basa sull’altro per l’elaborazione asincrona di alcuni dati. Possiamo risolvere questo tipo di problemi utilizzando un classico approccio di tipo producer-consumer.<\/p>\n
Nel Cloud, possiamo sfruttare i managed services per facilitare la realizzazione di tale architettura.
\nL’infrastruttura di riferimento per queste situazioni consiste nell’utilizzo una o pi\u00f9 code SQS per disaccoppiare il producer dal consumer.<\/p>\n
I managed services garantiscono numerosi benefici tra cui:<\/p>\n
Prima di addentrarci nella realizzazione della soluzione su AWS \u00e8 doveroso fare una veloce carrellata sui servizi che andremo ad utilizzare pi\u00f9 tardi. Cominciamo!<\/p>\n
Amazon Simple Queue Service (SQS) \u00e8 un servizio di code completamente gestito che permette di disaccopiare e far scalare microservizi, sistemi distribuiti e applicazioni Serverless.<\/p>\n
Essendo un servizio gestito, la sua applicazione libera totalmente il team di sviluppo dalla complessit\u00e0 di gestione di un middleware message-oriented e permette agli sviluppatori di concentrarsi sulle attivit\u00e0 core.<\/p>\n
Utilizzando le API \u00e8 possibile inviare, memorizzare e ricevere messaggi tra i componenti software a qualsiasi volume, senza perdere messaggi e non dipendendo dalla disponibilit\u00e0 di altri servizi.<\/p>\n
SQS offre due tipi di code di messaggi:<\/p>\n
AWS Lambda \u00e8 un servizio di calcolo che consente di eseguire il codice senza dover effettuare il provisioning delle risorse e senza dover gestire alcun server.<\/p>\n
AWS Lambda esegue il codice solo quando necessario e scala automaticamente a seconda delle richieste sostenendo efficacemente qualunque carico di lavoro, da poche richieste al giorno a migliaia al secondo.<\/p>\n
Il servizio si occupa anche di eseguire il codice su nfrastrutture di calcolo in alta disponibilit\u00e0 by design e di eseguire tutta l’amministrazione delle risorse di calcolo: dalla manutenzione dei server e del sistema operativo, al provisioning della capacit\u00e0, dallo scaling automatico, al monitoraggio e al logging del codice.
\nDa tenere presente \u00e8 il fatto che non \u00e8 possibile accedere alle istanze di calcolo o personalizzare il sistema operativo nei tempi di esecuzione previsti.<\/p>\n
Ora che i servizi sono stati introdotti, passiamo ad esplorare la soluzione.<\/p>\n
Per rendere la discussione pi\u00f9 pragmatica, facciamo finta di essere nella seguente situazione:<\/p>\n
Abbiamo un’applicazione web serverless che permette agli utenti di selezionare documenti e di scaricarli. Per ottimizzare i tempi di trasferimento occorre costruire un servizio per creare un archivio compresso contenente i file e salvarlo su S3. Il servizio creer\u00e0 anche un link firmato per consentire il download dell’archivio.<\/p>\n
In questa occasione non implementeremo nessuna delle funzionalit\u00e0, ma ci limiteremo ad utilizzare un po’ di codice stub per dimostrare la soluzione. In ogni caso, avere una situazione realistica ci aiuter\u00e0 a capire del dettaglio il meccanismo di funzionamento e i potenziali problemi che possono sorgere.<\/p>\n
Proponiamo ora la tipica infrastruttura AWS per il modello producer-consumer basata su AWS Lambda and SQS.<\/p>\n
<\/p>\n
Affinch\u00e9 tutto funzioni, \u00e8 necessario configurare un trigger SQS per il consumer; in questo modo il consumer lambda verr\u00e0 attivato nel momento in cui saranno rilevati messaggi sulla coda.<\/p>\n
Tornando alla nostra situazione di esempio, quindi:<\/p>\n
Il producer lambda sar\u00e0 la funzione lambda nel backend dell’applicazione web adibita all’invio di una richiesta al servizio di compressione, mentre il consumer sar\u00e0 un altro microservizio basato su Lambda col compito di leggere una lista di file (forse URI s3) e di omprimerli prima di memorizzare il risultato su S3. Supponiamo inoltre che questo servizio generi e memorizzi anche un link firmato in un database, da utilizzare successivamente.<\/p>\n
Vediamo ora il flusso di base per l’implementazione del pattern producer-consumer:<\/p>\n
il servizio di back-end basato su Lambda utilizzer\u00e0 AWS SDK per effettuare una chiamata API di tipo SendMessage verso SQS aggiungendo un messaggio alla coda. Il consumer Lambda verr\u00e0 invocato automaticamente dal servizio Lambda quando il messaggio sar\u00e0 stato aggiunto alla coda. A questo punto, il consumer proceder\u00e0 ad evadere la richiesta ricevuta “consumando” il messaggio.<\/p>\n
La funzione sar\u00e0 invocata dal servizio Lambda che ricever\u00e0 i messaggi come parametro di input.<\/p>\n
Per le code standard, Lambda sfrutta il long-polling standard (una chiamata chiamata ogni 20 secondi) di SQS per interrogare una coda fino a quando quest\u00e0 non diventa attiva.<\/p>\n
Quando i messaggi saranno disponibili, Lambda sar\u00e0 in grado di leggere fino a 5 batch e li invier\u00e0 alla nostra funzione. La dimensione di ciascun batch non \u00e8 altro che il numero di messaggi inviati ad una Lambda e pu\u00f2 essere modificata nelle impostazioni di trigger (1 – 10). Modificando il paramentro mentre sono ancora disponibili dei messaggi, Lambda aumenter\u00e0 il numero di processi dedicati alla lettura dei batch (fino a 60 istanze in pi\u00f9 al minuto). Il numero massimo di batch che possono essere elaborati contemporaneamente da una mappatura della sorgente dell’evento \u00e8 1000.<\/p>\n
Per le code FIFO, Lambda invia messaggi alla funzione nell’ordine in cui li riceve.<\/p>\n
Quando si invia un messaggio a una coda FIFO, si specifica un ID del gruppo di messaggi. Amazon SQS assicura che i messaggi dello stesso gruppo siano consegnati a Lambda in ordine. Lambda ordina i messaggi in gruppi e invia un solo lotto alla volta corrispondente a ciascun gruppo. Nel caso in cui la funzione restituisca un errore, saranno effettuati tutti i tentativi sui messaggi interessati prima che Lambda possa ricedvere ulteriori messaggi dallo stesso gruppo.<\/p>\n
Quindi, fondamentalmente, se si configura il trigger con una dimensione di batch di 1, Lambda eseguir\u00e0 il polling della coda e invocher\u00e0 una funzione ogni volta che un nuovo lavoro sar\u00e0 disponibile su di essa. Ogni lambda ricever\u00e0 esattamente 1 lavoro.
\nSe configurate un batch di 5, Lambda invocher\u00e0 comunque la funzione il prima possibile, ma fino a 5 messaggi alla volta saranno consegnati.<\/p>\n
Quando il servizio lambda ricever\u00e0 un messaggio, dovr\u00e0 eseguire il lavoro e ritornare un risultato senza errori affinch\u00e8 il messaggio venga correttamente eliminato dalla coda.<\/p>\n
Le cause di errore possono essere le pi\u00f9 varie, dal caso in cui la funzione Lambda non disponga di spazio su diosco sufficiente alla presenza di URI di Input non validi nel messaggio.<\/p>\n
Cosa succede?<\/p>\n
Semplice: se non \u00e8 possibile per qualsiasi motivo completare il lavoro, il messaggio resta non disponibile per il tempo di timeout (configurabile e personalizzabile). Al termine del periodo di timeout, poi, il messaggio torner\u00e0 disponile e sar\u00e0 pronto per essere elaborato da un altro consumer innescando nuovamente il timeout. Si entrerebbe cos\u00ec in un (potenzialmente) infinito e costosissimo loop. Per evitare di rimanere “intrappolati” in caso di errori, si potrebbe semplicemente recuperare tutte le eccezioni all’interno del codice Lambda e salvare il fallimento in modo tale che il messaggio possa comunque essere considerato “consumato”.<\/p>\n
Esiste per\u00f2 un modo per affrontare i fallimenti nel modo migliore possibile<\/p>\n
In ogni coda di SQS \u00e8 presente un’impostazione opzionale per specificare il parametro Dead letter queues.<\/p>\n
Una coda di tipo dead letter \u00e8 una coda speciale in cui SQS inserisce automaticamente i messaggi che vengono rifiutati per un numero di volte configurabile.<\/p>\n
Cos\u00ec, ad esempio, \u00e8 possibile creare una coda aggiuntiva e specificare il suo ARN come DLQ per la coda principale. \u00c8 anche possibile specificare il numero massimo di tentativi – “maxReceiveCount” – nella coda principale, in modo che il nostro sistema consenta solo alcuni tentativi. Questo \u00e8 in realt\u00e0 molto utile, perch\u00e9, come gi\u00e0 accennato, un messaggio pu\u00f2 essere rifiutato per innumerevoli ragioni, alcune delle quali non sono legate al codice del consumer e sono probabilmente temporanee. Un esempio \u00e8 il raggiungimento della soglia-limite di Lambda currency.<\/p>\n
Tornando alla nostra situazione: aggiungere una dead letter queue ci aiuter\u00e0 a risparmiare tempo e denaro.<\/p>\n
Un altro accorgimento potrebbe essere la configuarzione di un allarme CloudWatch che avverta il team di sviluppo quando il numero di messaggi rifiutato eccede un certo limite.<\/p>\n
<\/p>\n
Ecco un semplice template CloudFormation che crea una coda, una coda dead letter e una funzione lambda configurata per essere attivata sui nuovi messaggi.<\/p>\n
<\/p>\n
AWSTemplateFormatVersion : 2010-09-09\r\nResources:\r\n \r\n LambdaExecutionRole:\r\n Type: AWS::IAM::Role\r\n Properties:\r\n AssumeRolePolicyDocument:\r\n Version: '2012-10-17'\r\n Statement:\r\n - Effect: Allow\r\n Principal:\r\n Service:\r\n - lambda.amazonaws.com\r\n Action:\r\n - sts:AssumeRole\r\n Policies:\r\n - PolicyName: allowLambdaLogs\r\n PolicyDocument:\r\n Version: '2012-10-17'\r\n Statement:\r\n - Effect: Allow\r\n Action:\r\n - logs:*\r\n Resource: arn:aws:logs:*:*:*\r\n - PolicyName: allowSqs\r\n PolicyDocument:\r\n Version: '2012-10-17'\r\n Statement:\r\n - Effect: Allow\r\n Action:\r\n - sqs:ReceiveMessage\r\n - sqs:DeleteMessage\r\n - sqs:GetQueueAttributes\r\n - sqs:ChangeMessageVisibility\r\n Resource: !GetAtt MyQueue.Arn\r\n \r\n LambdaConsumer:\r\n Type: AWS::Lambda::Function\r\n Properties:\r\n Code:\r\n ZipFile: |\r\n def lambda_handler(event, context):\r\n for record in event['Records']:\r\n print(record['body'])\r\n Handler: index.lambda_handler\r\n Role: !GetAtt LambdaExecutionRole.Arn\r\n Runtime: python3.7\r\n Timeout: 10\r\n MemorySize: 128\r\n \r\n LambdaFunctionEventSourceMapping:\r\n Type: AWS::Lambda::EventSourceMapping\r\n Properties:\r\n BatchSize: 1\r\n Enabled: true\r\n EventSourceArn: !GetAtt MyQueue.Arn\r\n FunctionName: !GetAtt LambdaConsumer.Arn\r\n \r\n MyQueue:\r\n Type: AWS::SQS::Queue\r\n Properties:\r\n DelaySeconds: 0\r\n VisibilityTimeout: 30\r\n RedrivePolicy:\r\n deadLetterTargetArn : !GetAtt DLQ.Arn\r\n maxReceiveCount : 3\r\n DLQ:\r\n Type: AWS::SQS::Queue\r\n Properties:\r\n DelaySeconds: 0\r\n VisibilityTimeout: 180\r\n<\/pre>\nOra \u00e8 possibile implementare e testare il template inserendo un qualsiasi messaggio nella coda e cercando i log di esecuzione di lambda.<\/p>\n
<\/p>\n
START RequestId: 45dddad8-49d3-4378-ba7a-2e03217e9c40 Version: $LATEST\r\nHello from SQS!\r\nEND RequestId: 45dddad8-49d3-4378-ba7a-2e03217e9c40\r\nREPORT RequestId: 45dddad8-49d3-4378-ba7a-2e03217e9c40\tDuration: 1.56 ms\tBilled Duration: 100 ms\tMemory Size: 128 MB\tMax Memory Used: 56 MB\tInit Duration: 119.17 ms\r\n<\/pre>\nIn questo articolo abbiamo esplorato uno dei modelli pi\u00f9 comuni per disaccoppiare i microservizi serverless utilizzando servizi managed ad alta disponibilit\u00e0 su AWS ottenendo un’architettura estremamente flessibile, resiliente e solida.<\/p>\n
Soddisfatti? Fateci sapere! \ud83d\ude09<\/p>\n","protected":false},"excerpt":{"rendered":"
La progettazione di un’applicazione basata su microservizi presenta alcune importanti sfide da affrontare come implementare sistemi di service discovery, la […]<\/p>\n","protected":false},"author":8,"featured_media":1146,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[481],"tags":[307,263,271,309,267],"class_list":["post-1139","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-architecting","tag-amazon-simple-queue-service-sqs","tag-aws-lambda","tag-devops","tag-microservices","tag-serverless"],"yoast_head":"\n
Disaccoppiare servizi con SQS e Lambda trigger - Proud2beCloud Blog<\/title>\n\n\n\n\n\n\n\n\n\n\n\n\n\t\n\t\n\t\n\n\n\n\n\n\n\t\n\t\n\t\n