Book Image

Microservices Deployment Cookbook

By : Vikram Murugesan
Book Image

Microservices Deployment Cookbook

By: Vikram Murugesan

Overview of this book

This book will help any team or organization understand, deploy, and manage microservices at scale. It is driven by a sample application, helping you gradually build a complete microservice-based ecosystem. Rather than just focusing on writing a microservice, this book addresses various other microservice-related solutions: deployments, clustering, load balancing, logging, streaming, and monitoring. The initial chapters offer insights into how web and enterprise apps can be migrated to scalable microservices. Moving on, you’ll see how to Dockerize your application so that it is ready to be shipped and deployed. We will look at how to deploy microservices on Mesos and Marathon and will also deploy microservices on Kubernetes. Next, you will implement service discovery and load balancing for your microservices. We’ll also show you how to build asynchronous streaming systems using Kafka Streams and Apache Spark. Finally, we wind up by aggregating your logs in Kafka, creating your own metrics, and monitoring the metrics for the microservice.
Table of Contents (15 chapters)
Microservices Deployment Cookbook
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Writing REST APIs with Spring MVC


There are two types of communication models. One of them is synchronous, where the client waits for the server to respond to its request. The other is asynchronous, where the client fires a request and forgets. Though Servlet 3.0 and above let you create asynchronous servlets, in our recipes, we will focus on traditional servlet-based HTTP APIs for simplicity. We will also be looking at asynchronous communication in later chapters.

Getting ready

When it comes to building REST APIs, there are several frameworks to choose from. As we already set up the Spring ecosystem in our previous recipe, it would make more sense and be much easier to use Spring MVC to expose REST APIs.

How to do it...

