Assignment 2 - Add Dapr service-to-service invocation
June 8, 2022 ยท View on GitHub
Assignment goals
To complete this assignment, you must reach the following goals:
- The VehicleRegistrationService and FineCollectionService are both running with a Dapr sidecar.
- The FineCollectionService uses the Dapr service invocation building block to call the
/vehicleinfo/{licensenumber}endpoint on the VehicleRegistrationService.
This assignment targets number 1 in the end-state setup:
Step 1: Start the VehicleRegistrationService with Dapr
In assignment 1, you started all the services using mvn spring-boot:run. When you want to run a service with a Dapr sidecar that handles its communication, you need to start it using the Dapr CLI. There are a couple of things you need to specify when starting the service:
-
The service needs a unique id which Dapr can use to find it. This is called the app-id (or application Id). You specify this with the
--app-idflag on the command-line. -
Each of the services listens on a different HTTP port for requests (to prevent port collisions on localhost). The VehicleRegistrationService runs on port
6002for instance. You need to tell Dapr this port so the Dapr sidecar can communicate with the service. You specify this with the--app-portflag on the command-line. -
The service can use HTTP or gRPC to communicate with the Dapr sidecar. By default these ports are
3500and50001. But to prevent confusion, you'll use totally different port numbers in the assignments. To prevent port collisions on the local machine when running multiple services, you have to specify a unique HTTP and gRPC port per service. You specify this with the--dapr-http-portand--dapr-grpc-portflags on the command-line. Throughout the workshop, you will use the following ports:Service Application Port Dapr sidecar HTTP port Dapr sidecar gRPC port TrafficControlService 6000 3600 60000 FineCollectionService 6001 3601 60001 VehicleRegistrationService 6002 3602 60002 -
Finally you need to tell Dapr how to start the service. The services are Java/Spring Boot services which can be started with the command
mvn spring-boot:run.
You will use the run command of the Dapr CLI and specify all the options above on the command-line:
-
Make sure you have started Docker Desktop on your machine and the Dapr CLI and runtime are installed (see the prerequisites).
-
Open the source code folder in VS Code.
-
Open the terminal window in VS Code and make sure the current folder is
VehicleRegistrationService. -
Enter the following command to run the VehicleRegistrationService with a Dapr sidecar:
dapr run --app-id vehicleregistrationservice --app-port 6002 --dapr-http-port 3602 --dapr-grpc-port 60002 mvn spring-boot:run -
Check the logs for any errors. As you can see, both Dapr as well as application logging is shown as output.
Now you're running a 'Daprized' version of the VehicleRegistrationService. As you might have noticed, you didn't need to change any code for this to work. The VehicleRegistrationService is still just a web API listening for requests. Only now, you've started it with a Dapr sidecar next to it that can communicate with it. This means other services can use Dapr to call this service. This is what you'll do in the next step.
Step 2: Call the VehicleRegistrationService using Dapr service invocation
In this step, you're going to change the code of the FineCollectionService so it uses Dapr service invocation to call the VehicleRegistrationService.
First you're going to change the code so it calls the Dapr sidecar:
-
Open the file
FineCollectionService/src/main/java/dapr/fines/vehicle/DefaultVehicleRegistrationClient.javain VS Code. -
Inspect the
getVehicleInfomethod. It contains a call to the VehicleRegistrationService to retrieve the vehicle info:var params = Map.of("licenseNumber", licenseNumber); return restTemplate.getForObject(vehicleInformationAddress, VehicleInfo.class, params);The
restTemplateis a utility provided by Spring to invoke the VehicleRegistrationService. Its base address for consuming that REST web service is injected through the constructor of that class. That constructor is invoked from a Spring configuration class, which in turn reads the Spring configuration file using@Value. -
Open the file
FineCollectionService/src/main/resources/application.ymlin VS Code.Here we see the actual value being configured. Inspect the
vehicle-information.addresssetting. You can see that in the HTTP call, the URL of the VehicleRegistrationService (running on port 6002) is used. -
The API for calling the Dapr service invocation building block on a Dapr sidecar is:
http://localhost:<daprPort>/v1.0/invoke/<appId>/method/<method-name>You can substitute the placeholders in this URL with the appropriate values so the FineCollectionService's sidecar can call the the VehicleRegistrationService, this yields the following URL:
http://localhost:3601/v1.0/invoke/vehicleregistrationservice/method/vehicleinfo/{licenseNumber}As you can see in this URL, the FineCollectionService's Dapr sidecar will run on HTTP port
3601. -
Replace the URL in the configuration file with the new Dapr service invocation URL. The file should now look like this:
vehicle-information.address: http://localhost:3601/v1.0/invoke/vehicleregistrationservice/method/vehicleinfo/{licenseNumber}It's important to really grasp the sidecar pattern used by Dapr. In this case, the FineCollectionService calls the VehicleRegistrationService by calling its own Dapr sidecar! The FineCollectionService doesn't need to know anymore where the VehicleRegistrationService lives because its Dapr sidecar will take care of that. It will find it based on the
app-idspecified in the URL and call the target service's sidecar. -
Open a new terminal window in VS Code and make sure the current folder is
FineCollectionService. -
Check all your code-changes are correct by building the code:
mvn packageIf you see any warnings or errors, review the previous steps to make sure the code is correct.
-
Enter the following command to run the FineCollectionService with a Dapr sidecar:
dapr run --app-id finecollectionservice --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run -
Check the logs for any errors. As you can see, both Dapr as well as application logging is shown as output.
Now you're going to test the application:
-
Open a new terminal window in VS Code and change the current folder to
TrafficControlService. -
Enter the following command to run the TrafficControlService:
-
mvn spring-boot:run
The TrafficControlService does not need to run with a Dapr sidecar in this assignment. This is because it will still call the FineCollectionService over HTTP as before.
The services are up & running. Now you're going to test this using the simulation.
-
Open a new terminal window in VS Code and change the current folder to
Simulation. -
Start the simulation:
mvn spring-boot:run
You should see similar logging as before when you ran the application. So all the functionality works the same, but now you use Dapr service invocation to communicate between the FineCollectionService and the VehicleRegistrationService.
Step 3: Use Dapr service invocation with the Dapr SDK for Java
In this step, you're going to change the code of the FineCollectionService so it uses the Dapr SDK for Java to call the VehicleRegistrationService. The SDK provides a more integrated way to invoke the Dapr sidecar API.
First stop the simulation:
-
Open the terminal window in VS Code in which the Camera Simulation runs.
-
Stop the simulation by pressing
Ctrl-Cand close the terminal window by clicking the trashcan icon in its title bar (or typing theexitcommand). -
Open the terminal window in VS Code in which the FineCollectionService runs.
-
Stop the service by pressing
Ctrl-C. Keep this terminal window open and focused. -
Add a dependency to the Dapr SDK for Java to the
pom.xmlin the FineCollectionService directory:<dependency> <groupId>io.dapr</groupId> <artifactId>dapr-sdk</artifactId> </dependency>The version of the dependency is managed using Mavens "dependency management" - you can inspect the
pom.xmlfile inside the source code folder to see the exact version.The Dapr SDK for Java contains the
DaprClientclass that we will use to directly invoke the Dapr API. There is also an additional library that integrates with Spring Boot, but we don't need that yet. It is only necessary for building application that offer services with Dapr.
Now you'll change the code to use the Dapr-provided DaprClient to call the VehicleRegistrationService. In step 2 we used the existing code based on Spring's RestTemplate, keeping our code unaware of Dapr. The Dapr SDK for Java ensures that calls are routed through the Dapr sidecar.
-
Create a new file,
FineCollectionService/src/main/java/dapr/fines/vehicle/DaprVehicleRegistrationClient.javaand open it in VS Code. -
Declare a class
DaprVehicleRegistrationClientthat implements theVehicleRegistrationClientinterface. Make sure to include a package declaration:package dapr.fines.vehicle;. To fulfil the contract of theVehicleRegistrationClientinterface, add the following method:@Override public VehicleInfo getVehicleInfo(String licenseNumber) { return null; } -
Open the file
FineCollectionService/src/main/java/dapr/fines/FineCollectionConfiguration.javain VS Code. Add a new method to declare a Spring Bean of typeDaprClient:@Bean public DaprClient daprClient() { return new DaprClientBuilder().build(); }In the same class, the
vehicleRegistrationClientmethod declares a Spring Bean that provides an implementation of theVehicleRegistrationClientinterface. To do so, it needs aDaprClientbean. Replace this method with the following:@Bean public VehicleRegistrationClient vehicleRegistrationClient(final DaprClient daprClient) { return new DaprVehicleRegistrationClient(daprClient); }Finally, update the import statements in the class:
import dapr.fines.vehicle.DaprVehicleRegistrationClient; import io.dapr.client.DaprClient; import io.dapr.client.DaprClientBuilder; -
Go back to the
DaprVehicleRegistrationClientimplementation class and add a few import statements in this file to make sure you can use the Dapr client and the Sleuth integration:import io.dapr.client.DaprClient; import io.dapr.client.domain.HttpExtension; import spring.SleuthDaprTracingInjector; import java.time.Duration;Now add an instance variable of type
DaprClient, and add a constructor to inject it:public class DaprVehicleRegistrationClient implements VehicleRegistrationClient { private final DaprClient daprClient; public DaprVehicleRegistrationClient(final DaprClient daprClient) { this.daprClient = daprClient; }Finally, update the implementation of the
getVehicleInfo()method in this class to use theDaprClient:var result = daprClient.invokeMethod( "vehicleregistrationservice", "vehicleinfo/" + licenseNumber, null, HttpExtension.GET, VehicleInfo.class ); return result .contextWrite(new SleuthDaprTracingInjector()) .block(Duration.ofMillis(1000));As you can see in this snippet, this code does not require our application to know the address of the Vehicle Registration Service, only it's name. With each call to the
DaprClient, you specify theapp-idof the service you want to communicate with.Also note that the
invokeMethodmethod of theDaprClientreturns aMono. Because we don't want to get distracted by all the possibilities that reactive progamming brings, we immediately invokeblockto await the result. In a real-world scenario, it would make more sense to propagate that Mono through our application to make it more reactive.At this point, you can remove the
vehicle-information.addressentry from theapplication.ymlfile. The Dapr SDK for Java does not need it.
Now the FineCollectionService is changed to use the Dapr SDK for service invocation. Let's test this.
-
If you followed the instructions in this assignment, the VehicleRegistrationService and TrafficControlService are still running.
-
Open the terminal window in VS Code in which the FineCollectionService was running.
-
Enter the following command to start the changed FineCollectionService again:
dapr run --app-id finecollectionservice --app-port 6001 --dapr-http-port 3601 --dapr-grpc-port 60001 mvn spring-boot:run
The services are up & running. Now you're going to test this using the simulation.
-
Open a new terminal window in VS Code and change the current folder to
Simulation. -
Start the simulation:
mvn spring-boot:run
You should see similar logging as before when you ran the application.
Step 4: Use Dapr observability
So how can you check whether or not the call to the VehicleRegistrationService is handled by Dapr? Well, Dapr has some observability built in. You can look at Dapr traffic using Zipkin:
-
Open a browser and go the this URL: http://localhost:9411/zipkin.
-
Click the
RUN QUERYbutton in the top right of the screen to search for traces. -
You should see the calls between the FineCollectionService and the VehicleRegistrationService. You can expand and collapse each trace and click the
SHOWbutton to get more details:
-
If you click the dependencies button and search, you will see the services and the traffic flowing between them:

Next assignment
Make sure you stop all running processes and close all the terminal windows in VS Code before proceeding to the next assignment.
Go to assignment 3.