Da macro a micro…tutto!
I microservizi, in pratica
In questa seconda parte dell’articolo vedremo come realizzare un’applicazione di ticketing molto semplice utilizzando l’approccio a microservizi, Node.js (utilizzeremo il framework fastify) e Docker. Nello specifico l’applicazione avrà i seguenti microservizi:
- auth: microservizio che si occuperà della gestione dell’autenticazione e della registrazione degli utenti.
- users: microservizio che si occuperà della gestione degli utenti da parte di un amministratore di sistema.
- tickets: microservizio che si occuperà della gestione dei ticket.
L’accesso ai microservizi appena descritti avverrà attraverso un’API gateway. In pratica alla fine di questo articolo avremo descritto il seguente schema architetturale:
Prima di iniziare
Assicuratevi di avere tutto ciò che serve per lo sviluppo e cioè:
- Node.js: troverete tutte le informazioni utili all’installazione ed alla configurazione per la vostra specifica piattaforma sul sito ufficiale https://nodejs.org/en.
- Docker: anche in questo caso tutte le informazioni utili al download ed all’installazione le trovate sul sito ufficiale https://www.docker.com/get-started.
- Il vostro editor javascript preferito 🤪.
- Postman per testare le rotte ed i microservizi che creeremo.
- Un terminale unix like.
- Conoscenze del framework Fastify (https://fastify.io), e ovviamente di Node.js
Creiamo la struttura del progetto
Il primo passo è quello di installare create-fastify-app (per chi non conosce questo modulo npm consiglio di leggere quest’articolo). Si tratta di un’utility che ci aiuta ad effettuare lo scaffolding di applicazioni fastify. Per installarlo sul nostro sistema basta digitare quanto segue dalla riga di comando.
$ npm install -g create-fastify-app
Ora che create-fastify-app è installata avrete a disposizione il comando fastify-app. Digitando:
$ fastify-app -h Generate Fastify projects and utilities: fastify-app [command] <options>Command
run Execute a fastify app
generate:project Generate a ready to use Fastify
generate:service Generate service skeleton source in given
add:mysql Add fastify-mysql plugin in given project
add:mongo Add fastify-mongodb plugin in given project
add:cors Add fastify-cors plugin in given project
add:redis Add fastify-redis plugin in given project
add:postgres Add fastify-postgres plugin in given project
add:pov Add Point-Of-View plugin in given projectOptions-d, — directory
Fastify project folder-h, — help
Show this help message
Bene!
Ora creiamo una directory e la chiamiamo ticketing-system. Da linea di comando entriamo in questa directory e digitiamo il seguente comando
$ fastify-app generate:project -d auth
e rispondiamo alle domande che ci vengono poste:
? Application name: auth-service
? Description: The authentication service
? Author: <Il vostro nome>
? Email: <your@email.com>
? Version: 1.0.0
? Keywords: <tag separati da virgola>
? License: MIT
? Swagger File:
? Do you want generate Dockerfile and docker-compose.yml (y/n): n
Ci siete? Perfetto!
Ora ripetiamo questo comando anche per tickets e users. Una volta finito creiamo anche un file docker-compose.yml nella root del progetto. Alla fine la struttura delle directory somiglierà a quanto segue:
ticketing-system
├── docker-compose.yml
├── auth
│ ├── help
│ ├── package.json
│ ├── src
│ └── test
├── tickets
│ ├── help
│ ├── package.json
│ ├── src
│ └── test
└── users
├── help
├── package.json
├── src
└── test
Per il momento andremo avanti creando un microservizio alla volta. Iniziamo da quello di autenticazione.
Autenticazione
Ora che lo scheletro del nostro progetto è completo, iniziamo a scrivere il codice del nostro microservizio. Prima di iniziare però dobbiamo installare i plugin fastify di cui abbiamo bisogno.
Dalla directory principale del nostro microservizo di autenticazione digitiamo quanto segue.
$ cd auth
$ npm install
$ npm install --save fastify-jwt fastify-mongodb fluent-schema
JWT
Una volta terminata l’installazione, andiamo nella directory plugins e creiamo il file jwt.js
. Quando abbiamo terminato scriviamo il seguente codice all’interno del file:
const fp = require(‘fastify-plugin’)
const JWT = require(‘fastify-jwt’)module.exports = fp(async (fastify, opts) => {
let jwtOpts = Object.assign({}, {
secret: process.env.JWT_SECRET || ‘Th1s1sY0ur53cr3t’
}, opts.jwt) fastify.register(JWT, jwtOpts)
})
Per queste linee di codice utilizziamo fastify-jwt
che decora la nostra istanza fastify con i metodi standard di jsonwebtokens ossia decode, sign, e verify. Inoltre saranno disponibili anche i metodi request.jwtVerify
e reply.jwtSign
, e vedremo fra non molto come utilizzarli.
MongoDB
Ora che abbiamo configurato il plugin jwt facciamo lo stesso con quello MongoDB, quindi creiamo un file mongo.db.js
all’interno della directory plugins e scriviamo il seguente codice al suo interno:
const fp = require('fastify-plugin')
const isDocker = require('is-docker')
const MongoDB = require('fastify-mongodb')module.exports = fp(async (fastify, opts) => {
let mongoOpts = Object.assign({}, {
useNewUrlParser: true,
useUnifiedTopology: true,
url:`mongodb://${isDocker() ? 'mongodb-web' : 'localhost'}:27017/wetalk`,
}, opts.mongodb) fastify.register(MongoDB, mongoOpts)
})
Anche in questo caso utilizziamo un plugin sviluppato dal core team di fastify, ossiafastify-mongodb
, che decora il nostro microservizio aggiungendogli la possibilità di connettersi ad un istanza MongoDB. Un’ulteriore cosa che faremo ora è aggiornare il file docker-compose.yml
nella directory principale del progetto:
---
version: '3.6'
services:
mongodb-web:
image: mongo:latest
container_name: mongodb-web
ports:
- "27017:27017"
expose:
- "27017"
Adesso dovrebbe essere anche più chiara la configurazione del plugin MongoDB fatta in precedenza, ossia la riga:
`mongodb://${isDocker() ? 'mongodb-web' : 'localhost'}:27017/wetalk`
Per ora fermiamoci qui con il docker-compose.yml
. Ci torneremo fra poco, quando dovremo configurare anche il microservizio auth.
Implementiamo il servizio
Ora che abbiamo tutti gli elementi di contorno configurati, implementiamo il cuore del nostro microservizio. Per farlo ci spostiamo nella directory services e creiamo il file auth.js
e iniziamo a definire quanto segue:
const securePassword = require('secure-password')
const S = require('fluent-schema')const DUPLICATE_KEY_ERROR = 11000module.exports = async function (fastify, opts) {
const users = fastify.mongo.db.collection('users')
const pwd = securePassword() users.createIndex({
username: 1
}, { unique: true }) fastify.post('/signup', {
schema: {
body: S.object()
.prop('username', S.string().maxLength(10).required())
.prop('password', S.string().required())
.prop('fullName', S.string().maxLength(50).required()),
response: {
200: S.object()
.prop('token', S.string()),
400: S.object()
.prop('message', S.string())
}
}
}, async function (request, reply) {
const { fullName, username, password } = request.body
const hashedPassword = await pwd.hash(Buffer.from(password)) try {
await users.insertOne({
'fullName': fullName,
'username': username,
'password': hashedPassword
})
} catch (err) {
// duplicate key error
if (err.code === DUPLICATE_KEY_ERROR) {
return reply
.code(400)
.send({ message: 'username already registered' })
}
} const token = await reply.jwtSign({
username: username,
fullName: user.fullName
}) return { token: token }
}) fastify.post('/signin', {
schema: {
body: S.object()
.prop('username', S.string().required())
.prop('password', S.string().required()),
response: {
200: S.object()
.prop('token', S.string()),
404: S.object()
.prop('message', S.string()),
400: S.object()
.prop('message', S.string())
}
}
}, async function (request, reply) {
const { username, password } = request.body
const user = await users.findOne({ username: username }) if (!user) {
reply
.code(404)
.send({ message: 'username not found' })
return
}
const res = await pwd.verify(Buffer.from(password), user.password.buffer) switch (res) {
case securePassword.INVALID_UNRECOGNIZED_HASH:
reply.code(400)
.send({ message: 'This hash was not made with secure-password. Attempt legacy algorithm' })
return
case securePassword.INVALID:
reply.code(400)
.send({ message: 'Invalid password' })
return case securePassword.VALID_NEEDS_REHASH:
const hashedPassword = await pwd.hash(Buffer.from(password))
await users.update({ _id: user._id }, { hashedPassword })
break
} const token = await reply.jwtSign({
username: username,
fullName: user.fullName
}) return { token: token }
})
}
In auth.js
abbiamo definito due rotte:
POST /signup
: che consente la registrazione di un utentePOST /signin
: che consente, invece ad un utente di effettuare il login
Entrambe le rotte hanno uno schema ben definito. Questo perché Fastify utilizza un approccio basato su schema e, anche se non è obbligatorio, io consiglio vivamente di utilizzare JSON Schema per validare le nostre rotte e serializzare i risultati. Internamente, Fastify compila lo schema in una funzione altamente performante.
Sembra inutile commentare l’implementazione dei metodi. Anche perché se volessi farlo per tutti i microservizi mi dilungherei troppo e non è il caso 🤣. Dico solo che in entrambi i casi, nel momento in cui l’operazione di autenticazione o registrazione è andata a buon fine. Il microservizio restituisce un token che utilizzeremo per fare le invocazioni ad i prossimi servizi che creeremo.
Aggiorniamo il docker-compose
Ora che abbiamo definito la struttura - e lo schema - del nostro microservizio di autenticazione, aggiorniamo il docker-compose.ym
. Prima però creiamo un Dockerfile
all’interno della directory auth
e copiamo quanto segue al suo interno:
FROM node:12WORKDIR /usr/src/app
COPY package*.json ./RUN npm install --productionCOPY . /usr/src/app
EXPOSE 3001RUN npm -g install create-fastify-app
CMD [ "npm", "run", "dev" ]
Una volta che abbiamo finito aggiorniamo il nostro docker-compose.yml
con le seguenti informazioni:
---
version: '3.6'
services:
mongodb-web:
image: mongo:latest
container_name: mongodb-web
ports:
- "27017:27017"
expose:
- "27017"
auth-service:
container_name: auth-service
restart: always
build: ./auth
command: ["npm", "run", "dev"]
ports:
- "3001:3001"
expose:
- "3001"
environment:
- FASTIFY_PORT=3001
volumes:
- ./auth/:/usr/src/app
- /usr/src/app/node_modules
links:
- mongodb-web
Una volta terminato dirigiamoci nella directory principale del progetto e digitiamo:
$ docker-compose up -d
Se tutto è andato a buon fine, avrete a disposizione il servizio di autenticazione che abbiamo creato all’indirizzo http://localhost:3001
. Ora non vi resta di verificare con Postman che tutto funzioni.
La gestione dei Tickets
Ora che abbiamo terminato la nostra autenticazione passiamo all’implementazione del microservizio che si occuperà di gestire i ticket aperti da un utente.
Dalla directory principale del nostro microservizo di autenticazione digitiamo quanto segue.
$ cd auth
$ npm install
$ npm install --save fastify-jwt fastify-mongodb fluent-schema
JWT
L’implementazione è del tutto identica a quella utilizzata nell’esempio precedente. Andiamo nella directory plugins
, creiamo il file mongo.db.js
e copiamo quanto già fatto per il microservizio di autenticazione. L’unica cosa da fare è modificare il file hooks/preHandler.js
in modo che abbia il seguente aspetto:
'use strict'const fp = require('fastify-plugin')module.exports = fp(async (fastify, opts) => {
fastify.addHook('preHandler', async (request, reply) => {
return request.jwtVerify()
})
})
Questo pezzetto di codice farà in modo di controllare sostanzialmente le seguenti cose:
- Se la richiesta che stiamo effettuando al microservizio ha un token nell’header della richiesta.
- Se il token presente nell’header della richiesta è valido.
Nel caso in cui questi controlli vadano a buon fine. Nel vostro handler della richiesta, ovvero la funzione che gestisce effettivamente la richiesta, potrete accedere ai dati utente attraverso request.user
. Siccome nella definizione della rotta abbiamo salvato nel nostro token di autenticazione le proprietà username
e fullName
, queste saranno accessibili ed eventualmente utilizzate dai vostri handler.
MongoDB
Anche in questo caso il codice è del tutto identico al microservizio di autenticazione.
Implementiamo il servizio
Ora che abbiamo installato e configurato in nostri plugins passiamo all’implementazione. I metodi che implementeremo in questa gestione ticket saranno i seguenti:
GET /api/ticket
: ritornerà la lista di tutti i miei ticketPOST /api/ticket
: si occuperà di ricevere i dati relativi al ticket e di salvarli nella collection tickets del nostro database.DELETE /api/ticket/:id
: anche se non si dovrebbe 😅, questo metodo si occuperà di eliminare il ticket con l’id ricevuto come parametro.GET /api/ticket/:id
: infine questo ticket si occuperà di ritornare i dati del ticket che ha l’id inviato come parametro nella richiesta.
Ora immaginando che un ticket abbia il seguente schema:
S.object()
.prop('_id', S.string())
.prop('subject', S.string())
.prop('body', S.string())
.prop('username', S.string())
.prop('creation-date', S.string().format('date-time'))
Scriviamo il codice del nostro microservizio nel file services/ticket.js
:
'use strict'const S = require('fluent-schema')module.exports = async function(fastify, opts) { const ticketsCollection = fastify.mongo.db.collection('tickets')
const { ObjectId } = fastify.mongo fastify.delete('/:id', {
schema: {...}
}, async function (request, reply) {
const { id } = request.params;
const result = await ticketsCollection.deleteOne({
_id: new ObjectId(id),
username: request.user.username
}) if (result.deletedCount === 0) {
reply.code(404)
return { message: 'No ticket found' }
} return { message: `Ticket ${id} deleted!` }
}) fastify.get('/', {
schema: {...}
}, async function (request, reply) {
const tickets = await ticketsCollection.find({
username: request.user.username
}).sort({
_id: -1 // new tickets first
}).toArray()
return tickets
}) fastify.get('/:id', {
schema: {...}
}, async function (request, reply) {
const { id } = request.params;
const ticket = await ticketsCollection.findOne({
_id: new ObjectId(id),
username: request.user.username
}) if (!ticket) {
reply.code(404)
return { message: 'No ticket found' }
} return ticket
}) fastify.post('/', {
schema: {...}
}, async function (request, reply) {
const ticket = request.body
Object.assign(ticket, {
username: request.user.username
}) const data = await ticketsCollection.insertOne(ticket)
const _id = data.ops[0]._id return Object.assign({
_id
}, ticket)
})
}module.exports.autoPrefix = '/api/ticket'
Per una questione anche di leggibilità, noterete che in questo caso ho omesso gli schema per proteggere le nostre rotte. Troverete tutto il codice necessario al link del progetto che vi condivido alla fine dell’articolo.
Aggiorniamo il docker-compose
Ora, così come abbiamo fatto per il nostro microservizio di autenticazione, aggiorniamo il docker-compose.yml
, prima di farlo però creiamo un Dockerfile
all’interno della directory tickets
e copiate quanto segue al suo interno:
FROM node:12WORKDIR /usr/src/app
COPY package*.json ./RUN npm install --productionCOPY . /usr/src/app
EXPOSE 3002RUN npm -g install create-fastify-app
CMD [ "npm", "run", "dev" ]
Noterete che rispetto al microservizio precedente è stata cambiata solo la porta esposta. Una volta finito aggiorniamo il nostro docker-compose.yml
con le seguenti informazioni:
---
version: '3.6'
services:
mongodb-web:
...
auth-service:
... ticket-service:
container_name: ticket-service
restart: always
build: ./tickets
command: ["npm", "run", "dev"]
ports:
- "3002:3002"
expose:
- "3002"
environment:
- FASTIFY_PORT=3002
volumes:
- ./tickets/:/usr/src/app
- /usr/src/app/node_modules
links:
- mongodb-web
Ora che abbiamo finito torniamo nella directory principale del progetto e digitiamo:
$ docker-compose up -d
Se tutto è andato bene, avremo il microservizo di autenticazione che risponde all’indirizzo http://localhost:3001
e quello della gestione tickets all’indirizzo http://localhost:3002/api/tickets
. Ovviamente, quando effettuerete i test con Postman, assicuratevi di invocare il microservizio dei tickets aggiungendo nell’header delle richieste il token ricevuto dal microservizio di autenticazione.
La gestione utenti
La gestione utenti sarà del tutto simile a quella dei ticket. Quindi un semplice CRUD (Create, Read, Update, Delete) che gestisce le utenze. Non presenterò il codice sorgente del microservizio, perchè rischierei di essere solo ripetitivo e noioso. Quello che c’è da sapere è che alla fine dell’implementazione del microservizio avrete all’interno della directory users
il seguente Dockerfile
FROM node:12WORKDIR /usr/src/app
COPY package*.json ./RUN npm install --productionCOPY . /usr/src/app
EXPOSE 3003RUN npm -g install create-fastify-app
CMD [ "npm", "run", "dev" ]
e il docker-compose.yml
avrà il seguente aspetto:
---
version: '3.6'
services:
mongodb-web:
...
auth-service:
... tickets-service:
... users-service:
container_name: users-service
restart: always
build: ./users
command: ["npm", "run", "dev"]
ports:
- "3003:3003"
expose:
- "3003"
environment:
- FASTIFY_PORT=3003
volumes:
- ./users/:/usr/src/app
- /usr/src/app/node_modules
links:
- mongodb-web
API Gateway
Ok, ora che abbiamo terminato l’implementazione dei nostri microservizi avremo tre container docker che espongono i loro servizi.
Sembra tutto ok! Perfetto abbiamo finito!
Nel caso in cui vogliamo creare un’applicazione che invoca i nostri microservizi dobbiamo solo ricordarci che:
http://localhost:3001
: a questo endpoint risponderà il microservizio di registrazione ed autenticazione.http://localhost:3002/api/ticket
: qui invece risponderà il microservizio che gestisce i tickets.http://localhost:3003/api/user
: qui risponderanno gli endpoints del microservizio di gestione utenti.
A tal proposito ripropongo la stessa GIF del precedente articolo:
Non sembra una buona soluzione vero? Dobbiamo rendere l’infrastruttura invisibile a chi la utilizza. Quindi quello che faremo ora è implementare un API Gateway utilizzando fastify-http-proxy
. In questo modo l’utente si dovrà preoccupare di invocare solo un endpoint, in tutta l’applicazione. Il primo passo da compiere è quello di creare un nuovo progetto con CFA nella directory principale del progetto in modo che la struttura finale del progetto sia la seguente:
ticketing-system
├── docker-compose.yml
├── auth
├── gateway
├── tickets
└── users
Una volta creato il progetto entrate nella directory gateway ed installate il seguente modulo:
$ npm install --save fastify-http-proxy
Questo plugin inoltra tutte le richieste ricevute con un determinato prefisso (o nessuno) a un upstream. Tutti gli Hooks di fastify vengono applicati.
Una volta installato create tre file all’interno della directory service.
Il primo è auth.js
:
// file auth.js
const proxy = require('fastify-http-proxy')module.exports = async function (fastify, opts) {
fastify.register(proxy, {
upstream: 'http://auth-service:3001'
})
}
Il file tickets.js
const proxy = require('fastify-http-proxy')module.exports = async function (fastify, opts) {
fastify.register(proxy, {
upstream: 'http://tickets-service:3002'
})
}module.exports.autoPrefix = '/api/ticket'
ed infine Il file users.js
const proxy = require('fastify-http-proxy')module.exports = async function (fastify, opts) {
fastify.register(proxy, {
upstream: 'http://users-service:3003'
})
}module.exports.autoPrefix = '/api/user'
Fatto ciò creiamo il Dockerfile per il nostro gateway:
FROM node:12WORKDIR /usr/src/app
COPY package*.json ./RUN npm install --productionCOPY . /usr/src/app
EXPOSE 3000RUN npm -g install create-fastify-app
CMD [ "npm", "run", "dev" ]
e aggiorniamo il nostro docker-compose.yml
nel seguente modo:
version: '3.6'
services:
mongodb-web:
... gateway-service:
container_name: users-service
restart: gateway
build: ./users
command: ["npm", "run", "dev"]
ports:
- "3000:3000"
expose:
- "3000"
environment:
- FASTIFY_PORT=3000
volumes:
- ./gateway/:/usr/src/app
- /usr/src/app/node_modules
links:
- auth-service
- tickets-service
- users-service auth-service:
... tickets-service:
... users-service:
...
Ricordatevi di rimuovere la sezione expose a tutti gli altri microservizi. Questo è importante perché se in precedenza ogni microservizo esponeva una sua porta per consentirci di invocarli, ora tutto il traffico passerà dal servizio gateway, quindi l’unico microservizio che esporrà una porta sarà lui. Questo è anche il motivo per cui negli endpoint degli upstream non abbiamo utilizzato localhost
ma il relativo nome del container utilizzati nel docker-compose.yml
.
Ora, se tutto è andato come doveva andare, invocando il comando:
$ docker-compose up -d
sulla porta 3000 risponderà il nostro gateway che inoltrerà tutte le richieste che effettuerete al microservizio di riferimento restituendovi le relative risposte.
Monitoring
Ora che abbiamo terminato l’implementazione dei nostri microservizi, come facciamo a monitorare eventuali errori? E se dovessimo individuare un eventuale collo di bottiglia?
Elastic APM
Elastic APM è un sistema di monitoraggio delle prestazioni dell’applicazione basato sullo stack elastic. Consente di monitorare i servizi software e le applicazioni in tempo reale, raccogliendo informazioni dettagliate sulle prestazioni sui tempi di risposta delle richieste in arrivo, query al database, chiamate alla cache, richieste HTTP esterne e molto altro. Ciò semplifica l’individuazione e la risoluzione rapida dei problemi di prestazioni.
Elastic APM raccoglie anche automaticamente errori ed eccezioni non gestite. Gli errori vengono raggruppati principalmente in base allo stacktrace, quindi è possibile identificare nuovi errori man mano che si verificano e tenere d’occhio per quante volte.
Le metriche sono un’altra importante fonte di informazioni durante il debug dei sistemi di produzione. Gli agenti elastic APM raccolgono automaticamente le metriche di base a livello di host e le metriche specifiche dell’agent, come le metriche di runtime di Node.js.
Installazione e configurazione
Ora che abbiamo identificato come monitorare i nostri microservizi dobbiamo effettuare ancora qualche modifica al nostro progetto.
La prima cosa da fare è quella di modificare il nostro file docker-compose.yml
in modo che assuma il seguente aspetto:
version: '3.6'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.6.0
container_name: elasticsearch-web
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms1g -Xmx1g
ulimits:
memlock:
soft: -1
hard: -1
nofile:
soft: 65536
hard: 65536
ports:
- "9200:9200"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health | grep -v '\"status\":\"red\"'"] kibana:
image: docker.elastic.co/kibana/kibana:7.6.0
container_name: kibana-web
environment:
ELASTICSEARCH_URL: http://elasticsearch:9200
ports:
- "5601:5601"
healthcheck:
test: ["CMD", "curl", "-f", "http://kibana:5601/"]
depends_on:
- elasticsearch apm-server:
image: docker.elastic.co/apm/apm-server:7.6.0
container_name: apm-server
ports:
- "8200:8200"
environment:
- output.elasticsearch.hosts=['http://elasticsearch:9200']
- apm-server.host="0.0.0.0:8200"
- setup.kibana.host="kibana:5601"
command: apm-server -e -d "*" -E apm-server.host="0.0.0.0:8200" -E apm-server.expvar.enabled=true depends_on:
- elasticsearch
- kibana
healthcheck:
test: ["CMD", "curl", "-f" ,"http://apm-server:8200/"] mongodb-web:
... gateway-service:
...
links:
- apm-server
- auth-service
- tickets-service
- users-service auth-service:
...
links:
- apm-server
- mongodb-web tickets-service:
...
links:
- apm-server
- mongodb-web users-service:
...
links:
- apm-server
- mongodb-web
Modifichiamo i microservizi
Una volta modificato il docker-compose.yml
passiamo alla modifica dei singoli microservizi. La prima cosa da fare è installare una nuova dipendenza e cioè:
$ npm install --save elastic-apm-node
Fatto ciò creiamo un file apm.js
nella directory base di ogni microservizio. Il suo contenuto è sempre lo stesso:
const apm = require('elastic-apm-node')apm.start({
serverUrl: `http://apm-server:8200`
})
Adesso dobbiamo solo modificare il package.json
:
...
"scripts": {
...
"dev": "NODE_OPTIONS=\"-r ./apm.js\" fastify-app run -l info"
},
...
Perfetto! Ora dobbiamo solo dirigerci nella directory base del progetto e digitare:
$ docker-compose up
vedrete che verranno installate tutte le dipendenze che mancavano e che salirà l’intera infrastruttura che abbiamo creato.
Ora, se tutto è andato bene, date uno sguardo all’url http://localhost:5601/app/apm e vi ritroverete davanti qualcosa de tipo:
e se provate a curiosare all’interno di uno specifico microservizio, per esempio tickets, dopo aver utilizzato un po’ l’API:
Interessante no? Avrete a disposizione tutta una serie di informazioni utili, come, tempi medi di risposta, errori e tanto altro ancora. Lascio a voi il compito di curiosare etc. etc. etc.
Concludendo
In questo articolo abbiamo visto come implementare un sistema orientato ai microservizi con Node.js (Fastify), Docker e MongoDB. Ovviamente l’esempio implementato e descritto in questo articolo lo troverete al seguente indirizzo:
https://github.com/davidedantonio/from-macro-micro-microservices
Ora, cosa aspettate? Tocca a voi quindi… Happy Coding!