© Binildas Christudas 2019
Binildas ChristudasPractical Microservices Architectural Patternshttps://doi.org/10.1007/978-1-4842-4501-9_8

8. Spring Cloud

Binildas Christudas1 
(1)
Trivandrum, Kerala, India
 

In Chapter 7, you were introduced to the Spring Boot. You explored several examples built with Spring Boot so now you have the building blocks for an enterprise-level application, whether the application is medium scale or large scale. In this chapter, you will look into the next indispensable piece of Spring called Spring Cloud, which is built over Spring Boot itself. There are a bunch of common patterns in distributed, microservices ecosystems that can help you integrate the core services as a loosely coupled continuum, and Spring Cloud provides many powerful tools that enhance the behavior of Spring Boot applications to implement those patterns. You will learn only the major and critical blocks required to keep the discussion continuing in subsequent chapters, again with the help of concrete code samples. The samples in this chapter are incrementally built over the preceding samples, so please don’t skip any section or you may not get the succeeding samples to run smoothly.

You will learn about the following in this chapter:
  • The Feign client to make HTTP API calls

  • Hystrix, the circuit breakers to gracefully degrade

  • The Hystrix dashboard, showing a graphical overview of a circuit breaker in the system

  • Ribbon, the client-side load balancer

  • Eureka, a REST-based registry service

  • Zuul, the API gateway, which is the front controller for your microservices

  • The Config Server to externalize version control and manage configuration parameters for microservices

Spring Cloud for Microservices Architecture

The “The Architecture of Microservices” section in Chapter 4 talked about the major new concerns you need to deal with when you move from a traditional architecture to a microservices-based architecture. Due to the inversion effect in microservices architecture, many of the intra-application concerns now get moved to the inter (micro) services level, and when the number of microservices keeps growing, so does the inherent growth in associated outer architecture complexity.

Spring Cloud provides good out-of-the-box support for typical use cases and extensibility mechanisms required in the microservices ecosystem. A few of them are listed here:
  • Distributed/versioned configuration

  • Service registration and discovery

  • Routing and load balancing

  • Interservice calls

  • Circuit breakers

  • Distributed messaging

Similar to Spring Boot, Spring Cloud takes a very declarative approach, and often you get a lot of features with just an inclusion of a dependency in a classpath and/or an annotation. Let’s look into a few of them now.

Feign Client Usage in Spring Cloud

The Feign client is a Java-to-HTTP client-side binder inspired by libraries like Retrofit, JAXRS-2.0, and WebSocket. Feign’s intent is to reduce the complexity of invoking HTTP APIs by generalizing the binding denominator uniformly to HTTP APIs regardless of the maturity of the REST APIs (a.k.a. the restfulness of the APIs). Feign makes HTTP API calls simple by processing annotations into a templatized request. Just before sending the requests, arguments are applied to these templates in a straightforward fashion. However, Feign only supports text-based APIs. Nevertheless, Feign dramatically simplifies system aspects like replaying requests, unit testing, etc. You will look at invoking HTTP APIs in this section.

Design a Feign Client Scenario

Starting with this section and for all other examples in Spring Cloud in this chapter, you will use a simple yet representative real-world use case. You will look into the design of the use case first and then learn more. Figure 8-1 illustrates a simple scenario where there are two separate applications or, in our terms, two separate microservices: Product Web and Product Server.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig1_HTML.jpg
Figure 8-1

A Feign client usage scenario

Let’s explore the components one by one:
  • Product Server: Product Server is a full-fledged microservice application implementing a part of the CRUD operations. This microservice uses a Mongo DB to write to and read from the data pertaining to product entities. So the microservice encapsulates all the components required to do typical CRUD operations against a database and also to expose the functionality as REST-based services, including
    • ProductRestController: The REST controller exposing the selected CRUD operations

    • Product: The Entity class, which is what you are persisting to the DB

    • ProductRepository: The Mongo repository doing all the plumbing in executing the Mongo DB operations

    • EcomProductMicroserviceApplication: The Spring Boot-based Application class

  • Product Web: Product Web is designed as another full-fledged microservice application. However, this time the microservice application doesn’t do any CRUD; instead, it acts as a façade to the external world or clients delegating all calls to the Product Server microservice. So Product Web also gets all of the components previously seen for Product Server; the only difference is that there is no repository component since it doesn’t do any DB interactions. But then, since Product Web delegates all REST calls coming to it to the Product Server microservice, you must create code for that, and it is for this purpose you will leverage the Feign client instead of hand-coding all the plumbing, which I will explain next.

  • ProductService: ProductService is the interface listing all the methods selected for exposing, hence both Product Server and Product Web fulfill the contract specified in this interface.

  • ProductServiceProxy: This component is another abstract interface that extends ProductService, so it gets all the methods expected out of both the Product Server and the Product Web interfaces. Moreover, ProductServiceProxy is the interface for annotating the Feign client’s essential configuration. In every practical sense, this can be considered as the client-side proxy for the server-side functionality.

  • Spring Cloud Feign Client: The Spring’s Feign client is the actual implementation that Spring Cloud realizes on the fly. It is through this proxy implementation the calls from Product Web are delegated to Product Server.

The interactions described above are illustrated in Figure 8-2.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig2_HTML.jpg
Figure 8-2

Delegation of calls through the Feign client

Here, requests from the browser will hit the Product Web microservice first. The Product Web microservice will in turn delegate the call to the Product Server microservice with the help of a ProductServiceProxy. The Product Server microservice will retrieve the data from the database and provide the response, which will be returned back through the call stack to the browser.

Code Using Feign Client

Since Feign is a declarative HTTP client, with one spring-cloud-starter-feign dependency mentioned in your Maven POM and with another single annotation of @EnableFeignClients, you have a fully functional HTTP client with a sensible, ready-to-go default configuration. Visit pom.xml to see the explicit mention of the Feign client dependency. See Listing 8-1.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
Listing 8-1

Feign Client Dependency in Maven (ch08\ch08-01\ProductWeb\pom.xml)

