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 the “Hello World !” in a response to GET requests.
3.1. Maven Dependencies
First, let’s add the required spring-cloud-starter-zookeeper-discovery, spring-web, spring-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>1.5.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> <version>1.0.3.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR7</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-discovery, spring-web, spring-cloud-dependencies, spring-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> <version>1.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>1.5.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> <version>1.2.5.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR7</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.