– il nostro prodotto per la gestione del Backup e Disaster Recovery di infrastrutture AWS.\u00a0<\/span><\/p>\nA tal proposito \u00e8 stato particolarmente interessante il refactoring del sistema di notifiche interne all\u2019applicazione, che vengono \u201cpushate\u201d all\u2019interno dell\u2019interfaccia web al verificarsi di specifici eventi, come ad esempio la conclusione di un backup job o l\u2019apertura di una sessione di file-level recovery.<\/span><\/p>\nLa soluzione monolitica era basata su Ruby\/<\/span>ActionCable <\/b>e<\/span> Redis <\/b>e accentrava la gestione degli invii di notifiche in un controller dell\u2019applicazione.\u00a0<\/span><\/p>\nApprofittando del recente rilascio da parte di AWS delle API Websocket per API Gateway, abbiamo deciso di rifattorizzare l\u2019engine di gestione delle notifiche in un microservizio completamente serverless, utilizzando SQS per disaccoppiare l\u2019invio e la ricezione dei messaggi.<\/span><\/p>\nSempre recentemente sono stati introdotti i Lambda Layers e la possibilit\u00e0 di scrivere funzioni Lambda in Ruby (il linguaggio principale con cui \u00e8 sviluppato Noovolari Smart Backup); grazie a queste due feature abbiamo potuto riutilizzare buona parte del codice backend originale, mantenendolo pulito e facilmente integrabile in altre applicazioni.<\/span><\/p>\nQuesto approccio ci ha dato diversi spunti tecnici che abbiamo deciso di condividere in questo articolo, creando un piccolo tutorial su come realizzare un microservizio di notifiche completamente Serverless basato sui servizi AWS.\u00a0<\/span><\/p>\nTecnologie utilizzate<\/h2>\n
\u00a0<\/span>Per implementare questa soluzione andremo ad utilizzare i seguenti linguaggi e servizi:<\/span><\/p>\n\n- Html e JavaScript (jQuery)<\/span>\u00a0\u00a0\u00a0<\/span><\/li>\n
- AWS Lambda<\/span><\/li>\n
- Node.js su AWS Lambda<\/span><\/li>\n
- Ruby su AWS Lambda<\/span><\/li>\n
- Amazon API Gateway<\/span><\/li>\n
- Amazon SQS<\/span><\/li>\n
- Amazon DynamoDB<\/span><\/li>\n
- AWS IAM<\/span><\/li>\n<\/ul>\n
Implementazione lato client<\/span><\/h2>\nPer cominciare vediamo come attivare il canale WebSocket con API Gateway mediante l\u2019url che ci verr\u00e0 fornito all\u2019atto della creazione delle API di back-end.\u00a0<\/span><\/p>\nNel template principale dell\u2019applicazione abbiamo aggiunto la seguente funzione javascript:<\/span><\/p>\n<<\/span>script<\/span>><\/span>\r\n$<\/b>(<\/span>document<\/i><\/b>).<\/span>ready<\/b>(<\/span>function <\/b>() {<\/span>\r\n\u00a0\u00a0\u00a0<\/span>\/\/Setup notification system<\/span><\/i>\r\n\u00a0\u00a0\u00a0<\/span><\/i>noovolari<\/i><\/b>.<\/span>smartbackup<\/i><\/b>.<\/span>notifications<\/i><\/b>.<\/span>websocket<\/i><\/b>.<\/span>connect<\/span>(<\/span>\r\n'<\/b><%=<\/b> Rails<\/i><\/b>.<\/b>env <\/b>%><\/b>'<\/b>,<\/span>\r\n'<\/b><%=<\/b> Notification<\/i><\/b>::<\/b>NotificationAuthorizer<\/i><\/b>.<\/b>check_token<\/b>(<\/b>user_id<\/b>: <\/b>current_user<\/b>.<\/b>id<\/b>, <\/b>company_code<\/b>: <\/b>current_user<\/b>.<\/b>companies<\/b>.<\/b>first<\/b>.<\/b>code<\/b>) <\/b>%><\/b>'<\/b>\r\n \u00a0\u00a0);<\/span>\r\n<\/<\/span>script<\/span>><\/span><\/pre>\nQuesta funzione \u00e8 cos\u00ec implementata nel dettaglio:<\/span><\/p>\nnoovolari<\/i><\/b>.<\/span>smartbackup<\/i><\/b>.<\/span>notifications<\/i><\/b>.<\/span>websocket<\/i><\/b>.<\/span>connect <\/span>= <\/span>function<\/b>(stage, token) {<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>var <\/b>socketUrl <\/span>= <\/span>'wss:\/\/<\/b><WEBSOCKET_URL><\/b>\/'<\/b>+stage+<\/span>'?token='<\/b>+token;\r\n<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>\/\/ connecting to the websocket url and retrieve function for connect and disconnect<\/span><\/i>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/i>noovolari<\/i><\/b>.<\/span>smartbackup<\/i><\/b>.<\/span>notifications<\/i><\/b>.<\/span>websocket <\/b>=\u00a0 <\/span>new <\/b>WebSocket<\/i><\/b>(<\/span>socketUrl<\/span>);\r\n<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>noovolari<\/i><\/b>.<\/span>smartbackup<\/i><\/b>.<\/span>notifications<\/i><\/b>.<\/span>websocket<\/i><\/b>.<\/span>onopen <\/span>= <\/span>function <\/b>() {<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>console<\/i><\/b>.<\/span>info<\/span>(<\/span>'socket connection opened properly with...'<\/b>);<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0};<\/span>\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>noovolari<\/i><\/b>.<\/span>smartbackup<\/i><\/b>.<\/span>notifications<\/i><\/b>.<\/span>websocket<\/i><\/b>.<\/span>onclose <\/span>= <\/span>function <\/b>() {<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>\/\/ websocket is closed.<\/span><\/i>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><\/i>console<\/i><\/b>.<\/span>info<\/span>(<\/span>\"Connection closed...\"<\/b>);<\/span>\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0};<\/span>\r\n\r\n\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>noovolari<\/i><\/b>.<\/span>smartbackup<\/i><\/b>.<\/span>notifications<\/i><\/b>.<\/span>websocket<\/i><\/b>.<\/span>onmessage <\/span>= <\/span>function <\/b>(evt) {<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span>var <\/b>notification <\/span>= <\/span>