Home | about | blogs | microservices-spring-boot

Microservices with Spring Boot

Share it now!

What are Microservices?

Introduction

The term "microservices" appears in many technology journals these days. This blog addresses common questions including: "What are microservices, and more importantly, how are they a better solution than what's already out there? Also, if microservices are so great, how easy is it to develop microservices?" Microservices are stand-alone applications that break down functionality into fine-grained components that run and restart independently.

The Problem

The problem with large, monolithic applications is that they are difficult to maintain, test, and extend. Component architectures such as COM, SOA, etc. promised reusability but led to proliferations of libraries that became hard to maintain as well (e.g., DLL hell or JAR hell, etc.).  Microservices with Spring Cloud promise faster change, greater availability, fine-grain scaling, and better adaptation to DevOps thinking. They typically use REST and HTTP to decouple components, leading to easier scalability. With Spring Cloud and Spring Boot, you get an opinionated framework that reduces the work required when developing services while offering flexibility when you need to customize your system.

The Solution

Core Characteristics of Microservices Include:

  • Components exposed as services: Services are registered in a registry.
  • Tied to a specific domain: Services can focus on their own work rather than being tied to the workings of other services.
  • Loosely coupled: There's a natural separation of concerns, since the services can focus on their individual work. This narrowed focus reduces concerns such as how the services talk to each other or what's going on inside the other services.
  • Built to tolerate failure: Because components are not intertwined, the system is built to allow services to be automatically retried when a down service returns to health. Systems are assumed to fail, so resiliency is baked right in.
  • Delivered continuously via automation: Services can be updated independently and deployed without disturbing other components.
  • Built and ran by independent teams: Teams can "divide and conquer" by splitting the work into smaller components that don't disturb each other.

Components of Spring Cloud Thanks to Netflix®:

  • Service registry and discovery in Spring Cloud Eureka
  • Circuit breaking technology in Spring Cloud Hystrix
  • Client-side load balancing in Spring Cloud Ribbon
  • Service proxying and API gateway via Spring Cloud Zuul
  • Messaging (Kafka, Rabbit MQ, etc.) through Spring Cloud Stream
  • Data processing pipelines with Spring Cloud Data Flow
  • Convenient name proxies with Feign

Real World Use Cases for Microservices

Our recent work with a client allowed us to have a hands-on experience with microservices in a real-world education environment. We were able to deploy developers across multiple parts of the system and have them work independently to bring the system forward. In addition, we took advantage of a mixed online and local environment, to isolate components used only for development from deployed components. Eventually, we did end up with a local deployment for improved performance (initially getting things working online). Also, we used Vagrant to deploy a complete development environment for the developers working on the front end. This lessened concerns regarding wiring up microservices or having to stub out services, enabling front end developers to come up to speed quickly.

Microservices In Action: A Vending Machine Service

Application Background

Now, let's look at microservices in action for a real-world application. In this case, we are starting with a service to display the inventory in a vending machine. We will illustrate a problem with the application and see how Spring Cloud's Eureka can better separate the client from the server. A client (e.g., a mobile app or mobile-responsive web app) could talk directly to the vending machine. While this can be demonstrated using an HTTP lookup, the following example will simply use localhost.

This code was created using Spring Tool Suite (STS) based on Eclipse and Maven; however, the code was initiated using Spring Initializr at http://start.spring.io.

Application in Action

For our model, we have a vending machine (i.e., a dispenser) containing sleeves of items. Each item is either a can or bottle with a brand associated with it. In our example, the inventory is hard-coded, but live.

Produce an inventory in JSON of the products in the dispenser:
http://localhost:8086/inventory

Produce an inventory in JSON

Withdraw a beverage item and update the inventory:
http://localhost:8085/beverage/{line}

Withdraw a beverage item and update the inventory

{line} is a value from 0-4 corresponding to the brand dispensing line in our stock inventory.

To display the current contents in a client, we have a second service that simply calls the above directly to display the current inventory:
http://localhost:8086/dispenser

Display the current contents in a client

(This is before withdrawing a beverage as in the above URL)

