Service Discovery Patterns with Netflix Eureka
Photo by Daniel Lerman on Unsplash

Service Discovery Patterns with Netflix Eureka

Modern distributed systems assign network locations dynamically, so you don’t know what IP address or port each service instance has. Consequently, client applications need a mechanism to discover the IP address and port of the services they are using.

As we explore in this article, service discovery tools like Netflix Eureka enable service discovery and registration in microservice-based applications.


Service Discovery Patterns

The Problem

Modern distributed systems typically run in virtualized/containerized environments where the number of instances of a particular service and their network locations change dynamically. As a result, you can’t statically configure a client application with the IP and port of the service.


Service Registry

Service discovery includes a Service Registry mechanism, which is a special service that stores the network locations of each service instance.

A service updates the service registry when instances are created or destroyed. When a client application queries the service registry, it gets a list of available instances and routes.

Self Registration Pattern

A service instance is responsible for registering itself with the service registry. On startup the service instance invokes the service registry's registration API to register its network location.

It is also convenient that services supply a health check, which is an endpoint that is invoked periodically to verify that the service instance is healthy and available to handle requests.

Client-Side Discovery Pattern

When a client wants to invoke a service, it queries the service registry to obtain a list of available instances.


Clients may use load-balancing algorithms to determine which service instance to use for each request (load-balanced requests).


Netflix Eureka

Project Structure

As shown below, our Maven project is divided into three modules: api-gateway, order-service and service-registry.

├───api-gateway
│   ├───src
│   │   └───main
│   │       ├───java
│   │       └───resources
│   └───pom.xml
├───order-service
│   ├───src
│   │   └───main
│   │       ├───java
│   │       └───resources
│   └───pom.xml
├────service-registry
│   ├───src
│   │   └───main
│   │       ├───java
│   │       └───resources
│   └───pom.xml
└───pom.xml        

Service Registry, the Eureka Server

To include Eureka Server in our project, let’s add the cloud-starter-netflix-eureka-server dependency to the service-registry/pom.xml file.

<dependency>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    <groupId>org.springframework.cloud</groupId>
</dependency>        

The example below shows a minimal Eureka Server application.

@EnableEurekaServer
@SpringBootApplication
public class RegistryApplication {

    public static void main(String[] args) {
        SpringApplication.run(RegistryApplication.class, args);
    }
}        

Next, we add the service registry configuration to service-registry/src/main/resources/application.yml.

server.port: 8761

spring:
  cloud.config.enabled: false

# Standalone Eureka Server
eureka:
  instance:
    hostname: localhost
  client:
    register-with-eureka: false
    fetch-registry: false
    serviceUrl:
      defaultZone: https://${eureka.instance.hostname}:${server.port}/eureka/        

We configure the Eureka Server in standalone mode by setting eureka.client.register-with-eureka=false and eureka.client.fetch-registry=false. This switches off the client side behavior so that it does not keep trying and failing to reach its peers.

Once you run the Eureka Server, you can open https://localhost:8761/ in your browser.

As you can see, no instances have yet been registered.

Order Service, the Eureka Client

When a client registers with Eureka, it provides meta-data such as host, port, health indicator URL and other details.

To include the Eureka Client in our project, let’s add spring-cloud-starter-netflix-eureka-client and spring-boot-starter-actuator to order-service/pom.xml.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>        

The example below shows a minimal Spring Boot application.

@SpringBootApplication
public class OrdersApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrdersApplication.class, args);
    }

    @Bean
    RouterFunction<ServerResponse> orders() {
        return route()
            .POST("/orders", request -> ServerResponse.accepted().build())
            .build();
    }
}        

Here, we expose the /orders endpoint which always returns 202 Accepted as a response.

Next, we add the self-registration configuration to order-service/src/main/resources/application.yml.

server.port: 8585

spring:
  application.name: order-service

eureka:
  server:
    host: localhost:8761
  client:
    serviceUrl:
      defaultZone: https://${eureka.server.host}/eureka
    register-with-eureka: true
    fetch-registry: false

