1. Présentation et objectifs
Le but est de continuer le développement de notre architecture "à la microservice".
Nous allons utiliser Apache Pulsar, pour implémenter un début d’architecture EDA entre les micro-services game et mailing.

2. Pré-requis
En pré-requis à ce TP, il faut avoir implémenté la partie 6. Envoi d’emails.
3. mailing-api
3.1. Démarrage d’une instance locale de pulsar
Apache Pulsar est disponible sous la forme d’une image Docker : apachepulsar/pulsar.
Démarrez une instance de pulsar sur votre poste avec la commande suivante :
docker container run -d \
--name pulsar \
-p 6650:6650 \
-p 6080:8080 \
apachepulsar/pulsar:4.0.1 \
bin/pulsar standalone
3.2. Configuration Spring Boot
Dans votre projet mailing
, importez la dépendance spring-boot-starter-pulsar
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-pulsar</artifactId>
</dependency>
Configurez les properties suivantes dans un fichier application-local.properties
, pour utiliser votre instance locale :
spring.pulsar.client.service-url=pulsar://localhost:6650
spring.pulsar.admin.service-url=http://localhost:6080
3.3. Ajout d’un listener
Créez une classe ou un record qui représentera votre évènement trainer:new
.
Cette classe contient l’adresse mail du trainer, ainsi que son nom.
Annotez cette classe avec l’annotation @PulsarMessage(schemaType = SchemaType.JSON)
pour indiquer à Spring Boot que cet objet devra être sérialisé en JSON.
Implémentez une classe NewTrainerEventPulsarAdapter
.
L’annotation @PulsarListener
permet de marquer une méthode Java comme devant être appelée lorsqu’un message est disponible dans un topic Pulsar.
Ajoutez une méthode à cette classe, qui porte l’annotation, et reçoit en paramètre votre classe d’évènement :
@PulsarListener(topics = "trainer:new", subscriptionType = SubscriptionType.Shared)
void receiveEvent(NewTrainerEvent event) {
// TODO
}
Cette méthode écoute sur un topic <votre-nom>:trainer:new
.
On utilise un nommage de topic trainer:new avec votre nom en préfixe, pour éviter les conflits entre les étudiants !
|
Le paramètre SubscriptionType.Shared
permet de partager la connexion au topic entre plusieurs instances de votre micro-service !
Cette méthode pourra appeler la méthode de service existant par ailleurs.
Le démarrage de votre application devrait afficher les logs de connexion au topic pulsar :
o.a.p.c.impl.ConsumerStatsRecorderImpl : Starting Pulsar consumer status recorder with config: {"topicNames":["persistent://public/default/new-trainer"],"topicsPattern":null,"subscriptionName":"org.springframework.Pulsar.PulsarListenerEndpointContainer#0","subscriptionType":"Shared","subscriptionProperties":null,"subscriptionMode":"Durable","receiverQueueSize":1000,"acknowledgementsGroupTimeMicros":100000,"maxAcknowledgmentGroupSize":1000,"negativeAckRedeliveryDelayMicros":60000000,"maxTotalReceiverQueueSizeAcrossPartitions":50000,"consumerName":null,"ackTimeoutMillis":0,"tickDurationMillis":1000,"priorityLevel":0,"maxPendingChunkedMessage":10,"autoAckOldestChunkedMessageOnQueueFull":false,"expireTimeOfIncompleteChunkedMessageMillis":60000,"cryptoFailureAction":"FAIL","properties":{},"readCompacted":false,"subscriptionInitialPosition":"Latest","patternAutoDiscoveryPeriod":60,"regexSubscriptionMode":"PersistentOnly","deadLetterPolicy":null,"retryEnable":false,"autoUpdatePartitions":true,"autoUpdatePartitionsIntervalSeconds":60,"replicateSubscriptionState":false,"resetIncludeHead":false,"batchIndexAckEnabled":false,"ackReceiptEnabled":false,"poolMessages":false,"startPaused":false,"autoScaledReceiverQueueSizeEnabled":false,"topicConfigurations":[],"maxPendingChuckedMessage":10}
o.a.p.c.impl.ConsumerStatsRecorderImpl : Pulsar client config: {"serviceUrl":"pulsar://localhost:6650","authPluginClassName":null,"authParams":null,"authParamMap":null,"operationTimeoutMs":30000,"lookupTimeoutMs":30000,"statsIntervalSeconds":60,"numIoThreads":22,"numListenerThreads":22,"connectionsPerBroker":1,"connectionMaxIdleSeconds":25,"useTcpNoDelay":true,"useTls":false,"tlsKeyFilePath":null,"tlsCertificateFilePath":null,"tlsTrustCertsFilePath":null,"tlsAllowInsecureConnection":false,"tlsHostnameVerificationEnable":false,"concurrentLookupRequest":5000,"maxLookupRequest":50000,"maxLookupRedirects":20,"maxNumberOfRejectedRequestPerConnection":50,"keepAliveIntervalSeconds":30,"connectionTimeoutMs":10000,"requestTimeoutMs":60000,"readTimeoutMs":60000,"autoCertRefreshSeconds":300,"initialBackoffIntervalNanos":100000000,"maxBackoffIntervalNanos":60000000000,"enableBusyWait":false,"listenerName":null,"useKeyStoreTls":false,"sslProvider":null,"tlsKeyStoreType":"JKS","tlsKeyStorePath":null,"tlsKeyStorePassword":null,"tlsTrustStoreType":"JKS","tlsTrustStorePath":null,"tlsTrustStorePassword":null,"tlsCiphers":[],"tlsProtocols":[],"memoryLimitBytes":67108864,"proxyServiceUrl":null,"proxyProtocol":null,"enableTransaction":false,"dnsLookupBindAddress":null,"dnsLookupBindPort":0,"dnsServerAddresses":[],"socks5ProxyAddress":null,"socks5ProxyUsername":null,"socks5ProxyPassword":null,"description":null,"openTelemetry":null}
o.a.pulsar.client.impl.ConsumerImpl : [persistent://public/default/trainer:new][org.springframework.Pulsar.PulsarListenerEndpointContainer#0] Subscribing to topic on cnx [id: 0x58864231, L:/127.0.0.1:38618 - R:localhost/127.0.0.1:6650], consumerId 0
o.a.pulsar.client.impl.ConsumerImpl : [persistent://public/default/trainer:new][org.springframework.Pulsar.PulsarListenerEndpointContainer#0] Subscribed to topic on localhost/127.0.0.1:6650 -- consumer: 0
Implémentez toutes les méthodes disponibles sur le mailing service avec des listener Pulsar :
-
envoi d’un email de bienvenue (reçoit le nom d’un Trainer en paramètre, ainsi que son email)
-
envoi d’un email de fin de combat (reçoit les noms des 2 adversaires, leurs emails, et l’information de qui a gagné le combat)
4. trainer-api
4.1. Configuration
Ajoutez la dépendance spring-boot-starter-pulsar
à votre projet trainer-ui
.
Configurez les properties suivantes dans un fichier application-local.properties
, pour utiliser votre instance locale :
spring.pulsar.client.service-url=pulsar://localhost:6650
spring.pulsar.admin.service-url=http://localhost:6080
4.2. Ajout d’un producer
Spring propose un PulsarTemplate
pour poster des messages dans un topic.
Dans une classe de game-ui
, nommée UserEventsPulsarAdapter
, utilisez le PulsarTemplate
(reçu en injection de dépendance) pour envoyer les évènements à destination du mailing-api
dans le topic consacré <votre-nom>:trainer:new
.
À titre d’exemple, voici un usage du PulsarTemplate
pour envoyer un message sur un topic :
pulsarTemplate.send(
"trainer:new",
new NewTrainerEvent("Ash", "ash@gitlab-classrooms.org")
);
N’hésitez pas à copier/coller votre classe correspondant à l’évènement attendu depuis mailing-api .
|
Isolez toutes ces classes dans un package consacré. Vous pouvez aussi exposer une interface UserEventsPort dans votre couche "domaine" si elle existe, pour reprendre les principes de l’architecture hexagonale.
|
5. battle-api
Dans battle-api
, implémentez l’envoi d’un message lors de la fin d’un combat, à destination du mailing-api
dans le topic consacré <votre-nom>:battle:end
, de la même manière que cela a été fait entre game-ui
et mailing-api
.
6. Configuration pour Clever Cloud
La configuration de l’utilisation de Pulsar sur Clever Cloud nécessite quelques ajustements :
-
l’instance de Pulsar est partagée entre tous les étudiants
-
la connexion à Pulsar nécessite une authentification
-
les topics peuvent être créés dans un tenant/namespace unique
Toutes les informations nécessaires à la connexion à Pulsar sont disponibles à 2 endroits :
-
dans la console Clever Cloud.
-
dans un secret sur Vault.


Configurez dans vos projets mailing-api
, game-ui
et battle-api
l’utilisation de l’instance Pulsar de Clever Cloud :
spring.pulsar.client.service-url= (1)
spring.pulsar.client.authentication.plugin-class-name=org.apache.pulsar.client.impl.auth.AuthenticationToken
spring.pulsar.client.authentication.param.token= (2)
spring.pulsar.admin.service-url= (3)
spring.pulsar.admin.authentication.plugin-class-name=org.apache.pulsar.client.impl.auth.AuthenticationToken
spring.pulsar.admin.authentication.param.token= (2)
spring.pulsar.defaults.topic.tenant= (4)
spring.pulsar.defaults.topic.namespace= (5)
-
Utilisez la valeur de
ADDON_PULSAR_BINARY_URL
-
Utilisez la valeur de
ADDON_PULSAR_TOKEN
-
Utilisez la valeur de
ADDON_PULSAR_HTTP_URL
-
Utilisez la valeur de
ADDON_PULSAR_TENANT
-
Utilisez la valeur de
ADDON_PULSAR_NAMESPACE