Repeatedly hitting the beverage line will deplete the inventory:

Repeatedly hitting the beverage line will deplete the inventory

One of the many benefits of microservices architecture is to decouple the client from service; we can use the Eureka service registry as a means to discover services rather than hard-coding websites or references into code. Now, if the server remains at a fixed address or domain, we would have no difficulty; however, if the client were, say, embedded within a mobile web application, we would need to update the client every time the server name changed. Moreover, we would need to rely on a hardware load balancer to scale up on load. Instead, we can use the Eureka service to make the server discoverable by the client and we then demonstrate how to decouple the services.

Installing the Application

Steps:

  1. Create a development directory for the Vending Machine software
    1. $ cd <working-directory>
    2. For me, this is
      $ cd /Users/fredericvanwest/Downloads/VendingMachine/checkout
      $ mkdir VendingMachine
      $ cd VendingMachine
  2. Clone the repositories from git
    1. From the UNIX command line, clone the repository
      1. $ git clone https://github.com/fredvanwest/vending-machine-service.git
      2. $ git clone https://github.com/fredvanwest/vending-machine-client.git
    2. This will retrieve the sample repositories and stage them for development
  3. Import the vending-machine-service into Spring Tool Suite
    1. Launch Spring Tool Suite

      Launch Spring Tool Suite

    2. From Package Explorer, right-click and select "Import…"

      From Package Explorer, right-click and select "Import…"

    3. Select "Maven > Existing Maven Projects"

      Select "Maven > Existing Maven Projects"

    4. Select "Next"
    5. In "root directory" navigate to the working directory:

      In "root directory" navigate to the working directory

    6. Click "Open"
    7. At this point, select the /vending-machine-service/pom.xml file:

      Select the /vending-machine-service/pom.xml file

    8. Click "Finish"
    9. The project should build immediately
    10. Under the "Boot Dashboard", "Local > vending-machine-service", right-click and select "(Re)start":

      Under the "Boot Dashboard", "Local > vending-machine-service"

      Under "Local > vending-machine-service", right-click and select "(Re)start"

    11. This will start the service running on port 8086:

      This will start the service running on port 8086

    12. If port 8086 is unavailable, you'll need to adjust in the application.properties file under "src > main > resources":

      If port 8086 is unavailable, you'll need to adjust in the application.properties file under "src > main > resources"

    13. You can test if the service is running by opening a web browser and hitting the URL
      1. http://localhost:8086/inventory (See above as well)
      2. You should see that the following returned:

        {"sleeves":[{"items":[{"type":"can","brand":"Coke"},{"type":"can","brand":"Coke"},{"type":"can","brand":"Coke"}]},{"items":[{"type":"can","brand":"Diet Coke"},{"type":"can","brand":"Diet Coke"}]},{"items":[{"type":"can","brand":"Sprite"}]},{"items":[{"type":"can","brand":"Dr. Pepper"},{"type":"can","brand":"Dr. Pepper"}]},{"items":[{"type":"bottle","brand":"Dasani"},{"type":"bottle","brand":"Dasani"},{"type":"bottle","brand":"Dasani"},{"type":"bottle","brand":"Dasani"}]}]}​

  4. Import the vending-machine-client into Spring Tool Suite:

    From Package Explorer, right-click and select "Import…"

    1. From Package Explorer, right-click and select "Import…"
    2. Select "Maven > Existing Maven Projects":

      Select "Maven > Existing Maven Projects"

    3. Select "Next"
    4. In "root directory" navigate to the working directory:

      In "root directory" navigate to the working directory

    5. Click "Open"
    6. At this point, select the /vending-machine-client/pom.xml file:

      Select the /vending-machine-client/pom.xml file

    7. Click "Finish"
    8. The project should build immediately
    9. Under the "Boot Dashboard", "Local > vending-machine-client", right-click and select "(Re)start":

      Under the "Boot Dashboard", "Local > vending-machine-client"

      right-click and select "(Re)start"

    10. This will start the client running on port 8085
    11. If port 8085 is unavailable, you'll need to adjust in the application.properties file under "src > main > resources":

      If port 8085 is unavailable, you'll need to adjust in the application.properties file under "src > main > resources"

    12. You can test that the service is running by opening a web browser and hitting the URL:
      1. http://localhost:8085/dispenser (See above illustrations)
      2. You should see:

        COKE: 3 DIET CODE: 2 SPRITE: 1 DR PEPPER: 2 DASANI:4

      3. Note: The inventory is reset every time that the vending-machine-service is cycled, so the results above can be returned by restarting that service
      4. Also, you can test that the inventory goes down by issuing:
        http://localhost:8086/beverage/0
      5. This will return a JSON representation of an item:
        {"type":"can","brand":"Coke"}
      6. A subsequent call to http://localhost:8085/dispenser will now yield: 

        COKE: 2 DIET CODE: 2 SPRITE: 1 DR PEPPER: 2 DASANI:4

      7. This demonstrates that the COKE inventory dropped by one
      8. Make sure to stop the services in STS before proceeding

  5. Create the Eureka Server
    1. From Package Explorer, right-click and select “New > Spring Starter Project”:

      From Package Explorer, right-click and select "New > Spring Starter Project"

    2. From the dialog, change
      1. Name: vending-machine-server
      2. Group: com.vanwest
      3. Artifact: vending-machine-server
      4. Description: Vending Machine Server
      5. Package: com.vanwest
      6. Uncheck default location and select the same root directory as were used for vending-machine-server and vending-machine-client
      7. Make sure to paste "vending-machine-server" on the end of the Location:

        Paste "vending-machine-server" on the end of the Location

      8. Click "Next"
      9. Select "Cloud Discovery > Eureka Server":

        Select "Cloud Discovery > Eureka Server"

      10. Select "Ops > Actuator":

        Select "Ops > Actuator"

      11. Click "Finish"
    3. Add Eureka server to code
      1. Open "src > main> java > com.vanwest > VendingMachineServerApplication.java"
      2. Above @SpringBootApplication, add @EnableEurekaServer and fix imports

        Above @SpringBootApplication, add @EnableEurekaServer and fix imports

      3. Open "src > main > resources > application.properties", and enter:

        server.port=8761
        eureka.client.register-with-eureka=false
        eureka.client.fetch-registry=false
        eureka.datacenter=phoenix
        eureka.environment=development

        Open "src > main > resources > application.properties", and enter:/p>

    4. Under the "Boot Dashboard", "Local > vending-machine-server", right-click and select "(Re)start":

      Under the "Boot Dashboard", "Local > vending-machine-server"

      Right-click and select "(Re)start"

      1. You can see that Eureka is running by hitting the website:
        http://localhost:8761
      2. You should see that the "test" and "phoenix" environments are displayed
      3. Under "Instances currently registered with Eureka" you should see "No instances available":

        Under "Instances currently registered with Eureka" you should see "No instances available"


  6. Adjust the vending-machine-service to be a Eureka client
    1. From “Package Explorer” right-click on “vending-machine-service” and select “Maven > Add Dependency”:

      Right-click on "vending-machine-service"

      Select "Maven > Add Dependency"

    2. In "Enter groupid, artifactId or sha1 prefix or pattern (*):" enter: "spring-cloud-starter-eureka"
    3. Select "org.springframework.cloud  spring-cloud-starter-eureka":

      Select "org.springframework.cloud spring-cloud-starter-eureka"

    4. Click "OK"
    5. Open "src > main > java > com.vanwest.Vending.Machine.Service > VendingMachineServiceApplication.java"
    6. Above @SpringBootApplication, add @EnableEurekaClient and fix imports:

      Above @SpringBootApplication, add @EnableEurekaClient and fix imports

    7. Save the file
    8. Open "src > main> resources > application.properties" and enter:

      eureka.client.register-with-eureka=true
      eureka.client.fetch-registry=true
      eureka.instance.hostname=localhost

      Open "src > main> resources > application.properties"

    9. ​Save the file
    10. Right-click on "src > main > resources" and select "New > File" and enter: "bootstrap.properties":

      Right-click on "src > main > resources"

      Select "New > File" and enter: "bootstrap.properties"

    11. Enter: spring.application.name=vending-machine-service
    12. Save the file
    13. Under the "Boot Dashboard" "Local > vending-machine-service" right-click and select "(Re)start":

      Under the "Boot Dashboard" "Local > vending-machine-service"

      Right-click and select "(Re)start"

    14. Assuming you left vending-machine-server running, open:
      http://localhost:8761
    15. Under "Instances currently registered with Eureka" you should see "VENDING-MACHINE-SERVICE":

      See "VENDING-MACHINE-SERVICE":

    16. You can reassure yourself the service is running with the tests above by opening:
      http://localhost:8086/inventory

  7. Adjust the vending-machine-client to be a Eureka client and use a named servicen in place of the hard-coded address
    1. From “Package Explorer” right-click on “vending-machine-client” and select “Maven > Add Dependency”:

      From "Package Explorer" right-click on "vending-machine-client"

      Select "Maven > Add Dependency":

    2. In "Enter groupid, artifactId or sha1 prefix or pattern (*):" enter: "spring-cloud-starter-eureka"
    3. Select "org.springframework.cloud spring-cloud-starter-eureka":

      Select "org.springframework.cloud spring-cloud-starter-eureka"

    4. Click "OK"
    5. Open "src > main > java > com.vanwest.vendingmachineclient > VendingMachineClientApplication.java"
    6. Above @SpringBootApplication, add @EnableEurekaClient and fix imports:

      Above @SpringBootApplication, add @EnableEurekaClient and fix imports

    7. Open "src > main > java > com.vanwest.vendingmachineclient > VendingController.java":

      Open "src > main > java > com.vanwest.vendingmachineclient > VendingController.java

    8. Under "VendingController" enter:

      ​@LoadBalanced
      @Bean
      public RestTemplate restTemplate(RestTemplateBuilder builder) {
               
      return builder.build();
      }

              @Autowired
              private RestTemplate restTemplate;

    9. In method GetDispenser, replace:

      RestTemplate rest = new RestTemplate();
      Dispenser dispenser = rest.getForObject("http://localhost:8086/inventory",
      Dispenser.class);

      with:

      //RestTemplate rest = new RestTemplate();
      //Dispenser dispenser = rest.getForObject("http://localhost:8086/inventory", 
      //Dispenser.class);
      Dispenser dispenser = restTemplate.getForObject("http://vending-machine-service/inventory", Dispenser.class);

      Replace code

    10. Save the file
    11. Open "src > main> resources > application.properties" and enter:

      eureka.client.register-with-eureka=true
      eureka.client.fetch-registry=true
      eureka.instance.hostname=localhost

      Replace code

    12. Save the file
    13. Right-click on "src > main > resources" select "New > File" and enter: "bootstrap.properties":

      Right-click on "src > main > resources"

      Select "New > File" and enter: "bootstrap.properties"

    14. Enter: spring.application.name=vending-machine-client
    15. Save the file
    16. Under the "Boot Dashboard", "local > vending-machine-client", right-click and select "(Re)start":

      Under the "Boot Dashboard", "local > vending-machine-client"

      Right-click and select "(Re)start"

      1. Now open:
        http://localhost:8761

      2. Under "Instances currently registered with Eureka" you should see "VENDING-MACHINE-CLIENT":

        Look for "VENDING-MACHINE-CLIENT"

      3. You can reassure yourself the service is running with the tests above by hitting:
        http://http://localhost:8086/beverage/0

        Look for "VENDING-MACHINE-CLIENT"

        and seeing that the inventory is now reduced in:
        http://localhost:8085/dispenser

        Reassure yourself the service is running

    So here we can see how a direct named URL was replaced with registry lookup in Eureka. The Spring Cloud framework makes it much easier to create microservices and manage them. Additional features, such as circuit breaker technology, can allow a microservice to halt on fault or load and resume when conditions improve without human intervention. Microservice technology is instrumental in deploying fault-tolerant systems, which is especially true in the education domain.

Return to the blog listing page