Add the message producers and listeners
You will next add the code required to send and receive messages to the visits
service. The message-emulator
will send a PetClinicMessageRequest
to the visits-requests
queue. The visits
service will need to listen to this queue and each time a VisitRequest
message is submitted, it will create a new Visit
for the pet ID referenced in the message. The visits
service will also send back a VisitResponse
as a confirmation to the visits-confirmations
queue. This is the queue the message-emulator
is listening to.
Step by step guidance
-
In the
spring-petclinic-visits-service
directory, create a newsrc/main/java/org/springframework/samples/petclinic/visits/entities
subdirectory and add aVisitRequest.java
class file containing the following code:package org.springframework.samples.petclinic.visits.entities; import java.io.Serializable; import java.util.Date; public class VisitRequest implements Serializable { private static final long serialVersionUID = -249974321255677286L; private Integer requestId; private Integer petId; private String message; public VisitRequest() { } public Integer getRequestId() { return requestId; } public void setRequestId(Integer id) { this.requestId = id; } public Integer getPetId() { return petId; } public void setPetId(Integer petId) { this.petId = petId; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
-
In the same directory, add a
VisitResponse.java
class containing the following code:package org.springframework.samples.petclinic.visits.entities; public class VisitResponse { Integer requestId; Boolean confirmed; String reason; public VisitResponse() { } public VisitResponse(Integer requestId, Boolean confirmed, String reason) { this.requestId = requestId; this.confirmed = confirmed; this.reason = reason; } public Boolean getConfirmed() { return confirmed; } public void setConfirmed(Boolean confirmed) { this.confirmed = confirmed; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public Integer getRequestId() { return requestId; } public void setRequestId(Integer requestId) { this.requestId = requestId; } }
-
In the
spring-petclinic-visits-service
directory, create a newsrc/main/java/org/springframework/samples/petclinic/visits/config
subdirectory and add aMessagingConfig.java
class file containing the following code:package org.springframework.samples.petclinic.visits.config; import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.support.converter.MappingJackson2MessageConverter; import org.springframework.jms.support.converter.MessageConverter; import org.springframework.samples.petclinic.visits.entities.VisitRequest; import org.springframework.samples.petclinic.visits.entities.VisitResponse; @Configuration public class MessagingConfig { @Bean public MessageConverter jackson2Converter() { MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); Map<String, Class<?>> typeMappings = new HashMap<String, Class<?>>(); typeMappings.put("visitRequest", VisitRequest.class); typeMappings.put("visitResponse", VisitResponse.class); converter.setTypeIdMappings(typeMappings); converter.setTypeIdPropertyName("messageType"); return converter; } }
-
In the same directory, add a
QueueConfig.java
class file containing the following code:package org.springframework.samples.petclinic.visits.config; import org.springframework.beans.factory.annotation.Value; public class QueueConfig { @Value("${spring.jms.queue.visits-requests:visits-requests}") private String visitsRequestsQueue; public String getVisitsRequestsQueue() { return visitsRequestsQueue; } }
-
In the
spring-petclinic-visits-service
directory, create a newsrc/main/java/org/springframework/samples/petclinic/visits/service
subdirectory and add aVisitsReceiver.java
class file containing the following code:package org.springframework.samples.petclinic.visits.service; import java.util.Date; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.jms.annotation.JmsListener; import org.springframework.jms.core.JmsTemplate; import org.springframework.samples.petclinic.visits.entities.VisitRequest; import org.springframework.samples.petclinic.visits.entities.VisitResponse; import org.springframework.samples.petclinic.visits.model.Visit; import org.springframework.samples.petclinic.visits.model.VisitRepository; import org.springframework.stereotype.Component; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Component @Slf4j @RequiredArgsConstructor public class VisitsReceiver { private final VisitRepository visitsRepository; private final JmsTemplate jmsTemplate; @JmsListener(destination = "visits-requests") void receiveVisitRequests(VisitRequest visitRequest) { log.info("Received message: {}", visitRequest.getMessage()); try { Visit visit = new Visit(null, new Date(), visitRequest.getMessage(), visitRequest.getPetId()); visitsRepository.save(visit); jmsTemplate.convertAndSend("visits-confirmations", new VisitResponse(visitRequest.getRequestId(), true, "Your visit request has been accepted")); } catch (Exception ex) { log.error("Error saving visit: {}", ex.getMessage()); jmsTemplate.convertAndSend("visits-confirmations", new VisitResponse(visitRequest.getRequestId(), false, ex.getMessage())); } } }
This
VisitsReceiver
service is listening to thevisits-requests
queue. Each time a message is present on the queue, it will dequeue this message and save a newVisit
in the database. In the next step, you will verify it by having it sent a confirmation message to thevisits-confirmations
queue. -
Rebuild the spring-petclinic-visits-service microservice
cd .. mvn clean package -DskipTests -rf :spring-petclinic-visits-service
-
Navigate to the
staging-acr
directory, copy the jar file of the visit-service and rebuild the container.cd staging-acr rm spring-petclinic-visits-service-$VERSION.jar cp ../spring-petclinic-visits-service/target/spring-petclinic-visits-service-$VERSION.jar spring-petclinic-visits-service-$VERSION.jar docker build -t $MYACR.azurecr.io/spring-petclinic-visits-service:$VERSION \ --build-arg ARTIFACT_NAME=spring-petclinic-visits-service-$VERSION.jar \ --build-arg APP_PORT=8080 \ --build-arg AI_JAR=ai.jar \ . docker push $MYACR.azurecr.io/spring-petclinic-visits-service:$VERSION
-
Navigate to the kubernetes folder and update the
spring-petclinic-visits-service.yml
file so it also contains an environment variable for theSPRING_JMS_SERVICEBUS_CONNECTIONSTRING
. Add the below at the bottom of the existing environment variables below line 39 and before theimagePullPolicy
.- name: SPRING_JMS_SERVICEBUS_CONNECTIONSTRING valueFrom: secretKeyRef: name: sbsecret key: sbconn
-
In the same file, below the environment variable you just added and again right abobe the
imagePullPolicy
, add avolumeMounts
section.volumeMounts: - name: secrets-store01-inline mountPath: "/mnt/secrets-store" readOnly: true
-
In the same file, below the
serviceAccountName: workload-identity-sa
on line 18, add avolumes
section:volumes: - name: secrets-store01-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "azure-kvname-user-msi"
The resulting spring-petclinic-visits-service.yml file should look like this. Also double check that the secretKeyRef name and key of the existing environment variables and the volumes and volumeMounts names, are the same as what you set previously when you configured Key Vault integration.
apiVersion: apps/v1 kind: Deployment metadata: labels: app: visits-service name: visits-service spec: replicas: 1 selector: matchLabels: app: visits-service template: metadata: labels: app: visits-service azure.workload.identity/use: "true" spec: serviceAccountName: workload-identity-sa volumes: - name: secrets-store01-inline csi: driver: secrets-store.csi.k8s.io readOnly: true volumeAttributes: secretProviderClass: "azure-kvname-user-msi" containers: - image: <your-registry-name>.azurecr.io/spring-petclinic-visits-service:2.7.6 name: visits-service env: - name: "CONFIG_SERVER_URL" valueFrom: configMapKeyRef: name: config-server key: CONFIG_SERVER_URL - name: "APPLICATIONINSIGHTS_CONNECTION_STRING" valueFrom: configMapKeyRef: name: config-server key: APPLICATIONINSIGHTS_CONNECTION_STRING - name: "APPLICATIONINSIGHTS_CONFIGURATION_CONTENT" value: >- { "role": { "name": "visits-service" } } - name: SPRING_JMS_SERVICEBUS_CONNECTIONSTRING valueFrom: secretKeyRef: name: sbsecret key: sbconn volumeMounts: - name: secrets-store01-inline mountPath: "/mnt/secrets-store" readOnly: true imagePullPolicy: Always livenessProbe: failureThreshold: 3 httpGet: path: /actuator/health port: 8080 scheme: HTTP initialDelaySeconds: 180 successThreshold: 1 readinessProbe: failureThreshold: 3 httpGet: path: /actuator/health port: 8080 scheme: HTTP initialDelaySeconds: 10 successThreshold: 1 ports: - containerPort: 8080 name: http protocol: TCP - containerPort: 9779 name: prometheus protocol: TCP - containerPort: 8778 name: jolokia protocol: TCP securityContext: privileged: false --- apiVersion: v1 kind: Service metadata: labels: app: visits-service name: visits-service spec: ports: - port: 8080 protocol: TCP targetPort: 8080 selector: app: visits-service type: ClusterIP
-
Reapply the yaml definition on the AKS cluster.
cd ../kubernetes kubectl apply -f spring-petclinic-visits-service.yml
-
Double check that the visits-service started up correctly.
kubectl get pods -w
-
To validate the resulting functionality, in the Azure Portal, navigate back to the page of the
visits-requests
queue of the Service Bus namespace you deployed earlier in this lab. -
On the Overview page of the
visits-requests
queue, verify that there are no active messages. -
In the web browser window, open another tab and navigate to the public endpoint of the
api-gateway
service. -
On the Welcome to Petclinic page, select Owners and, in the drop-down menu, select All.
-
In the list of owners, select the first entry (George Franklin).
-
On the Owner Information page, in the Pets and Visits section, verify the presence of an entry representing the message you submitted earlier in this lab.