# actuator
management:
  endpoints.web.exposure.include: health,info
  info.env.enabled: true
info:
  application.name: ${spring.application.name}        

The default status page and status indicators of a Eureka instance are /info and /health respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application.

API Gateway, the Discovery Client

Once you have an application instance in the service registry, you can use the discovery client to look for service instances on the Eureka Server.

First, let’s add spring-cloud-starter-netflix-eureka-client and spring-boot-starter-webflux to api-gateway/pom.xml.

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>        

Next, we add the client-side discovery configuration to api-gateway/src/main/resources/application.yml.

server.port: 9090

eureka:
  server:
    host: localhost:8761
  client:
    serviceUrl:
      defaultZone: https://${eureka.server.host}/eureka
    register-with-eureka: false
    fetch-registry: true        

In the example below, we create a @Configuration class where we set up a load-balanced instance of WebClient.

@Configuration
class WebClientConfiguration {

    @Bean
    @LoadBalanced
    WebClient webClient() {
        return WebClient.builder().build();
    }
}        

We use the native com.netflix.discovery.EurekaClient as the service discovery client to get available service instances, as shown below.

@RestController
public class OrdersServiceProxy {

    private static final String ORDER_SERVICE = "order-service";

    private final WebClient webClient;
    private final EurekaClient discoveryClient;

    public OrdersServiceProxy(WebClient webClient, EurekaClient discoveryClient) {
        this.webClient = webClient;
        this.discoveryClient = discoveryClient;
    }

    @PostMapping("v1/orders")
    public Mono<ResponseEntity<Void>> ordersV1(Order order) {

        String baseUrl = 
            discoveryClient.getNextServerFromEureka(ORDER_SERVICE, false)
                .getHomePageUrl();

        return webClient.post()
            .uri(baseUrl + "/orders")
            .body(Mono.just(order), Order.class)
            .retrieve()
            .toBodilessEntity();
    }
}        

Thanks for reading. I hope this was helpful!

The example code is available on GitHub.

要查看或添加评论,请登录

Jonathan Manera的更多文章

  • Spring Cloud Circuit Breaker Resilience4j

    Spring Cloud Circuit Breaker Resilience4j

    A distributed system, which comprises many services interacting to achieve business goals, is prone to failures in the…

  • Building Microservices with Spring Boot and?gRPC

    Building Microservices with Spring Boot and?gRPC

    gRPC Remote Procedure Call is a binary message based protocol for writing cross-language applications. As part of this…

  • Aspect Oriented Programming for?Babies

    Aspect Oriented Programming for?Babies

    Aspect Oriented Programming is a powerful tool that enables a clean modularization of “crosscutting” concerns of…

  • 7 Best Practices for Java API Documentation

    7 Best Practices for Java API Documentation

    The Javadoc Tool simplifies the documentation process in Java, allowing developers to document their code seamlessly as…

  • The Holy Trinity: JUnit5, Mockito, and Instancio

    The Holy Trinity: JUnit5, Mockito, and Instancio

    Unit testing is a software testing technique where individual components of an application (units) are tested in…

  • Code Quality Analysis with Sonar

    Code Quality Analysis with Sonar

    SonarQube is an open-source platform for continuous code quality inspection. This tool provides a detailed analysis of…

  • Mapping Bidirectional Object Associations using MapStruct

    Mapping Bidirectional Object Associations using MapStruct

    In object-oriented programming, an association is a relationship between two entities that defines how two objects…

  • API-First Design with OpenAPI Generator

    API-First Design with OpenAPI Generator

    APIs are contracts between services and their clients. These contracts are usually documented using an interface…

    1 条评论
  • Testing Microservices with Testcontainers

    Testing Microservices with Testcontainers

    When writing integration tests for Spring Boot applications, we usually need to access external resources such as…

  • 10 Meditations for Creative Professionals

    10 Meditations for Creative Professionals

    Sometimes, in our day-to-day life, we feel that something is not quite right. We find it hard to be as productive as we…

社区洞察

其他会员也浏览了