An Intro to Spring Cloud Zookeeper

1. Introduction

In this article, we will get acquainted with Zookeeper and how it’s used for Service Discovery which is used as a centralized knowledge about services in the cloud.

Spring Cloud Zookeeper provides Apache Zookeeper integration for Spring Boot apps through autoconfiguration and binding to the Spring Environment.

2. Service Discovery Setup

We will create two apps:

  • An app that will provide a service (referred to in this article as the Service Provider)
  • An app that will consume this service (called the Service Consumer)

Apache Zookeeper will act as a coordinator in our service discovery setup. Apache Zookeeper installation instructions are available at the following link.

3. Service Provider Registration

We will enable service registration by adding the spring-cloud-starter-zookeeper-discovery dependency and using the annotation @EnableDiscoveryClient in the main application.

Below, we will show this process step-by-step for the service that returns “Hello World!” in a response to GET requests.

3.1. Maven Dependencies

First, let’s add the required spring-cloud-starter-zookeeper-discoveryspring-webspring-cloud-dependencies and spring-boot-starter dependencies to our pom.xml file:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
	<version>2.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
        <version>5.1.14.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
     </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.2. Service Provider Annotations

Next, we will annotate our main class with @EnableDiscoveryClient. This will make the HelloWorld application discovery-aware:

@SpringBootApplication
@EnableDiscoveryClient
public class HelloWorldApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

And a simple controller:

@GetMapping("/helloworld")
public String helloWorld() {
    return "Hello World!";
}

3.3. YAML Configurations

Now let us create a YAML Application.yml file that will be used for configuring the application log level and informing Zookeeper that the application is discovery-enabled.

The name of the application with which gets registered to Zookeeper is the most important. Later in the service consumer, a feign client will use this name during the service discovery:

spring:
  application:
    name: HelloWorld
  cloud:
    zookeeper:
      discovery:
        enabled: true
logging:
  level:
    org.apache.zookeeper.ClientCnxn: WARN

The spring boot application looks for zookeeper on default port 2181. If zookeeper is located somewhere else, the configuration needs to be added:

spring:
  cloud:
    zookeeper:
      connect-string: localhost:2181

4. Service Consumer

Now we will create a REST service consumer and registered it using Spring Netflix Feign Client.

4.1. Maven Dependency

First, let’s add the required spring-cloud-starter-zookeeper-discoveryspring-webspring-cloud-dependenciesspring-boot-starter-actuator and spring-cloud-starter-feign dependencies to our pom.xml file:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>2.2.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

4.2. Service Consumer Annotations

As with the service provider, we will annotate the main class with @EnableDiscoveryClient to make it discovery-aware:

@SpringBootApplication
@EnableDiscoveryClient
public class GreetingApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(GreetingApplication.class, args);
    }
}

4.3. Discover Service With Feign Client

We will use the Spring Cloud Feign Integration, a project by Netflix that lets you define a declarative REST Client. We declare how the URL looks like and feign takes care of connecting to the REST service.

The Feign Client is imported via the spring-cloud-starter-feign package. We will annotate a @Configuration with @EnableFeignClients to make use of it within the application.

Finally, we annotate an interface with @FeignClient(“service-name”) and auto-wire it into our application for us to access this service programmatically.

Here in the annotation @FeignClient(name = “HelloWorld”), we refer to the service-name of the service producer we previously created.

@Configuration
@EnableFeignClients
@EnableDiscoveryClient
public class HelloWorldClient {
 
    @Autowired
    private TheClient theClient;

    @FeignClient(name = "HelloWorld")
    interface TheClient {
 
        @RequestMapping(path = "/helloworld", method = RequestMethod.GET)
        @ResponseBody
	String helloWorld();
    }
    public String HelloWorld() {
        return theClient.HelloWorld();
    }
}

4.4. Controller Class

The following is the simple service controller class that will call the service provider function on our feign client class to consume the service (whose details are abstracted through service discovery) via the injected interface helloWorldClient object and displays it in response:

@RestController
public class GreetingController {
 
    @Autowired
    private HelloWorldClient helloWorldClient;

    @GetMapping("/get-greeting")
    public String greeting() {
        return helloWorldClient.helloWorld();
    }
}

4.5. YAML Configurations

Next, we create a YAML file Application.yml very similar to the one used before. That configures the application’s log level:

logging:
  level:
    org.apache.zookeeper.ClientCnxn: WARN

The application looks for the Zookeeper on default port 2181. If Zookeeper is located somewhere else, the configuration needs to be added:

spring:
  cloud:
    zookeeper:
      connect-string: localhost:2181

5. Testing the Setup

The HelloWorld REST service registers itself with Zookeeper on deployment. Then the Greeting service acting as the service consumer calls the HelloWorld service using the Feign client.

Now we can build and run these two services.

Finally, we’ll point our browser to http://localhost:8083/get-greeting, and it should display:

Hello World!

6. Conclusion

In this article, we have seen how to implement service discovery using Spring Cloud Zookeeper and we registered a service called HelloWorld within Zookeeper server to be discovered and consumed by the Greeting service using a Feign Client without knowing its location details.

As always, the code for this article is available on the GitHub.

Related posts:

Spring Security OAuth Login with WebFlux
Guide to Java 8’s Collectors
Hướng dẫn Java Design Pattern – Command
Using Spring @ResponseStatus to Set HTTP Status Code
Servlet 3 Async Support with Spring MVC and Spring Security
A Guide to Spring Boot Admin
Java Program to Implement Disjoint Sets
Guide to Dynamic Tests in Junit 5
Java Program to Find Location of a Point Placed in Three Dimensions Using K-D Trees
Copy a List to Another List in Java
Java Program to Check whether Graph is a Bipartite using DFS
Java Program to Find kth Smallest Element by the Method of Partitioning the Array
Introduction to Spring Boot CLI
Java Program to Implement Suffix Array
Error Handling for REST with Spring
Java Program to Implement Fisher-Yates Algorithm for Array Shuffling
Java Program to Implement Affine Cipher
Cơ chế Upcasting và Downcasting trong java
Java Program to Implement ArrayBlockingQueue API
Integer Constant Pool trong Java
Java Program to implement Array Deque
Getting Started with Forms in Spring MVC
Mapping a Dynamic JSON Object with Jackson
Request a Delivery / Read Receipt in Javamail
Merging Streams in Java
Hướng dẫn Java Design Pattern – Abstract Factory
Java Program to Check whether Undirected Graph is Connected using BFS
Java Program to Generate All Possible Combinations Out of a, b, c, d, e
Check if a String is a Palindrome in Java
Java Program to Implement the Schonhage-Strassen Algorithm for Multiplication of Two Numbers
How to Replace Many if Statements in Java
Java Program to Represent Linear Equations in Matrix Form