Published on

Implementing Service Discovery in Microservices with Eureka

Table of Contents

Microservices are quickly becoming the go-to architecture for companies looking to build modern, scalable applications. But microservices architecture comes with a lot of challenges and one of them is the dynamic nature of a service's location and its number of instances.

In traditional monolithic applications, most often, we used to have a fixed number of instances running on static IPs. In that case we didn't need Service Discovery because all instances were running on a well-known location.

In the case of microservices, they usually run in a containerized environment which has a virtual network. Inside this network, addresses are dynamically assigned to services for reasons such as Autoscaling. But then, the question is how microservices communicate with each other if they cannot determine each other's address. This is where Service Discovery helps us.

In this post, we will explore how Service Discovery works and how it can be implemented using Eureka. By the end of this article, you will have a better understanding of Service Discovery in microservices and how to implement it using Eureka.

What is Service Discovery?

Service discovery is a technique used in distributed systems that enables services within a the system to find and communicate with each other.

How does Service Discovery Works?

There are three parts to it.

Registration

First, it involves registering services in a special component known as service-registry. It stores the location of each service, as well as any other necessary information for communication such as address and port.

Heartbeats

After registration, services periodically send additional requests, also known as heartbeats, to the service-registry to let it know they are well and alive. Otherwise, it removes the services from the registry. The heartbeat period is usually set to 30 seconds but can be customized according to your needs.

Discovery

When one service needs another service, it sends a request to the registry and receives back the required details.

Implementing Service Discovery with Eureka

What is Eureka?

Eureka is a REST-based service developed by Netflix used for locating services.

Preparation

First, we need to run Eureka on our system. It is available as a docker image, so we can run the following command to start the Eureka server. Make sure your docker client is running.

docker run -d -p 8761:8761 --name eureka steeltoeoss/eureka-server

It will run the Eureka server. Open http://localhost:8761/ and you will see a page like the below one.

Eureka server dashboard

Registration

Open your microservice and install the following NuGet packages.

Steeltoe.Discovery.ClientCore
Steeltoe.Discovery.Eureka

At this point you may be wondering what is Steeltoe? Steeltoe provides a collection of libraries which help you build microservices applications with ease. You can read more about it here.

The two packages we have installed are for the following purposes:

  • Steeltoe.Discovery.ClientCore provides basic Service Discovery related functionalities.
  • Steeltoe.Discovery.Eureka provides Eureka specific Service Discovery functionalities.

Then open appsettings.json and add the following section:

appsettings.json
{
...
  "Eureka": {
    "Client": {
      "ShouldRegisterWithEureka": true,
    },
    "Instance": {
      "AppName": "airline-booking"
    }
  },
...
}

The Service Discovery packages that we have installed automatically reads this section.

By setting Eureka:Client:ShouldRegisterWithEureka to true, we are enabling automatic service registration; i.e., whenever our microservice starts it will automatically register itself with the Eureka server.

Eureka:Instance:AppName sets the name of the microservice.

You can read about all other options available to configure here.

Then in you Program.cs file call AddServiceDiscovery().

Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Host.AddServiceDiscovery(builder => builder.UseEureka());

var app = builder.Build();

app.MapControllers();

app.Run();

This is all the code we need to register our application to the Eureka server. Run the application and visit the Eureka page (http://localhost:8761/). You will see the microservice registered there. If not, wait for a few seconds and keep refreshing the Eureka server page.

Services registered on Eureka

Heartbeat

We don't need to need to do anything explicitly to send heartbeat requests. The Service Discovery packages automatically send these. You can see the image below of an HTTP heartbeat request.

Heartbeat log

Discovery

First, open appsettings.json and add Eureka:Client:ShouldFetchRegistry setting.

appsettings.json
...
  "Eureka": {
    "Client": {
      "ShouldRegisterWithEureka": true,
      "ShouldFetchRegistry": true
    },
    "Instance": {
      "AppName": "airline-booking"
    }
  },
...

This setting will enable the Service Discovery option.

Next, create a http wrapper class for the microservice with which we want to communicate. For example, here we want to communicate with airline-search microservice.

AirlineSearchClient.cs
public class AirlineSearchClient
{
	private readonly HttpClient client;

	public AirlineSearchClient(HttpClient client)
	{
		client.BaseAddress = new Uri("http://airline-search/");
		this.client = client;
	}

	public async Task<string> Search()
	{
		var response = await client.GetAsync("api/search");
		if (!response.IsSuccessStatusCode)
		{
			return null;
		}

		return await response.Content.ReadAsStringAsync();
	}
}

Note how we have used http://airline-search/ as the base address. That's how service discovery keeps us decoupled from other service's exact locations. We just have to specify the service name and it will figure out exactly where to send the request.

The name airline-search should be the same with which service is registered on Eureka. Remember, we specify the service name in the appsettings.json under Eureka:Instance:AppName field.

Next we'll register this Http client.

Program.cs
builder.Services
    .AddHttpClient("AirlineSearchClient")
    .AddServiceDiscovery()
    .AddTypedClient<AirlineSearchClient>();

Here we are using Microsoft's Basic and Typed Consumption patterns to register our http wrapper client. First, we register an HttpClient, then we configured the HttpClient to use DiscoveryHttpMessageHandler by calling AddServiceDiscovery() and then we made it a TypedClient.

You can read more about registering Http clients on ASP.NET here.

And that's it. Now run the microservices and they will be able to communicate with each other.

Conclusion

Service Discovery is a crucial technique in microservices architecture, enabling services to find and communicate with each other in a distributed system. This article explored how Service Discovery works and how it can be implemented using Eureka, a REST-based service developed by Netflix 🎥.

We walked through the implementation steps of Service Discovery with Eureka, including registration, heartbeats, and discovery. We also learned about Steeltoe, a collection of libraries that provides basic and Eureka-specific Service Discovery functionalities.

Overall, Service Discovery with Eureka simplifies the communication process between microservices, providing a reliable and scalable way to locate and access services within a distributed system.