With Feign added on the classpath, only one more annotation is needed to make everything work with the default configuration properties. This is mentioned in Listing 8-2.
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@EnableFeignClients(basePackageClasses = ProductServiceProxy.class)
@ComponentScan(basePackageClasses = ProductServiceProxy.class)
@CrossOrigin
@RestController
public class ProductRestController implements ProductService{
private ProductServiceProxy productServiceProxy;
    @Autowired
    public ProductRestController(ProductServiceProxy productServiceProxy){
        this.productServiceProxy = productServiceProxy;
    }
    @RequestMapping(value = "/productsweb", method = RequestMethod.GET ,
        produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Resources<Resource<Product>>> getAllProducts() {
        return productServiceProxy.getAllProducts();
    }
    @RequestMapping(value = "/productsweb/{productId}", method =
        RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Resource<Product>> getProduct(
        @PathVariable("productId") String productId) {
        return productServiceProxy.getProduct(productId);
    }
}
Listing 8-2

Enable Feign Client (ch08\ch08-01\ProductWeb\src\main\java\com\acme\ecom\product\controller\ProductRestController.java)

ProductServiceProxy is injected into the code in Listing 8-2 in the Feign client proxy. Creating this REST proxy client is really easy, and most of the time all you need to do is create an interface and add some annotations. The Spring Cloud environment will create the implementation at runtime and make the call delegate work. Listing 8-3 shows the ProductServiceProxy in code.
import org.springframework.cloud.netflix.feign.FeignClient;
@FeignClient(name="product-proxy", url = "http://localhost:8080")
public interface ProductServiceProxy extends ProductService{
}
Listing 8-3

Feign REST Client (ch08\ch08-01\ProductWeb\src\main\java\com\acme\ecom\product\client\ProductServiceProxy.java)

I will not explain the code for the Product Server microservice since it is very similar to the sample I already explained in the “Develop a RESTful Web Service” section in Chapter 7.

Build and Test the Feign Client

The complete code required to demonstrate the Feign client is in folder ch08-01. Make sure MongoDB is up and running. You may want to refer to Appendix A to get started with MongoDB.

You first build and package the executables for the Product Server microservice and bring up the server. There is a utility script provided which you can easily execute in folder ch08\ch08-01\ProductServer\make.bat:
cd ch08\ch08-01\ProductServer
D:\binil\gold\pack03\ch08\ch08-01\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-01\ProductServer>mvn -Dmaven.test.skip=true clean package
You can run the Spring Boot application in more than one way. The straightforward way is to execute the JAR file via the following commands:
D:\binil\gold\pack03\ch08\ch08-01\ProductServer>run
D:\binil\gold\pack03\ch08\ch08-01\ProductServer>java -jar -Dserver.port=8080 ./target/Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
These commands will bring up the Product Server in port 8080. Note that an initialization component kept in the following place will pump a few Product instances into the MongoDB during startup, which will be handy for demonstrating your application later:
ch08\ch08-01\ProductServer\src\main\java\com\acme\ecom\productInitializationComponent.java
Next, build and package the executables for the Product Web microservice and bring up the server:
cd ch08\ch08-01\ProductWeb
D:\binil\gold\pack03\ch08\ch08-01\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-01\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-01\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-01\ProductWeb>java -jar -Dserver.port=8081 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar

Above command will bring up Product Web in port 8081

There is a quick utility provided in the following location to test the application:
ch08\ch08-01\ProductWeb\src\main\resources\product.html

Open this HTML utility preferably in the Chrome browser. Upon loading itself, the browser client will fire a request to the Product Web listening at port 8081, which will delegate the calls to the Product Server listening at 8080 by proxying through the Feign client.

If everything goes well, the browser widget will be filled with data from the back-end database (Figure 8-3). When you refresh the screen, the browser will hit the Product Web microservice first. The Product Web microservice will then call the Product Server microservice, which in turn will query the Mongo DB and return the results. The notable feature here is that in this demonstration you have created two different microservices and then one microservice made a call to the other microservice using Feign client. This is one of the ways by which microservices can communicate with each other using HTTP calls in a synchronous manner.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig3_HTML.jpg
Figure 8-3

Testing the Feign client

Hystrix Fallback

There are times when a particular microservice is busy—or a particular database, or for that matter any resource—so it is not in a position to respond back within the expected SLA. In such scenarios, the caller service cannot wait on the dependent service endlessly, so there should be alternate strategies. You’ll handle a scenario with the Hystrix circuit breaker falling back to an alternate strategy.

Circuit breakers can gracefully degrade functionality when a method call to a service or to a resource fails. Use of the circuit breaker pattern can allow a microservice to continue its operations when a dependent service fails. This will prevent the failure from cascading, thus giving the failing service enough time to recover.

Design a Hystrix Fallback Scenario

You are going to expand the sample from the previous section (illustrated in Figure 8-1). Figure 8-4 illustrates the simple scenario where there are three separate applications or, in our terms, three separate microservices: Product Web, Product Server 1, and Product Server 2.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig4_HTML.jpg
Figure 8-4

A Hystrix fallback usage scenario

The main components for the sample scenario are
  • Product Server 1

  • Product Server 2

  • Product Web

  • Product Service

  • ProductServiceProxy

  • ProductAlternateServiceComponent

  • ProductAlternateServiceProxy

  • Spring Cloud Feign Client

The explanation for many of the above components is the same as that provided in the “Feign Client” section earlier, so I won’t duplicate it here. However, I will explain the new components.
  • ProductAlternateServiceComponent: Product Web is designed to delegate all REST calls coming to it to the Product Server 1 microservice. However, if the Product Server 1 microservice is down or not responding for some reason, you can design a Hystrix callback to a fallback component named ProductAlternateServiceComponent. In real life, this can be a local cache from where you can respond to the client with alternate data. However, in your sample, you have already seen how a Feign client can be used to proxy calls to a remote service. So, you will have a ProductAlternateServiceProxy component to which calls can be proxied.

  • Product Server 2: Product Server 2 is a full-fledged microservice application duplicating the CRUD operations implemented in Product Server 1. This microservice again uses a Mongo DB to write to and read from the data pertaining to product entities. This microservice acts as an alternate microservice implementation and can serve requests if Product Server 1 is not responding.

  • ProductAlternateServiceProxy: The ProductAlternateServiceProxy component is another abstract interface that extends Product Service, so it gets all the methods expected out of both the Product Server and the Product Web interfaces. This is the interface you annotate with the Feign client essential configurations in such a way that all calls can be proxied to the Product Server 2 microservice.

You will split the component interaction into two, as shown in Figure 8-5 and Figure 8-6. In Figure 8-5, you can see that the query from the client-side browser hits Product Web, which is Microservice 1 in the sample. The Product Web microservice then proxies the query to Microservice 2, which is Product Server 1. If Product Server 1 is up and running, then the sample execution will end after responding back to the client with the response data, and so this sample will behave exactly like the previous sample from the “Feign Client” section. Instead, you can either not bring the Product Server 1 up or, if it’s already up, you can bring it down. In that case, Hystrix will retry to the fallback service configured.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig5_HTML.jpg
Figure 8-5

Hystrix detects service non-availability

Assuming the fallback microservice Product Server 2 is up, the query will be retried to hit this microservice and the execution will happen through the happy flow depicted in Figure 8-6.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig6_HTML.jpg
Figure 8-6

Hystrix retries the alternate service

Code the Hystrix Fallback Scenario

You have already seen how the Feign client is declared in your POM. Similarly, you can bring the Hystrix dependencies by declaring them again in the POM. All the code samples for this section are placed in folder ch08\ch08-02. Visit pom.xml to see the explicit mention of the Hystrix dependency. See Listing 8-4.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
Listing 8-4

Spring Cloud Hystrix Dependency (ch08\ch08-02\ProductWeb\pom.xml)

Next, you need to mention the Hystrix fallback. This is mentioned in the ProductServiceProxy; see Listing 8-5.
@FeignClient(name="product-proxy", url = "http://localhost:8080",
fallback = ProductAlternateServerComponent.class)
public interface ProductServiceProxy extends ProductService{
}
Listing 8-5

Hystrix Fallback Declaration (ch08\ch08-02\ProductWeb\src\main\java\com\acme\ecom\product\client\ProductServiceProxy.java)

Here you have instructed Hystrix to consider ProductAlternateServerComponent as the component to be retried as a fallback. Let’s now look at the ProductAlternateServerComponent code mentioned in Listing 8-6.
@EnableFeignClients(basePackageClasses = ProductAlternateServiceProxy.class)
@ComponentScan(basePackageClasses = ProductAlternateServiceProxy.class)
@Component
public class ProductAlternateServerComponent implements ProductServiceProxy{
private ProductAlternateServiceProxy productAlternateServiceProxy;
    @Autowired
    public ProductAlternateServerComponent(
            ProductAlternateServiceProxy productAlternateServiceProxy){
        this.productAlternateServiceProxy = productAlternateServiceProxy;
    }
    @Override
    public ResponseEntity<Resources<Resource<Product>>> getAllProducts() {
        return productAlternateServiceProxy.getAllProducts();
    }
    @Override
    public ResponseEntity<Resource<Product>> getProduct(
            @PathVariable("productId") String productId) {
        return productAlternateServiceProxy.getProduct(productId);
    }
}
Listing 8-6

Hystrix Fallback Implementation (ch08\ch08-02\ProductWeb\src\main/java\com\acme\ecom\product\component\ ProductAlternateServerComponent.java)

One approach is to have ProductAlternateServerComponent respond with some kind of locally cached data as a fallback. However, since you have an alternate microservice designated as a fallback service, you can leverage it here. You have also leveraged the Feign client to delegate the call to the Product Server 2 microservice. For this Feign client, you will make use of ProductAlternateServiceProxy with the code from Listing 8-7.
@FeignClient(name="product-alternate-proxy", url = "http://localhost:8079")
public interface ProductAlternateServiceProxy extends ProductService{
}
Listing 8-7

Feign Client Proxy to Alternate Microservice (ch08\ch08-02\ProductWeb\src\main\java\com\acme\ecom\product\client\ ProductAlternateServiceProxy.java)

Here you expect that the Product Server 2 microservice is available at http://localhost:8079, which will act as the fallback for the Product Server 1 microservice, which is expected to be available at http://localhost:8080.

You now need to enable Hystrix and configure its timeout interval, which you do in
ch08\ch08-02\ProductWeb\src\main\resources\application.properties:
feign.hystrix.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000

The rest of the code is similar to what you saw in the “Feign Client” section, so I won’t repeat the explanation here. Instead, let’s execute the code.

Build and Test the Hystrix Fallback Scenario

The complete code required to demonstrate the Hystrix fallback is kept inside folder ch08-02. Make sure MongoDB is up and running. You may want to refer to Appendix A to get started with MongoDB.

You first build and package the executables for the Product Server 1 microservice and bring up the server. A utility script that you can easily execute is in folder ch08\ch08-02\ProductServer\make.bat.
cd ch08\ch08-02\ProductServer
D:\binil\gold\pack03\ch08\ch08-02\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-02\ProductServer>mvn -Dmaven.test.skip=true clean package
You can run the Spring Boot application in more than one way. The straightforward way is to execute the JAR file via the following commands:
cd ch08\ch08-02\ProductServer
D:\binil\gold\pack03\ch08\ch08-02\ProductServer>run
D:\binil\gold\pack03\ch08\ch08-02\ProductServer>java -jar -Dserver.port=8080 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
These commands will bring up the Product Server in port 8080. You will now build and package the executables for the Product Server 2 microservice and bring up the server in a new command window:
cd ch08\ch08-02\ProductServerAlternate
D:\binil\gold\pack03\ch08\ch08-02\ProductServerAlternate>make
D:\binil\gold\pack03\ch08\ch08-02\ProductServerAlternate>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-02\ProductServerAlternate>run
D:\binil\gold\pack03\ch08\ch08-02\ProductServerAlternate>java -jar -Dserver.port=8079 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
Next, build and package the executables for the Product Web microservice and bring up the server:
cd ch08\ch08-02\ProductWeb
D:\binil\gold\pack03\ch08\ch08-02\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-02\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-02\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-02\ProductWeb>java -jar -Dserver.port=8081 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
These commands will bring up Product Web in port 8081. You may open this HTML utility preferably in the Chrome browser:
ch08\ch08-02\ProductWeb\src\main\resources\product.html

Upon loading itself, the browser client will fire a request to Product Web, which is listening on port 8081, and it will delegate the calls to Product Server 1 listening at 8080 by proxying through the Feign client.

Repeated browser refreshing will also point the hits to Product Server 1, which you can verify by looking at the log happening in its command window. To demonstrate Hystrix fallback, simply bring down Product Server 1 and refresh the browser. This time you will see the hits coming to Product Alternate Server. You can also see corresponding logs in the Product Web microservice console, as shown in Listing 8-8.
D:\binil\gold\pack03\ch08\ch08-02\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-02\ProductWeb>java -jar -Dserver.port=8081 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)
2019-02-21 16:53:59 INFO  org.springframework.boot.SpringApplication.logStartupProfileInfo:593 - No active profile set, falling back to default profiles: default
2019-02-21 16:54:10 INFO  com.acme.ecom.product.InitializationComponent.init:61- Start
2019-02-21 16:54:10 DEBUG com.acme.ecom.product.InitializationComponent.init:63- Doing Nothing...
2019-02-21 16:54:10 INFO  com.acme.ecom.product.InitializationComponent.init:65- End
2019-02-21 16:54:13 INFO  org.springframework.boot.StartupInfoLogger.logStarted:57 - Started EcomProductMicroserviceApplication in 25.369 econds (JVM running for 30.007)
2019-02-21 16:56:41 INFO  com.acme.ecom.product.controller.ProductRestController.getAllProducts:84 - Delegating...
2019-02-21 16:56:57 INFO  com.acme.ecom.product.controller.ProductRestController.getAllProducts:84 - Delegating...
2019-02-21 16:56:58 INFO  com.acme.ecom.product.component.ProductAlternateServerComponent.getAllProducts:78 - Delegating...
Listing 8-8

Hystric Fallback in Action

Hystrix Dashboard

A Hystrix dashboard can be used to provide a graphical overview of a circuit breaker in the system. In a typical microservices architecture, there will be more than one microservice processes and in such cases, you will need to monitor more than one circuit breaker in the application. For this, you can use Turbine, which is not explained here. In this section, you will see how to enable a Hystrix dashboard.

Redesign a Hystrix Fallback Method

You will now slightly modify the Feign and Hystrix design from the “Hystrix Fallback” section. The idea is to first demonstrate yet another way of defining a Hystrix fallback and afterwards demonstrate the Hystrix dashboard. The refactored design is shown in Figure 8-7.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig7_HTML.jpg
Figure 8-7

Refactored design for the Hystrix fallback

In the refactored design, all of the Feign client interactions have moved from the Controller to a Component class. So the Product Server Component now depends on the two proxies, which will get injected with Feign client proxies at runtime.

Code the New Design of Hystrix

All the code samples for this section are placed in folder ch08\ch08-03. The Hystrix dashboard application is yet another Spring Boot application. To enable it, you need to follow a few steps. Visit pom.xml to see the explicit mention of the Hystrix dashboard dependency in Listing 8-9.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Listing 8-9

Hystrix Dashboard Dependency (ch08\ch08-03\ProductWeb\pom.xml)

The first dependency needs to be enabled via annotating a @Configuration with @EnableHystrixDashboard and the latter dependency automatically enables the required metrics within your web-application microservice.

You then need to add the @EnableHystrixDashboard to your Spring Boot main class, as shown in Listing 8-10.
@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
@Configuration
public class EcomProductMicroserviceApplication {
    public static void main(String[] args) {
        SpringApplication.run(EcomProductMicroserviceApplication.class, args);
    }
}
Listing 8-10

Enable Hystrix Dashboard (ch08\ch08-03\ProductWeb\src\main\java\com\acme\ecom\product\EcomProductMicroserviceApplication.java)

The Rest Controller delegates all calls to ProductServerComponent, as shown in Listing 8-11.
@RestController
public class ProductRestController implements ProductService{
    private ProductServerComponent productServerComponent;
    @Autowired
    public ProductRestController(ProductServerComponent
            productServerComponent){
        this.productServerComponent = productServerComponent;
    }
    @RequestMapping(value = "/productsweb", method = RequestMethod.GET ,
            produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Resources<Resource<Product>>> getAllProducts() {
        return productServerComponent.getAllProducts();
    }
    @RequestMapping(value = "/productsweb/{productId}",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Resource<Product>> getProduct(
            @PathVariable("productId") String productId) {
        return productServerComponent.getProduct(productId);
    }
}
Listing 8-11

REST Controller (ch08\ch08-03\ProductWeb\src\main\java\com\acme\ecom\product\controller\ProductRestController.java)

As you can see in Listing 8-11, ProductRestController simply delegates the call to the ProductServerComponent. In ProductServerComponent, you mark the methods as HystrixCommand, and for each HystrixCommand you also define fallback methods, as shown in Listing 8-12.
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
@EnableFeignClients(basePackageClasses = {ProductServiceProxy.class,
    ProductAlternateServiceProxy.class})
@ComponentScan(basePackageClasses = {ProductServiceProxy.class,
        ProductAlternateServiceProxy.class})
public class ProductServerComponent implements ProductService{
    private ProductServiceProxy productServiceProxy;
    private ProductAlternateServiceProxy productAlternateServiceProxy;
@HystrixCommand(fallbackMethod = "getAllTheProducts")
    public ResponseEntity<Resources<Resource<Product>>> getAllProducts() {
        return productServiceProxy.getAllProducts();
    }
    @HystrixCommand(fallbackMethod = "getTheProduct")
    public ResponseEntity<Resource<Product>> getProduct(
            @PathVariable("productId") String productId) {
        return productServiceProxy.getProduct(productId);
    }
    public ResponseEntity<Resources<Resource<Product>>> getAllTheProducts() {
        return productAlternateServiceProxy.getAllProducts();
    }
    public ResponseEntity<Resource<Product>> getTheProduct(
            @PathVariable("productId") String productId) {
        return productAlternateServiceProxy.getProduct(productId);
    }
}
Listing 8-12

Hystrix Commands (ch08\ch08-03\ProductWeb\src\main\java\com\acme\ecom\product\component\ProductServerComponent.java)

As you can see, if Product Server 1 is down, a call to productServiceProxy.getAllProducts() will fail, and since you have declared getAllTheProducts() as the fallback method, a retry will happen with a call to productAlternateServiceProxy. The fallback method is used by Hystrix in case of an error (the call to the productServiceProxy service fails or a timeout occurs) or to fast fail if the circuit is open.

Build and Test the Hystrix Fallback Scenario

The complete code required to demonstrate the Hystrix fallback is kept inside folder ch08-03. Make sure MongoDB is up and running. You can then build, pack, and run the three microservices in the following order:
cd ch08\ch08-03\ProductServer
D:\binil\gold\pack03\ch08\ch08-03\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-03\ProductServer>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-03\ProductServer>run
D:\binil\gold\pack03\ch08\ch08-03\ProductServer>java -jar -Dserver.port=8080 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
cd ch08\ch08-03\ProductServerAlternate
D:\binil\gold\pack03\ch08\ch08-03\ProductServerAlternate>make
D:\binil\gold\pack03\ch08\ch08-03\ProductServerAlternate>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-03\ProductServerAlternate>run
D:\binil\gold\pack03\ch08\ch08-03\ProductServerAlternate>java -jar -Dserver.port=8079 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
cd ch08\ch08-03\ProductWeb
D:\binil\gold\pack03\ch08\ch08-03\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-03\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-03\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-03\ProductWeb>java -jar -Dserver.port=8081 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
The last command will bring up Product Web in port 8081. You may open this HTML utility preferably in the Chrome browser:
ch08\ch08-03\ProductWeb\src\main\resources\product.html

Upon loading itself, the browser client will fire a request to Product Web, listening in port 8081, which will delegate the calls to Product Server 1, listening at 8080, by proxying through the Feign client.

Repeated browser refreshing will also point the hits to Product Server 1, which you can verify by looking at the log happening in its command window. To demonstrate Hystrix fallback, simply bring down Product Server 1 and refresh the browser. This time you will see the hits coming to Product Server 2. You can also see corresponding logs in the Product Server microservice console.

Inspect Hystrix Dashboard

The Hystrix dashboard (Figure 8-8) is available at the following URL in your case:
http://localhost:8081/hystrix
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig8_HTML.jpg
Figure 8-8

Hystrix dashboard

The Hystrix dashboard will ask for the URL of a Hystrix stream. Every Hystrix-enabled application produces a stream where the status of all circuits is constantly written. The URL is of the form http://application-node:port/hystrix.stream, and in your case the URL is http://localhost:8081/hystrix.stream. The stream can be monitored from the dashboard, as shown in Figure 8-9.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig9_HTML.jpg
Figure 8-9

Stream monitoring in Hystrix dashboard

Ribbon, the Client-Side Load Balancer

Having looked at the Feign client and the Hystrix fallback, now is the right time to introduce the next important component in Spring Cloud: Ribbon. Ribbon is a client-side load balancer that gives you extra control over the behavior of HTTP and TCP clients. Feign already uses Ribbon, so understanding Ribbon is crucial.

Load balancing distributes incoming traffic between two or more microservices. It enables you to achieve fault tolerance in your microservice applications. Load balancing aims to optimize resource usage, maximize throughput, minimize response time, and avoid overloading any single microservice. Using multiple microservice instances of the same service with load balancing instead of a single instance may increase reliability and availability at the cost of redundancy.

Ribbon offers configuration options such as connection timeouts, retries, retry algorithms (exponential, bounded back off), load balancing, fault tolerance, support for multiple protocols (HTTP, TCP, UDP), support in an asynchronous and reactive model, caching, and batching.

Design a Ribbon Client Scenario

You will use the same design you used in the section for the Feign client. However, you will enhance the Feign client to internally leverage Ribbon. The design is shown in Figure 8-10.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig10_HTML.jpg
Figure 8-10

Design with Ribbon

You will use the same Product Server microservice module you used for previous sections in this chapter. In the previous sections, you also used the Product Server Alternate microservice module; however, in this section you will have only one module, the Product Server microservice module, but you will run two instances of it. This is important since I also want to demonstrate that a microservice can be instantiated to more than one in number (rather, replicated) to scale horizontally provided you have taken care of other design aspects like statelessness, idempotency, etc.

Code to Use Ribbon Client

All the code samples for this section are placed in folder ch08\ch08-04. Visit pom.xml to see the explicit mention of the Ribbon dependency. See Listing 8-13.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
Listing 8-13

Ribbon Dependency (ch08\ch08-04\ProductWeb\pom.xml)

The central concept of Ribbon is that of the named client. Each load balancer is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer (e.g. using the @FeignClient annotation ) as shown in Listing 8-14.
@FeignClient(name="product-proxy")
public interface ProductServiceProxy extends ProductService{
}
Listing 8-14

Named Client in Ribbon (ch08\ch08-04\ProductWeb\src\main\java\com\acme\ecom\product\client\ProductServiceProxy.java)

You now need to provide configuration values to Ribbon, which you will do in ch08\ch08-04\ProductWeb\src\main\resources\application.properties:
product-proxy.ribbon.listOfServers=localhost:8080,localhost:8081

This is it, and this will do all the required plumbing.

Build and Test the Ribbon Client

The complete code required to demonstrate Ribbon is kept inside folder ch08-04. Make sure MongoDB is up and running. You can then build, pack, and run the three microservices in the following order:
cd ch08\ch08-04\ProductServer
D:\binil\gold\pack03\ch08\ch08-04\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-04\ProductServer>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-04\ProductServer>run1
D:\binil\gold\pack03\ch08\ch08-04\ProductServer>java -jar -Dserver.port=8080 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
cd ch08\ch08-04\ProductServer
D:\binil\gold\pack03\ch08\ch08-04\ProductServer>run2
D:\binil\gold\pack03\ch08\ch08-04\ProductServer>java -jar -Dserver.port=8081 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
cd ch08\ch08-03\ProductWeb
D:\binil\gold\pack03\ch08\ch08-04\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-04\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-04\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-04\ProductWeb>java -jar -Dserver.port=8082 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
The last command will bring up Product Web in port 8082. You should have noted that the two instances of the Product Server microservices are running in port 8080 and 8081. This explains why you used the following configuration:
product-proxy.ribbon.listOfServers=localhost:8080,localhost:8081
You may now open this HTML utility preferably in the Chrome browser:
ch08\ch08-04\ProductWeb\src\main\resources\product.html

Upon loading itself, the browser client will fire a request to the Product Web listening in port 8082, which will delegate the call to any one instance of the Product Server microservice by proxying through the Feign client.

Repeated browser refreshing will load balance the hits from the Product Web microservice to either of the Product Server microservice instances, which you can verify by looking at the log happening in the command window of the Product Server microservice. If you bring down one of the Product Server microservices and refresh the browser, you will see that the hits will always go to the other Product Server instance running. If you reinstate the Product Server microservice you brought down earlier, the Ribbon load balancing will again come into action, alternating calls from the Product Web microservice to both instances of the Product Server microservice.

Eureka, the Service Registry

Eureka is a REST-based registry service that is primarily used in public cloud-based deployments, especially in the AWS cloud. Using Eureka you can locate microservices for the purpose of load balancing and failover. Eureka is Spring Cloud’s service registry, which acts as a phone book for your microservices. Each microservice self-registers with the service registry, telling the registry where it lives (host, node name, port, etc.) and perhaps other service-specific metadata. Eureka has two parts, the Eureka Server and a client component, the Eureka Client, which makes interactions with the registry service much easier. The client also has a built-in load balancer that does basic round-robin load balancing.

A service registry like Eureka has another feature that provides one or more levels of indirection for service consumers. This means service consumers can do a registry look-up using a logical service name, and this logical service name may be mapped to a different actual deployed service whose physical address entries are mapped against the above logical name. This insulates the service consumers from any changes happening to actual service details or whatever in the physical deployment environment. In the sample explained in the “Ribbon” section, you might have noticed hard-coded values for the service URL like:
localhost:8080,localhost:8081

This is not a good practice. A DNS (Domaine Name System ) is used to resolve similar problems; however, a DNS is comparatively heavy when you consider hundreds of microservices within an enterprise application. Further, you do not want to expose the server details of your microservices outside your perimeter or DMZ (Demilitarized Zone). Typically the service consumers in a microservice-based enterprise application are other microservices. Hence these microservice have to ask questions like service topology (“Are there any ‘product-services’ available, and if so, where?”) and service capabilities (“Can you handle A, B, and C?”). This is where service registries like Eureka and Consul come into play. Eureka does not impose any restrictions on the protocol or method of communication, so you can use Eureka to use protocols such as thrift, http(s) or any other RPC mechanisms.

You might still doubt the need for Eureka when there are already many software and hardware load balancers, including AWS Elastic Load Balancer and AWS Route 53. AWS Elastic Load Balancer is used to expose edge services, which typically connect to end-user web traffic whereas Eureka fills the need for mid-tier load balancing or, in our nomenclature, microservices registration and load balancing. Route 53 is analogous to a DNS service where you can host your DNS records even for non-AWS data centers, so it comes with the associated drawbacks of the traditional DNS-based load-balancing solutions where your traffic can still be routed to servers that may not be healthy or may not even exist, which is very typical in the case of public cloud-based deployments like AWS provisioned for auto scaling. Since you should prefer your microservices to be stateless (non-sticky), Eureka facilitates a much better scalability model since your microservices can be resilient to the outages of the load balancers (Eureka) because the information regarding the available servers is cached on the client. This does require a small amount of memory, but this increased resiliency doesn’t penalize the overall resource concerns much.

Design a Eureka-Enabled Scenario

You will modify the design by introducing few new components, as shown in Figure 8-11. Here you replace ProductServiceProxy with RestTemplate. This means the Product Web microservice will now use RestTemplate to consume any of the instances of the Product Server microservice. RestTemplate simplifies communication with HTTP servers, and the “Develop a RESTful Web Service” section in Chapter 7 already explained how to use RestTemplate to communicate with RestController. You will extend that here. Another intention of using RestTemplate is that you can use Ribbon indirectly via an autoconfigured RestTemplate when RestTemplate is on the classpath and a LoadBalancerClient bean is defined so that you build over the previous sample of Ribbon itself.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig11_HTML.jpg
Figure 8-11

Design with Eureka

By enabling the Eureka registry, all microservices will self-register to the registry when brought up. Further, in a previous sample you saw how to bring redundancy by instantiating the same Product Server microservice more than once. You will follow the same pattern for Eureka so that there is redundancy at the service registry level too.

Code to Use Eureka

All the code samples for this section are placed in folder ch08\ch08-05. Visit pom.xml to see the explicit mention of the Eureka Server dependency. See Listing 8-15.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
Listing 8-15

Eureka Dependency (ch08\ch08-05\Eureka\pom.xml)

Next, you need Spring Cloud’s @EnableEurekaServer to stand up a registry that other microservices can talk to. This is done in your regular Spring Boot application main class with one annotation added to enable the service registry. See Listing 8-16.
@EnableEurekaServer
@SpringBootApplication
public class EurekaRegistryApplication {
public static void main(String[] args) {
        SpringApplication.run(EurekaRegistryApplication.class, args);
    }
}
Listing 8-16

Enable Eureka Server (ch08\ch08-05\Eureka\src\main\java\com\acme\ecom\infra\EurekaRegistryApplication.java)

When Eureka comes up, it will attempt to register itself, so you need to disable that; see Listing 8-17.
spring.application.name=eureka-registry
server.port=8761
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=true
eureka.client.server.waitTimeInMsWhenSyncEmpty=0
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/
eureka.server.enableSelfPreservation=false
Listing 8-17

Eureka Configuration (ch08\ch08-05\Eureka\src\main\resources\application.properties)

If you want to bring multiple instances of Eureka registry with this single properties file, you need to override at least the first two parameters. You do so in the following two scripts:
ch08\ch08-05\Eureka\run1.bat
java -jar -Dserver.port=8761 -Dspring.application.name=eureka-registry1 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
ch08\ch08-05\Eureka\run2.bat
java -jar -Dserver.port=8762 -Dspring.application.name=eureka-registry2 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
Since you are dealing with HAL-based JSON data, you want to configure the REST template you want to use in the Product Web microservice to invoke calls in the Product Server microservice to deal with HAL-formatted data. See Listing 8-18.
@Configuration
public class RestTemplateConfiguration{
@LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate(getRequiredMessageConvertors());
    }
    private List<HttpMessageConverter getRequiredMessageConvertors(){
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
            false);
        mapper.registerModule(new Jackson2HalModule());
        MappingJackson2HttpMessageConverter converter =
            new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(MediaType.parseMediaTypes(
            "application/hal+json, application/json"));
        converter.setObjectMapper(mapper);
        return Arrays.asList(converter);
    }
}
Listing 8-18

Rest Template Configuration to Handle HAL JSON Format (ch08\ch08-05\ProductWeb\src\main\java\com\acme\ecom\product\controller\RestTemplateConfiguration.java)

The Product Server microservice and the Product Web microservice must register themselves with the registry and use the Spring Cloud DiscoveryClient abstraction to interrogate the registry for their own host and port. You can use the @EnableEurekaClient to activate the Netflix Eureka DiscoveryClient implementation. EnableEurekaClient is a convenience annotation for clients to enable the Eureka discovery configuration (specifically). This annotation turns on discovery and lets the autoconfiguration find the Eureka classes if they are available. See Listing 8-19.
@SpringBootApplication
@EnableEurekaClient
public class EcomProductMicroserviceApplication {
public static void main(String[] args) {
        SpringApplication.run(EcomProductMicroserviceApplication.class, args);
    }
}
Listing 8-19

Enable Eureka Client (ch08\ch08-05\ProductServer\src\main\java\com\acme\ecom\product\EcomProductMicroserviceApplication.java)

Visit pom.xml to see the explicit mention of the Eureka dependency. See Listing 8-20.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
Listing 8-20

Eureka Dependency (ch08\ch08-05\ProductServer\pom.xml)

You saw the ProductController code in the “Develop a RESTful Web Service” section in Chapter 7 when retrieving the product data from the database using ProductRepository, so I will not explain the ProductController in the Product Server microservice again. However, you need to inspect the full code of the ProductController used by the Product Web microservice, since this code demonstrates how to disassemble a HATEOAS-based HTTP response completely and next assemble it back again to create a HATEOAS-based HTTP response to return back. Listing 8-21 shows the code for getAllProducts() .
@CrossOrigin
@RestController
public class ProductRestController implements ProductService{
    @Autowired
    RestTemplate restTemplate;
    private static String PRODUCT_SERVICE_URL =
        "http://product-service/products";
    @Autowired
    public ProductRestController(RestTemplate restTemplate){
        this.restTemplate = restTemplate;
    }
    @RequestMapping(value = "/productsweb",
        method = RequestMethod.GET,
        produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Resources<Resource<Product>>> getAllProducts() {
        ParameterizedTypeReference<PagedResources<Product>>
            responseTypeRef =
                new ParameterizedTypeReference<PagedResources<Product>>() {};
        ResponseEntity<PagedResources<Product>> responseEntity =
            restTemplate.exchange(PRODUCT_SERVICE_URL, HttpMethod.GET,
             (HttpEntity<Product>) null, responseTypeRef);
        PagedResources<Product> resources = responseEntity.getBody();
        Collection<Product> products = resources.getContent();
        List<Product> productList = new ArrayList<Product>(products);
        Link links[] =
            {linkTo(methodOn(ProductRestController.class).getAllProducts()).
            withSelfRel(),linkTo(methodOn(ProductRestController.class).
            getAllProducts()).withRel("getAllProducts")};
        if(products.isEmpty()){
            return new ResponseEntity<Resources<Resource<Product>>>(
                HttpStatus.NOT_FOUND);
        }
        List<Resource<Product>> list = new ArrayList<Resource<Product>> ();
        for(Product product:products){
            list.add(new Resource<Product>(product,
                linkTo(methodOn(ProductRestController.class).
                getProduct(product.getId())).withSelfRel()));
        }
        Resources<Resource<Product>> productResponse =
            new Resources<Resource<Product>>(list, links) ;
        return new ResponseEntity<Resources<Resource<Product>>>(
            productResponse, HttpStatus.OK);
    }
}
Listing 8-21

Dissemble and Assemble getAllProducts HAL JSON Data (ch08\ch08-05\ ProductWeb\src\main\java\com\acme\ecom\product\controller\ProductRestController.java)

Listing 8-21 shows how to dissemble the Product entities you retrieved one by one, and further enrich them to be HAL-formatted data and assemble them to respond back to the client. Listing 8-22 shows similar code for a single product retrieval case.
public class ProductRestController implements ProductService{
    @RequestMapping(value = "/productsweb/{id}", method =
        RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Resource<Product>> getProduct(
            @PathVariable("id") String id) {
        Product product = restTemplate.getForObject(
            PRODUCT_SERVICE_URL + "/" + id, Product.class);
        if (product == null) {
            return new ResponseEntity<Resource<Product>>(
                HttpStatus.NOT_FOUND);
        }
        Resource<Product> productResponse = new Resource<Product>(
            product, linkTo(methodOn(ProductRestController.class).
            getProduct(product.getId())).withSelfRel());
        return new ResponseEntity<Resource<Product>>(
            productResponse, HttpStatus.OK);
    }
}
Listing 8-22

Dissemble and Assemble getProduct HAL JSON Data

The rest of the code is more or less the same as you have seen in earlier samples, so it won’t be explained.

Build and Test the Eureka Sample

The complete code required to demonstrate the Eureka registry is kept inside folder ch08-05. Make sure MongoDB is up and running. You can then build, pack, and run the different microservices in the following order:

You first bring up the Eureka registry microservices:
cd ch08\ch08-05\Eureka
D:\binil\gold\pack03\ch08\ch08-05\Eureka>make
D:\binil\gold\pack03\ch08\ch08-05\Eureka>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-05\Eureka>run1
D:\binil\gold\pack03\ch08\ch08-05\Eureka>java -jar -Dserver.port=8761 -Dspring.application.name=eureka-registry1 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
When the first Eureka starts up, it will complain with a stack trace in the console, since the registry cannot find a replica node to connect to. In a production environment, you will want more than one instance of the registry. For your sample purposes, you will bring up one more instance of Eureka.
D:\binil\gold\pack03\ch08\ch08-05\Eureka>run2
D:\binil\gold\pack03\ch08\ch08-05\Eureka>java -jar -Dserver.port=8762 -Dspring.application.name=eureka-registry2 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar

Note in the Eureka console windows that both instances sync with each other. You may need to wait few seconds before you notice this synchronization. Also note that this kind of delay is characteristic at different stages of the demonstration of this sample, since registry registration, look-ups, and synchronizations all have little delays (which you can control by tweaking the configurations).

Since the registry is all set up now, you can bring up the application microservices next:
cd ch08\ch08-05\ProductServer
D:\binil\gold\pack03\ch08\ch08-05\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-05/ProductServer>mvn -Dmaven.test.skip=true clean package
D:/binil/gold/pack03/ch08/ch08-05/ProductServer>run1
D:/binil/gold/pack03/ch08/ch08-05/ProductServer>java -Dserver.port=8080 -Dspring.application.name=product-service -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/ -jar ./target/Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
Listing 8-23 provides a glimpse of what should happen in the Eureka console when you start the microservices.
D:\binil\gold\pack03\ch08\ch08-05\Eureka>run1
D:\binil\gold\pack03\ch08\ch08-05\Eureka>java -jar -Dserver.port=8761 -Dspring.application.name=eureka-registry1 .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
2019-02-21 19:09:38.836  INFO 16672 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@28ba21f3: startup date [Thu Feb 21 19:09:38 IST 2019]; root of context hierarchy
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.4.RELEASE)
2019-02-21 19:10:59.615  INFO 16672 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2019-02-21 19:10:59.633  INFO 16672 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : The response status is 200
2019-02-21 19:10:59.646  INFO 16672 --- [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry  : Running the evict task with compensationTime 0ms
2019-02-21 19:11:08.318  INFO 16672 --- [nio-8761-exec-4] c.n.e.registry.AbstractInstanceRegistry  : Registered instance PRODUCT-SERVICE/tiger:product-service:8080 with status UP (replication=true)
2019-02-21 19:11:29.675  INFO 16672 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : The response status is 200
2019-02-21 19:11:41.187  INFO 16672 --- [nio-8761-exec-8] c.n.e.registry.AbstractInstanceRegistry  : Registered instance PRODUCT-SERVICE/tiger:product-service:8081 with status UP (replication=true)
2019-02-21 19:11:59.647  INFO 16672 --- [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry  : Running the evict task with compensationTime 0ms
cd ch08\ch08-05\ProductServer
D:\binil\gold\pack03\ch08\ch08-05\ProductServer>run2
D:\binil\gold\pack03\ch08\ch08-05\ProductServer>java -Dserver.port=8081 -Dspring.application.name=product-service -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/ -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
Listing 8-23

Microservices Registering with Eureka

Note that unlike in the case of Eureka instances, you have started all instances of the Product Server microservice with the same application name. This is to make explicit to the registry that both instances in fact point to the same microservice. This will help Eureka to route to any one of these instances when a look-up comes to this microservice.

Next, bring up the Product Web microservice:
cd ch08\ch08-05\ProductWeb
D:\binil\gold\pack03\ch08\ch08-05\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-05\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold/pack03/ch08/ch08-05/ProductWeb>run
D:/binil/gold/pack03/ch08/ch08-05/ProductWeb>java -Dserver.port=8082 -Dspring.application.name=product-web -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/ -jar ./target/Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar

The last command will bring up Product Web in port 8082.

You may inspect the Eureka console using a web browser by pointing to the following URLs where you have the registry services running:
http://localhost:8761/
http://localhost:8762/
See Figure 8-12.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig12_HTML.jpg
Figure 8-12

The Eureka Console

You may now open this HTML utility preferably in the Chrome browser: ch08\ch08-05\ProductWeb\src\main\resources\product.html.

Upon loading itself, the browser client will fire a request to Product Web, listening on port 8082, which will delegate the call to any one instance of the Product Server microservice.

Repeated browser refreshing will load balance the hits from the Product Web microservice to the Product Server microservice instances, which you can verify by looking at the log happening in the command window of the Product Server microservices. If you bring down one of the Product Server microservices and refresh the browser, you will see that the hit will always go to the other Product Server instance running. This is because you use @LoadBalanced for the RestTemplate, which you use to hit the Product Server microservice from the Product Web microservice, as shown in Listing 8-18. If you can bring back the Product Server microservice you brought down earlier, the Ribbon load balancing will again come into action, alternating calls from the Product Web microservice to both instances of the Product Server microservice. Similarly, you may also want to test by bringing down one of the Eureka microservices. Eureka clients are built to handle the failure of one or more Eureka peers in the Eureka farm. Since Eureka clients have the registry information cached with them, they can operate reasonably well, even when all of the available Eureka peers goes down.

Bootstrap Your Bootstrap Server

You should understand by now that every service instance in a microservice-based enterprise application can be registered with the Eureka registry, and this is the case with Eureka registry instances. There is one exception to this, which is the API gateway, which you will look at the next section; until then, ignore this exception. I have said that the Eureka registry is the directory of services for any interested service consumer, so if the service consumer can somehow reach the registry with the name of the service it wants to consume, the registry will provide the information (host, port, etc.) on how to reach that service. Now the golden question is, how does any service consumer reach a registry to start with? In other words, the Eureka registry will help any microservice to bootstrap and advertise itself. But how can a Eureka registry ever advertise itself? How does the bootstrap server bootstrap itself?!

You need a standard set of well-identifiable addresses for Eureka servers. One mechanism is to have an internal DNS or similar service. However, such pinned (IP) addresses will only work in a traditional LAN environment. The moment we talk about deployment in a public cloud like AWS, the scenario changes. In a cloud like AWS, instances come and go. This means you cannot pin Eureka servers with a standard hostname or an IP address. Mechanisms like AWS EC2 Elastic IP addresses must be used here. An Elastic IP address is a static IPv4 address designed for dynamic cloud computing. With an Elastic IP address, you can mask the failure of a microservice by rapidly remapping the address to another instance in your account. You need one Elastic IP for every Eureka server in the cluster. Once you configure your Eureka Server with the list of Elastic IP addresses, the Eureka server deals with the hassle of finding the Elastic IP that is unused and binds it to itself during the start up.

Zuul, the API Gateway

Any service needs to be reached by using an address—more correctly, using a well-known address. Addresses like www.google.com or www.apple.com are examples. Similarly, when you host an enterprise application, it will need to have an address, specifically a home page URL. Such an URL when typed in a browser will lead your request to the landing resource in your web site. Typically, these names or well-known URLs are DNS resolved and directed to a physical IP or a combination of physical IPs. When you deploy applications in a cluster or farm for scalability reasons, you also need a load balancer, which will act as a reverse proxy where the request will land first and then will be load balanced to any one instance in the farm. Apache HTTP server and F5 are similar devices. AWS ELB (Elastic Load Balancer) is a load balancing solution for web services exposed to end-user traffic in an AWS cloud scenario. In all these cases, once you advertise the publicly exposed IP, from there onwards the internal details of the enterprise application, including the network and deployment details, are hidden from the outside world. This is a recommended pattern and you will follow the same in your microservice applications.

The Bootstrap URL

In the previous section, you saw that every microservice can self-register to the Eureka registry as and when they come up so that consumers can be directed to those services by the registry. In order to discover where the registry is, you will use well-known and fixed addresses for the registries. These registries only contain route mappings to internal microservices. Externally accessible resources and addresses are typically not indexed in the registry; instead, you need to expose them using other ways. DNS is one such mechanism. But you also know that subsequent queries or requests arising from those publicly accessible resources (like many AJAX calls from the home page and other pages) need to access those microservices. You require a mechanism to do this. This is where the API gateway will come into play. A set of microservices can be exposed to the clients from outside the perimeter using an API gateway whereas the rest of the microservices, which you don’t want to expose, can still be kept unexposed. So your API gateway will become the bootstrap mechanism for those URLs by which the client accesses the publicly exposed microservices.

Figure 8-13 depicts how to set up an API gateway in an on-premises deployment. The “Mesh App and Services Architecture” section in Chapter 4 talked about the Mesh App and Services Architecture and I will extend the notion of MASA to accommodate the API gateway. There are many concerns to be addressed at the point of the API gateway in every enterprise application, and security is one such concern. This can all be addressed at the API gateway since the gateway will act as the single front door to your enterprise application landscape.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig13_HTML.jpg
Figure 8-13

Zuul, the API gateway on premises

The following points are to be noted when you use an API gateway:
  • Public services alone are bound to an API gateway. This means all microservice APIs that are to be addressable from outside the enterprise DMZ will be bound to the API gateway.

  • For microservices that are not necessarily accessible from outside, there is no need to expose them at the API gateway.

In Figure 8-13, you can see that all microservices are registered at Eureka. Then, all microservices that are to be accessible from outside are bound to the API gateway too. However, microservices that are not necessarily accessible from outside, like Microservice 2, are not bound to the API gateway. The next notable aspect is that the API gateway itself should be addressable from outside. So is the case with all apps, since apps are to be accessible directly by client devices. In Figure 8-13, you can see the apps domain where we typically place all components addressable and accessible from outside the DMZ. Typically this domain is composed of web apps, mobile apps, agents destined for IoT devices, etc. Registering the apps and the API gateway to a suitable external naming service makes this possible, and DNS is one way of doing this. Any such publically accessible resources will be registered in the public DNS and hence their addresses will be resolved through DNS lookup. A successful DNS lookup will provide the actual URL for the resource to the client device so that the client device can request the resource and get back the response, which will get rendered. This is shown in Figure 8-14.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig14_HTML.jpg
Figure 8-14

Bootstraping the URL to a microservice application

Once you receive the app content (the home page in a traditional web app context) and the same is rendered in the device, from then onwards all the rest of the hits from the client device to the server can be routed to one or more context root URLs. This means all subsequent requests from the app in the client device will hit the API gateway, as shown in Figure 8-15. This is marked with labels in the diagram. Since the API gateway is also publicly exposed, it makes sense to get its IP resolved through a naming service like DNS or alternatively the client app itself can remember the IP of the API gateway and hit (Label 7). The API gateway will first do a registry look up (Label 9) and subsequently route the request to the destination microservice (Label 10). Zuul by default uses Ribbon to locate an instance to forward the request to via discovery (e.g. Eureka in your case). So if your services are registered in Eureka, Zuul can also take care of instance lookup and carry the load balanced request forward.

Let’s extend the scenario to one more level. Assume that Microservice 1 has to internally call Microservice 2 to access some functionality. Figure 8-13 shows that Microservice 2 is not bound to the API gateway since it is an always internally accessible microservice; however, it too is registered with a name in Eureka. Microservice 1 can then use Feign client or a similar mechanism to do a Eureka look up and also do a Ribbon-based load balance so that it can route the call to Microservice 2 (Label 12).
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig15_HTML.jpg
Figure 8-15

Request from the client app to microservices

You need to appreciate that the deployment architectures explained here are typical ones, and there can be multiple variations based on your enterprise requirements.

Design a Zuul-Enabled Scenario

You will modify your design in Figure 8-13 by introducing the Zuul API gateway; the modified design is shown in Figure 8-16. The flow is as explained in the previous section; however, I will not introduce all the complexities of DNS, etc. here. Let’s look at the flow.

The client device, which is the browser in your case, will be used to render the product.html (Label 1). Once the product.html is loaded into the browser, it will fire a request to the Product Web microservice. You will not bind the Product Web microservice into the API gateway in this sample to keep it simple, so you will only bind the Product Server microservice to the API gateway. Hence the request from the client device to the Product Web microservice is a direct hit not through the API gateway (Label 3). The Product Web microservice now has to delegate the request to the Product Server microservice and in order to do so, it will route the request through the API gateway (Label 4). The API gateway will do a registry look up (Label 5) and return the server details where the Product Server microservices are hosted back to the Ribbon client in the Product Web microservice. The Product Web microservice takes this info and load balances the request to any one of the instances of the Product Server microservice (Label 6), since you will have two instances of Product Server microservices hosted.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig16_HTML.jpg
Figure 8-16

Design with Zuul

Code to Use Zuul

All the code samples for this section are in folder ch08\ch08-06.Visit pom.xml to see the explicit mention of the Zuul dependency. See Listing 8-24.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
Listing 8-24

Zuul dependency (ch08\ch08-06\ProductApiZuul\pom.xml)

Note that Zuul by itself is a microservice, so it has to register itself to the registry just like any other normal microservice. Hence Zuul also needs a Eureka dependency, as shown in Listing 8-25.

Next, you need Spring Cloud’s @EnableZuulProxy to stand up an API gateway that other microservices can bind to. This is done in your regular Spring Boot application main class with one annotation added to enable the Zuul gateway.
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ProductServerApiApplication {
public static void main(String[] args) {
        SpringApplication.run(ProductServerApiApplication.class, args);
    }
}
Listing 8-25

Enable Zuul (ch08\ch08-06\ProductApiZuul\src\main\java\com\acme\ecom\infra\ProductServerApiApplication.java)

You now need to define the routes at the API gateway, which you will do in application.yml. See Listing 8-26.
spring:
    application:
        name: product-service-api
server:
    port: 8082
zuul:
routes:
     product-api:
        path: /api/**
        service-id: product-service
eureka:
    client:
        serviceUrl:
            defaultZone: http://localhost:8761/eureka/
hystrix:
    command:
        default:
            execution:
                isolation:
                    thread:
                        timeoutInMilliseconds: 2000
Listing 8-26

Define Routes in Zuul Gateway (ch08\ch08-06\ProductApiZuul\src\main\resources\application.yml)

Zuul will now get registered in Eureka in the name product-service-api. Further, any URLs of the pattern /api/** coming to Zuul will get routed to product-service.

The Product Server microservice code is similar to the code you saw in the “Code to Use Eureka” section except that you have removed the MongoDB dependency and instead use a simple in-memory representation of product data to make the sample simpler. However, the Product Web microservice code needs explanation since it is in this place where you look up the Zuul API gateway to delegate requests to the Product Server microservice. See Listing 8-27.
@RestController
public class ProductRestController implements ProductService{
    @Autowired
    RestTemplate restTemplate;
    private static String PRODUCT_SERVICE_URL =
        "http://product-service-api/api/products";
    @Autowired
    public ProductRestController(RestTemplate restTemplate){
        this.restTemplate = restTemplate;
    }
    @RequestMapping(value = "/productsweb",
        method = RequestMethod.GET ,
        produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Resources<Resource<Product>>> getAllProducts() {
        ResponseEntity<PagedResources<Product>> responseEntity =
            restTemplate.exchange(PRODUCT_SERVICE_URL, HttpMethod.GET,
            (HttpEntity<Product>) null, responseTypeRef);
            // other code goes here…
    }
}
Listing 8-27

Use API Gateway to Call Other Microservices (ch08\ch08-06\ProductWeb\src\main\java\com\acme\ecom\product\controller\ProductRestController.java)

All of the other code remains similar to what you saw in the “Code to Use Eureka” section; however, the change is in the URL. The product-service-api portion of the URL http://product-service-api/api/products refers to the Zuul API gateway’s address, so the call will first hit the gateway. In the API gateway you have configured that any URLs of the pattern /api/** coming to Zuul will get routed to product-service, hence the above full URL will get translated to the products end point in the Product Server microservice.

Build and Test the Zuul Sample

The complete code required to demonstrate the Eureka registry is in folder ch08-06. You don’t require MongoDB for this sample. You can build, pack, and run the different microservices in the following order. You first bring up the Eureka registry microservices:
cd ch08\ch08-06\Eureka
D:\binil\gold\pack03\ch08\ch08-06\Eureka>make
D:\binil\gold\pack03\ch08\ch08-06\Eureka>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-06\Eureka>run
D:\binil\gold\pack03\ch08\ch08-06\Eureka>java -jar -Dserver.port=8761 -Dspring.application.name=eureka-registry -Deureka.client.registerWithEureka=false -Deureka.client.fetchRegistry=true -Deureka.client.server.waitTimeInMsWhenSyncEmpty=0 -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ -Deureka.server.enableSelfPreservation=false .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
You next bring up the Zuul API gateway:
D:\binil\gold\pack03\ch08\ch08-06\ProductApiZuul>make
D:\binil\gold\pack03\ch08\ch08-06\ProductApiZuul>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-06\ProductApiZuul>run
D:\binil\gold\pack03\ch08\ch08-06\ProductApiZuul>java -Dserver.port=8082 -Dspring.application.name=product-service-api -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ -jar ./target/Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
Since the registry and API gateway are all set up now, you can bring up the application microservices next:
cd ch08\ch08-06\ProductServer
D:\binil\gold\pack03\ch08\ch08-06\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-06\ProductServer>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-06\ProductServer>run1
D:\binil\gold\pack03\ch08\ch08-06\ProductServer>java -Dserver.port=8080 -Dspring.application.name=product-service -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
cd ch08\ch08-06\ProductServer
D:\binil\gold\pack03\ch08\ch08-06\ProductServer>run2
D:\binil\gold\pack03\ch08\ch08-06\ProductServer>java -Dserver.port=8081 -Dspring.application.name=product-service -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
Next, bring up the Product Web microservice:
cd ch08\ch08-06\ProductWeb
D:\binil\gold\pack03\ch08\ch08-06\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-06\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-06\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-06\ProductWeb>java -Dserver.port=8084 -Dspring.application.name=product-web -Deureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar

The last command brings up Product Web in port 8084.

You may inspect the Eureka console using a web browser by pointing to the following URL where you have the registry services running: http://localhost:8761/. See Figure 8-17.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig17_HTML.jpg
Figure 8-17

Eureka Console

You may now open this HTML utility, preferably in the Chrome browser: ch08\ch08-06\ProductWeb\src\main\resources\product.html.

Upon loading itself, the browser client will fire a request to Product Web, listening in port 8084, which will delegate the call to any instance of the Product Server microservice. The Product Web microservice now has to delegate the request to Product Server microservice and in order to do so, it will route the request through the API gateway. The API gateway will do a registry look up and return the server details where the Product Server microservices are hosted back to the Ribbon client in Product Web microservice. The Product Web microservice takes this info and load balances the request to any one of the instances of the Product Server microservices.

The Config Server

Externalizing configuration parameters is a critical characteristic that microservices exhibit. This is true for both application parameters and infrastructure parameters. When there are many microservices, maintaining the configuration parameters externally is a non-trivial task and Spring Cloud’s Config Server comes to your rescue here.

The Spring Config Server maintains configuration parameters in a version-controlled repository like SVN or Git. Again, the repository can be local or remote, and in single node or in multinode configurations for high availability. Since the configuration parameters can be version controlled and then referred directly by the Production servers, scenarios of run time errors due to incorrect values and such can be avoided completely.

Design a Configuration Scenario

You will introduce Config Server into your sample without doing many changes to the other microservices. In your design, you will utilize Config Server only for the Product Server microservices, even though it can be utilized for all other microservices. As shown in Figure 8-18, the Product Server microservice utilizes the Spring Cloud Config Server to manage the values for one of its configuration parameters.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig18_HTML.jpg
Figure 8-18

Design for a Config Server scenario

Code to Use Config Server

All the code samples for this section are in folder ch08/ch08-07. Visit pom.xml to see the explicit mention of the Config Server dependency. See Listing 8-28.
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>
Listing 8-28

Config Server Dependency (ch08\ch08-07\ConfigServer\pom.xml)

Next, you want to add @EnableConfigServer to the Application class, which is shown in Listing 8-29.
@SpringBootApplication
@EnableConfigServer
public class EcomProductConfigApplication {
public static void main(String[] args) {
        SpringApplication.run(EcomProductConfigApplication.class, args);
    }
}
Listing 8-29

Enabling Config Server (ch08\ch08-07\ConfigServer\src\main\java\com\acme\ecom\product\EcomProductConfigApplication.java)

Next, configure the Config Server itself to point to the GIT URL. See Listing 8-30.
server:
  port: 8888
spring:
  cloud:
    config:
      server:
        git:
          uri: file://D:/binil/gold/pack03/ch08/ch08-07/ConfigServer/config-repo
Listing 8-30

Spring Cloud Config Server Configurations (ch08\ch08-07\ConfigServer\src\main\resources\bootstrap.yml)

Here, since Port 8888 is the default port for the Config Server, even without explicit mention of server.port, the Config Server will bind to 8888. Next, you provide the GIT URL. It’s in this repository where you need to place all your configuration files for the different microservices.

Next, you need to enable your microservice to access the Config Server. Visit the Maven dependency shown in Listing 8-31.
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Listing 8-31

Config Server Client (ch08\ch08-07\ProductServer\pom.xml)

The Product Server microservice will act as the client for the Config Server. This is enabled by the spring-cloud-starter-config . You need to note the actuator dependency too, which is required for refreshing the configuration parameters when there is a change to the parameter in the Config Server. Next, inspect the microservice configuration file, shown in Listing 8-32.
server:
  port: 8081
spring:
  application:
    name: productservice
  cloud:
    config:
      server:
        uri: http://localhost:8888
management:
  security:
    enabled: false
Listing 8-32

Product Server Microservice Configurations (ch08\ch08-07\ProductServer\src\main\resources\application.yml)

The significant configuration parameters are
spring.application.name=productservice
spring.cloud.config.uri=http://localhost:8888
Here productservice is a logical name given to the Product Server microservice, which will also be treated as its service ID. The Config Server will look for productservice.yml in the repository to resolve any application configuration parameters. See Listing 8-33.
page:
  size: 3
Listing 8-33

Product Server Configuration Parameters (ch08\ch08-07\ConfigServer\config-repo\productservice.yml)

You will see the relevance of the configuration parameter in Listing 8-33 in the next section. You may now move onto the Product Server microservice code shown in Listing 8-34.
@RestController
@RefreshScope
public class ProductRestController {
    @Value("${page.size}")
    private int size = 0;
    // other code goes here…
}
Listing 8-34

Config Server Client (ch08\ch08-07\ProductServer\src\main\java\com\acme\ecom\product\controller\ProductRestController.java)

For demonstrating the centralized configuration of parameters and propagation of their changes, you have introduced an application-specific parameter named size, which is referred from the externalized configuration by the name page.size. In the application code, you use this parameter to control the number of rows of products returned when someone accesses them using the client.

Build and Test the Config Server

The complete code required to demonstrate the Config Server is in folder ch08-07. You don’t require MongoDB for this sample too. You can build, pack, and run the different microservices in the following order.

You first bring up the Config Server microservices:
cd ch08\ch08-07\ConfigServer
D:\binil\gold\pack03\ch08\ch08-07\ConfigServer>make
D:\binil\gold\pack03\ch08\ch08-07\ConfigServer>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-07\ConfigServer>run
D:\binil\gold\pack03\ch08\ch08-07\ConfigServer>java -Dserver.port=8888 -Dspring.application.name=productservice -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar

Once the Config Server is up, the configuration parameters in productservice.yml listed in Listing 8-33 can be inspected by typing http://localhost:8888/productservice/default.

The first part in the URL is the microservice name. In your case, the microservice name is productservice. The microservice name is a logical name given to the application, using the spring.application.name property in application.yml of the Spring Boot (Product Server) application. Each application should have a unique name. The Config Server will use this name to resolve and pick up the appropriate .yml or .properties files from the Config Server repository. The application name is also referred to as a service ID. See Figure 8-19.
../images/477630_1_En_8_Chapter/477630_1_En_8_Fig19_HTML.jpg
Figure 8-19

Inspecting the Config Server

Bring up the application microservices next:
cd ch08\ch08-07\ProductServer
D:\binil\gold\pack03\ch08\ch08-07\ProductServer>make
D:\binil\gold\pack03\ch08\ch08-07\ProductServer>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-07\ProductServer>run
D:\binil\gold\pack03\ch08\ch08-07\ProductServer>java -Dserver.port=8081 -Dspring.application.name=productservice -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar
cd ch08\ch08-07\ProductWeb
D:\binil\gold\pack03\ch08\ch08-07\ProductWeb>make
D:\binil\gold\pack03\ch08\ch08-07\ProductWeb>mvn -Dmaven.test.skip=true clean package
D:\binil\gold\pack03\ch08\ch08-07\ProductWeb>run
D:\binil\gold\pack03\ch08\ch08-07\ProductWeb>java -Dserver.port=8080 -Dspring.application.name=productweb -jar .\target\Ecom-Product-Microservice-0.0.1-SNAPSHOT.jar

Open this HTML utility, preferably in the Chrome browser: ch08\ch08-07\ProductWeb\src\main\resources\product.html.

Upon loading itself, the browser client will fire a request to Product Web, listening on port 8080, which will delegate the call to the Product Server microservice. You can see as many Product rows as configured in the productservice.yml listed in the client browser.

As a next step, change the value of page.size productservice.yml file (from 3 to 2 or 4) and save the file. You should be able to view this change in the Config Server by typing the Config server URL in a browser: http://localhost:8888/productservice/default.

You may now refresh the browser client. However, the change may not be reflected in the Product Server microservice. In order to force the Product Server microservice to reload the configuration parameters, call the /refresh endpoint of the Product Server microservice. This is the actuator’s refresh endpoint. The following command will send an empty POST to the /refresh endpoint:
curl –d {} localhost:8081/refresh

Alternatively, you may also use Postman to send an empty POST request. If you refresh the browser client again, you will see that the change is reflected. The /refresh endpoint will refresh the locally cached configuration parameters of the Product Server microservice and reload1 fresh values from the Config Server.

Summary

You explored the major building blocks of Spring Cloud in this chapter. Since Spring Cloud provides simple, easy-to-use, and Spring-friendly APIs and further since it’s built on Spring’s “convention over configuration” approach, Spring Cloud defaults all configurations and helps you to get off to a quick start. Many of the Spring Cloud features will be used to build your e-commerce sample application in later chapters, so the introductory level of knowledge provided in this chapter is essential for you to connect multiple microservices and visualize the complete picture. As you may have surmised, many of the Spring Cloud components are built from the ground up with the characteristics of fail safe and high availability in mind. This is of priority with increased outer architecture complexity for the microservices scenarios and you will do an end-to-end analysis of various essential high availability aspects and features in the next chapter.