The true advantage of Spring Boot is that you do not have to add any new dependencies to enable web support for your application. Spring Boot's parent pom file (spring-boot-starter-parent) takes care of that for you. Now let's take a look at how to write our first API. If you are familiar with Spring MVC, this should be really straight-forward for you:

  1. Create a Controller class called com.packt.microservices.geolocation.GeoLocationController.java, which will be responsible for basic CRUD operations for the geolocation of all users:

            package com.packt.microservices.geolocation; 
     
            import org.springframework.web.bind.annotation.RequestMapping; 
            import org.springframework.web.bind.annotation.RestController; 
     
            @RestController 
            @RequestMapping("/geolocation") 
            public class GeoLocationController { 
     
            } 
    

    There are two things to note here. The @RestController annotation indicates that we are going to use this controller to expose our REST APIs. It implicitly adds the @ResponseBody annotation to all controller methods as that is something you would want to do when exposing your REST APIs using Spring MVC. The @RequestMapping annotation specifies where your HTTP resource is located. We are setting @RequestMapping on the controller level to apply it to all controller methods.

    Note

    Using @RequestMapping on the Controller class level to define a root resource path is considered to be one of the best practices. Instead of having to create API paths such as /getGeolocation or /createGeolocation, it is always a better practice to use the same path, /geolocation, with the GET method to get geolocation data and the POST method to create geolocation data.

  2. Before we jump into creating our APIs, we will need some classes for the domain object and service. Let's start with creating our domain object. Assume that our GeoLocation consists latitude and longitude. We will be defining both latitude and longitude as double to provide better precision. Now we will have to say which user's geolocation it is. So we might want to add a userId. We also need to say at what time the user was at the geolocation. So we might want to add a timestamp in EPOCH time format. The timestamp will be of type long. This is how your plain old java object (POJO) class will look:

            package com.packt.microservices.geolocation; 
    
            import java.io.Serializable; 
            import java.util.UUID; 
     
              public class GeoLocation implements Serializable { 
     
                private static final long serialVersionUID = 1L; 
       
               private double latitude; 
               private double longitude; 
               private UUID userId; 
               private long timestamp; 
       
              public double getLatitude() { 
                return latitude; 
              } 
       
              public void setLatitude(double latitude) { 
                this.latitude = latitude; 
              } 
       
              public double getLongitude() { 
               return longitude; 
              } 
       
              public void setLongitude(double longitude) { 
                this.longitude = longitude; 
              } 
       
              public UUID getUserId() { 
               return userId; 
              } 
       
              public void setUserId(UUID userId) { 
               this.userId = userId; 
              } 
       
             public long getTimestamp() { 
              return timestamp; 
             } 
       
             public void setTimestamp(long timestamp) { 
              this.timestamp = timestamp; 
             } 
            } 
    

    As you can see, we have used the java.util.UUID class to represent the userId, assuming that this UUID uniquely identifies a user. We will not be creating the user POJO as it is out of scope for this recipe.

    In an ideal scenario, one would be using a NoSQL or relational database to store the geolocations. In this case, NoSQL sounds more suitable due to several reasons, including the fact that our data is time series data, in JSON format, unstructured but will change over time and we will have a humongous amount of data.

  3. For simplicity purposes, we will be storing our geolocations in an in-memory java.util.List<GeoLocation> collection. Let's create our repository that holds all our geolocation objects, com.packt.microservices.geolocation.GeoLocationRepository.java:

            package com.packt.microservices.geolocation; 
     
            import java.util.ArrayList; 
            import java.util.Collections; 
            import java.util.List; 
     
            import org.springframework.stereotype.Repository; 
     
            @Repository 
            public class GeoLocationRepository { 
     
              private List<GeoLocation> geolocations = new
              ArrayList<GeoLocation>(); 
       
                public void addGeoLocation(GeoLocation geolocation) { 
                  geolocations.add(geolocation); 
                } 
       
               public List<GeoLocation> getGeoLocations() { 
                  return Collections.unmodifiableList(geolocations); 
               } 
             } 
    
  4. Now let's take a look at how your Service interface will look:

            package com.packt.microservices.geolocation; 
     
            import java.util.List; 
     
            public interface GeoLocationService { 
     
              public GeoLocation create(GeoLocation geolocation); 
              public List<GeoLocation> findAll(); 
            }  
    
  5. Both our repository and service have a very simple interface. Ideally in real-time applications, you might want to add more complicated methods that not only perform CRUD operations but also sort, filter, select only specific fields, and so on. Now let's take a look at our com.packt.microservices.geolocation.GeoLocationServiceImpl.java class:

            package com.packt.microservices.geolocation; 
     
            import java.util.List; 
     
            import org.springframework.beans.factory.annotation.Autowired; 
     
            import org.springframework.stereotype.Service; 
     
            @Service 
            public class GeoLocationServiceImpl implements
            GeoLocationService { 
       
              @Autowired 
              private GeoLocationRepository repository; 
     
              @Override 
              public GeoLocation create(GeoLocation geolocation) { 
               repository.addGeoLocation(geolocation); 
               return geolocation; 
              } 
     
              @Override 
              public List<GeoLocation> findAll() { 
                return repository.getGeoLocations(); 
              } 
            }

    Note

    It is always strongly recommended that you write unit test cases for any new code. But as that is a little out of scope for this book, we will not be writing unit test cases for any of the previous code. To learn more about unit testing Spring Boot applications, please take a look at Spring Boot's documentation at https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html.

  6. Now that our domain and service classes are all set to go, let's modify our Controller class to save and find geolocations. Add the following snippet into your Controller class body:

            @Autowired
            private GeoLocationService service;
    
            @RequestMapping(method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
            public GeoLocation create(@RequestBody GeoLocation geolocation) {
              return service.create(geolocation);
            }
      
            @RequestMapping(method = RequestMethod.GET, produces = "application/json")
            public List<GeoLocation> findAll() {
              return service.findAll();
            } 
    

In this implementation, there are a few things to notice. The @RequestMapping annotation does not have a path defined as it is already derived from the class-level annotation. For both the create and findAll methods, we are using the same path but different HTTP methods as per best practice. Since we are dealing only with JSON here, we have set the produces and consumes values to application/json. The return types of the create and findAll methods are GeoLocation and List<GeoLocation> respectively. Spring MVC internally uses Jackson to convert them to their equivalent JSON strings.

That's it! We are now ready to test our application:

  1. Let's try to create two geolocations using the POST API and later try to retrieve them using the GET method. Execute the following cURL commands in your terminal one by one:

          curl -H "Content-Type: application/json" -X POST -d'{"timestamp": 1468203975, "userId": "f1196aac-470e-11e6-beb8-9e71128cae77", "latitude": 41.803488, "longitude": -88.144040}' http://localhost:8080/geolocation
    
  2. This should give you an output similar to the following (pretty-printed for readability):

            { 
             "latitude": 41.803488, 
             "longitude": -88.14404, 
             "userId": "f1196aac-470e-11e6-beb8-9e71128cae77", 
             "timestamp": 1468203975 
            } 
     
     
            curl -H "Content-Type: application/json" -X POST -d '{"timestamp": 1468203975, "userId": "f1196aac-470e-11e6-beb8-9e71128cae77", "latitude": 9.568012, "longitude": 77.962444}' http://localhost:8080/geolocation
    
  3. This should give you an output similar to the following (pretty-printed for readability):

    {
              "latitude": 9.568012,
              "longitude": 77.962444,
              "userId": "f1196aac-470e-11e6-beb8-9e71128cae77",
              "timestamp": 1468203975
            }
    
  4. To verify whether your entities were stored correctly, execute the following cURL command:

          curl http://localhost:8080/geolocation
    
  5. This should give you an output similar to the following (pretty-printed for readability):

            [ 
            { 
              "latitude": 41.803488, 
              "longitude": -88.14404, 
              "userId": "f1196aac-470e-11e6-beb8-9e71128cae77", 
              "timestamp": 1468203975 
            }, 
            { 
              "latitude": 9.568012, 
              "longitude": 77.962444, 
              "userId": "f1196aac-470e-11e6-beb8-9e71128cae77", 
              "timestamp": 1468203975 
            } 
            ]
    

You now have a fully working version of your microservice. The remaining recipes in this chapter try to achieve the same logic with different frameworks, such as WildFly Swarm and Dropwizard. Later in this chapter, we will also look at another framework that helps you build REST APIs quickly called SparkJava (different from Apache Spark). If you will be using Spring Boot for your microservices, you can jump to the next chapter. If you are interested in any of the frameworks that were mentioned, jump to the appropriate recipe in this chapter.