Pattern: Client-side service discovery
pattern inter-service communication service discoveryContext
Services typically need to call one another. In a monolithic application, services invoke one another through language-level method or procedure calls. In a traditional distributed system deployment, services run at fixed, well known locations (hosts and ports) and so can easily call one another using HTTP/REST or some RPC mechanism. However, a modern microservice-based application typically runs in a virtualized or containerized environments where the number of instances of a service and their locations changes dynamically.

Consequently, you must implement a mechanism for that enables the clients of service to make requests to a dynamically changing set of ephemeral service instances.
Problem
How does the client of a service - the API gateway or another service - discover the location of a service instance?
Forces
- Each instance of a service exposes a remote API such as HTTP/REST, or Thrift etc. at a particular location (host and port)
 - The number of services instances and their locations changes dynamically.
 - Virtual machines and containers are usually assigned dynamic IP addresses.
 - The number of services instances might vary dynamically. For example, an EC2 Autoscaling Group adjusts the number of instances based on load.
 
Solution
When making a request to a service, the client obtains the location of a service instance by querying a Service Registry, which knows the locations of all service instances.
The following diagram shows the structure of this pattern.

This is typically handled by a Microservice chassis framework
Examples
The Microservices Example application is an example of an application that uses client-side service discovery. It is written in Scala and uses Spring Boot and Spring Cloud as the Microservice chassis. They provide various capabilities including client-side discovery.
RegistrationServiceProxy is a component of that application.
In order to register a user, it invokes another service using the Spring Framework’s RestTemplate:
@Component
class RegistrationServiceProxy @Autowired()(restTemplate: RestTemplate) extends RegistrationService {
  @Value("${user_registration_url}")
  var userRegistrationUrl: String = _
  override def registerUser(emailAddress: String, password: String): Either[RegistrationError, String] = {
      val response = restTemplate.postForEntity(userRegistrationUrl,
        RegistrationBackendRequest(emailAddress, password),
        classOf[RegistrationBackendResponse])
       ...
}
It is injected with the RestTemplate and the user_registration_url, which specifies the REST endpoint.
When the application is deployed user_registration_url is set to this URL http://REGISTRATION-SERVICE/user - see the docker-compose.yml file. REGISTRATION-SERVICE is the logical service name that is resolved to a network location using client-side service discovery.
The service discovery is implemented using Netflix OSS components.
It provides Eureka, which is a Service Registry, and  Ribbon, which is an HTTP client that queries Eureka in order to route HTTP requests to an available service instance.
Client-side service discovery is configured using various Spring Cloud annotations:
@Configuration
@EnableEurekaClient
@Profile(Array("enableEureka"))
class EurekaClientConfiguration {
  @Bean
  @LoadBalanced
  def restTemplate(scalaObjectMapper : ScalaObjectMapper) : RestTemplate = {
    val restTemplate = new RestTemplate()
    restTemplate.getMessageConverters foreach {
      case mc: MappingJackson2HttpMessageConverter =>
        mc.setObjectMapper(scalaObjectMapper)
      case _ =>
    }
    restTemplate
  }
The @EnableEurekaClient annotation enables the Eureka client.
The @LoadBalanced annotation configures the RestTemplate to use Ribbon, which has been configured to use the Eureka client to do service discovery.
As a result, the RestTemplate will handle requests to the http://REGISTRATION-SERVICE/user endpoint by querying Eureka to find the network locations of available service instances.
Resulting context
Client-side discovery has the following benefits:
- Fewer moving parts and network hops compared to Server-side Discovery
 
Client-side discovery also has the following drawbacks:
- This pattern couples the client to the Service Registry
 - You need to implement client-side service discovery logic for each programming language/framework used by your application, e.g Java/Scala, JavaScript/NodeJS. For example, Netflix Prana provides an HTTP proxy-based approach to service discovery for non-JVM clients.
 
Related patterns
- Service Registry - an essential part of service discovery
 - Microservice chassis - Client-side service discovery is the responsibility the microservice chassis framework
 - Server Side Discovery is an alternative solution to this problem.
 
      
      
    Premium content now available for paid subscribers at 
  
      
  
      
  