Connecting two Microservices together

In Chapter 2, Creating Your First Microservice, where we implemented our first Microservice, we created a REST-based Microservice that returns artificial temperature information when requested. In this chapter, we are going to implement three core services, one will be the modified version of the temperature service where it gets a location as a path parameter and returns an artificial temperature related to that location. The other service will be the location service, which returns three defined locations. Our third service will be the forecast service, which fetches all locations from the location service we implemented and requests the temperature service for each location. All these calls will be done in synchronous mode.

The resource configuration for the application is as follows:

@ApplicationPath("/smartcity")
public class SmartCityServices extends Application {

@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();

classes.add(LocationResource.class);
classes.add(TemperatureResource.class);
classes.add(ForecastResource.class);

return classes;
}
}

With the advent of new Java EE 8 specifications, the object of JSON mapping will be taken care of by JSON-B, the Java API for JSON Binding. The reference implementation of JSON-B is Yasson RI and it's being used implicitly. We are not using any implementations from Jackson, which is a well-known JSON library for Java, to handle the mapping. 

You can find more details about JSON-B at http://json-b.net ; for more information about Yasson, check out https://github.com/eclipse/yasson.

The location service implementation is given as follows. It just returns three cities with their names defined inside instances of the Location class. The response is wrapped with GenericEntity to overcome the type-erasure problem, since the type will be removed at runtime, meaning that List<Location> will be set as List<?> .

The code below gives implementation of the Location service:

@Path("/location")
public class LocationResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getLocations() {

List<Location> locations = new ArrayList<>();
locations.add(new Location("London"));
locations.add(new Location("Istanbul"));
locations.add(new Location("Prague"));

return Response
.ok(new GenericEntity<List<Location>>(locations){})
.build();
}
}

The temperature service retrieves the city name as a path parameter, and randomly generates a temperature in degrees Celsius for the requested city. In order to simulate the temperature-measuring process, a delay of 500 milliseconds is added to the execution. Since we are doing service executions synchronously, the delay will accumulate for the forecast service. We will discuss asynchronous methods of communication among services in Chapter 4, Asynchronous communication in Microservices

The following code showcases the delay in response: 

@Path("/temperature")
public class TemperatureResource {

@GET
@Path("/{city}")
@Produces(MediaType.APPLICATION_JSON)
public Response getAverageTemperature(
@PathParam("city") String cityName) {

Temperature temperature = new Temperature();
temperature.setTemperature(calculateTemperature());
temperature.setTemperatureScale(TemperatureScale.CELSIUS);

try {
Thread.sleep(500);
} catch (InterruptedException ignored) {}

return Response.ok(temperature).build();
}

private Double calculateTemperature() {
Random r = new Random();
int low = 30, high = 50;
return (double) (r.nextInt(high - low) + low);
}
}

The Forecast service that bundles both the location and temperature services is given in the preceding code snippet. We define the web targets for both the location and temperature services with @Uri, and then within the getLocationsWithTemperature() method. First, we invoke the location service, and then for each location that we have, we request the temperature service with that location.

The following code shows getLocationWithTemprature method implementation:

@Path("/forecast")
public class ForecastResource {

@Uri("location")
private WebTarget locationTarget;

@Uri("temperature/{city}")
private WebTarget temperatureTarget;

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getLocationsWithTemperature() {
long startTime = System.currentTimeMillis();
ServiceResponse response = new ServiceResponse();

List<Location> locations = locationTarget.request()
.get(new GenericType<List<Location>>() {});

locations.forEach(location -> {
Temperature temperature = temperatureTarget
.resolveTemplate("city", location.getName())
.request()
.get(Temperature.class);

response.getForecasts()
.add(new Forecast(location, temperature));
});

long endTime = System.currentTimeMillis();
response.setProcessingTime(endTime - startTime);

return Response.ok(response).build();
}
}

After deploying the artifact onto Payara Micro, requesting the http://localhost:8080/forecast-service/smartcity/forecast URL will result in the following excerpt. Keep in mind that temperature values are randomly generated. So in our sample response, the processing time is 1,542 milliseconds, which is a total of 3 consecutive calls to the temperature service.

The following JSON shows the expected response:

{ 
"processingTime":1542,
"forecasts":[
{
"location":{
"name":"London"
},
"temperature":{
"temperature":30.0,
"temperatureScale":"CELSIUS"
}
},
{
"location":{
"name":"Istanbul"
},
"temperature":{
"temperature":30.0,
"temperatureScale":"CELSIUS"
}
},
{
"location":{
"name":"Prague"
},
"temperature":{
"temperature":30.0,
"temperatureScale":"CELSIUS"
}
}
]
}

When we implement this in an asynchronous way in Chapter 4Asynchronous Communication for Microservices, the processing time will reduce drastically.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.128.226.121