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 Data JPA @Query
Java Program to Describe the Representation of Graph using Adjacency Matrix
Loại bỏ các phần tử trùng trong một ArrayList như thế nào trong Java 8?
Java Program to Check if any Graph is Possible to be Constructed for a Given Degree Sequence
Java Program to Find kth Smallest Element by the Method of Partitioning the Array
Introduction to Spring Cloud Netflix – Eureka
Java Program to Represent Graph Using Adjacency List
How To Serialize and Deserialize Enums with Jackson
Phương thức forEach() trong java 8
XML-Based Injection in Spring
Spring Boot - Enabling Swagger2
Java Program to Implement Pagoda
Java Program to Implement ArrayList API
HTTP Authentification and CGI/Servlet
Hướng dẫn Java Design Pattern – Transfer Object
Introduction to Spring Method Security
Guide to Java OutputStream
Tránh lỗi ConcurrentModificationException trong Java như thế nào?
Java Program to Check whether Directed Graph is Connected using BFS
Prevent Cross-Site Scripting (XSS) in a Spring Application
Introduction to Java 8 Streams
Java Program to Generate Random Numbers Using Probability Distribution Function
Java Program to Implement Sparse Matrix
Giới thiệu JDBC Connection Pool
Configure a Spring Boot Web Application
Retrieve User Information in Spring Security
Spring Security Remember Me
Hướng dẫn Java Design Pattern – Iterator
Error Handling for REST with Spring
Luồng Daemon (Daemon Thread) trong Java
Java Program to Implement Booth Algorithm
Display Auto-Configuration Report in Spring Boot