Service Discovery in Microservices with C# and .NET

Service Discovery in Microservices with C# and .NET

Microservices architecture has revolutionized the way we design and build software systems, allowing us to break down complex applications into smaller, independently deployable services. However, managing these services and ensuring they can find and communicate with each other can be a daunting task. This is where the Service Discovery pattern comes into play.

In this article, we'll explore the importance of service discovery and learn how to implement it in C# and .NET using Eureka. We'll cover the fundamentals, practical implementation, and best practices to effectively manage service discovery in your microservices ecosystem.

Understanding Service Discovery

What is Service Discovery?

Service discovery is a design pattern that addresses the dynamic registration and discovery of services within a microservices architecture. It allows services to find each other and communicate without hardcoding their network locations or IP addresses. Instead, services register themselves with a service registry, and other services can query the registry to discover available services.

Why is it important in microservices architecture?

In a microservices environment, services are typically deployed dynamically, and their instances can come and go due to scaling, updates, or failures. This dynamic nature makes it challenging to maintain up-to-date information about service locations manually. Service discovery simplifies this task and offers several benefits:

  • Dynamic Scaling: New instances of services can be added or removed without affecting the overall system. Service discovery ensures that clients can always find the latest instances.
  • Load Balancing: Service discovery can be used to distribute incoming requests evenly across available service instances, improving performance and fault tolerance.
  • Resilience: It helps in handling failures gracefully. If a service instance fails, clients can be directed to healthy instances automatically.
  • Decoupling: Services don't need to be aware of the network details or locations of other services, promoting loose coupling and making it easier to refactor and scale components.

Service Registration

How microservices register themselves

In a service discovery setup, each microservice registers itself with a service registry when it starts up. The registration typically includes information like the service name, version, network location, and health status. Service registries are responsible for maintaining a real-time database of available services.

Service Registration in C# with Eureka

One popular service registry tool is Netflix Eureka. Steeltoe is provided a very rich .Net library based on both Eureka and Consul for service discovery. In this article I used asp.net 6 and Steeltoe library of Eureka.

Running Eureka Server in Docker

Eureka Discovery service has two parts:

  • Eureka Server: which is the main part that all microservices register themselves in it.
  • Eureka Client: It is an API library which is used by each microservices to register itself in Eureka Server and also discover other registered services.

One of the best ways to install Eureka Server easily is running it in docker. You can simply install and run it using following command:

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

Note that 8761 is the default port of Eureka server. After running the docker, you are able to navigate to Eureka Server page by browsing to http//localhost:8761

Registering with Eureka

To be able to use Eureka client you need to install several packages using NuGet:

Steeltoe.Common.Hosting
Steeltoe.Discovery.ClientCore        

We need to register the client service and add the middleware before use using these lines:

builder.Services.AddDiscoveryClient(builder.Configuration);
…
app.UseDiscoveryClient();        

We also need to provide some configuration in appSettings.json:

// Eureka info
"eureka": {
   "client": {
      "serviceUrl": "https://localhost:8761/eureka/",
      "shouldFetchRegistry": "false",
      "shouldRegisterWithEureka": true,
????  "validateCertificates": false
  },
??"instance": {
??????"appName": "myapi",
??????"port": "8080",
??????"ipAddress": "localhost",
??????"preferIpAddress": true
????}
}        

With these settings we set the Url of the service a name (myapi) for this Api so later by providing this name to the server we will access this API base address

Now suppose from another service we call it “ClientService” we need to access “myapi”.

We need again to install same packages and add following settings in appSettings.json:

"eureka": {
  "client": {
    "serviceUrl": "https://locahost:8761/eureka/",
    "shouldFetchRegistry": "true",
    "shouldRegisterWithEureka": false,
    "validateCertificates": false
  }
}        

With these settings, our client app knows about Eureka server.

builder.Services.AddDiscoveryClient(builder.Configuration);

builder.Services.AddHttpClient("myapi", 
       client => client.BaseAddress = new Url("https://myapiurl/"))
       .AddServiceDiscovery();        

The interesting thing here is that we associate the name “myapi” with an address which looks like a URL but really is the name of the service within Eureka that we want to access. Then, by using the AddServiceDiscovery this will be converted to an instance URL representing the instance of the service associated with the app name.

Service Discovery Clients

Service discovery clients are responsible for querying the service registry to find the location of other services. Once a service is registered, it can use a client library to discover and communicate with services it depends on.

Implementing Service Discovery Clients in C#

Let's say “ClientService” needs to use some APIs in myapi service. So In “ClientService” we need to inject IHttpClientFactory:

private readonly IHttpClientFactory _httpClientFactory;

public HomeController(IHttpClientFactory httpClientFactory, ILogger<HomeController> logger)
{
   _httpClientFactory = httpClientFactory;
   _logger = logger;
}?        

And with now we can use the APIs from service:

public async Task<IActionResult> Index()
{
   var client = _httpClientFactory.CreateClient("myapi");
   var myview = await client.GetFromJsonAsync<IList<MyApiModel>>("apiname");
   return View(myview);
}?        

Docker and Kubernetes Integration

In containerized environments like Docker and Kubernetes, service discovery is often integrated into the platform. Kubernetes, for example, provides its own service discovery and load balancing mechanisms, making it easier to manage microservices.

Practical Implementation

Let's walk through a simplified real-world scenario where we implement service discovery in a C# .NET Core application. Imagine you have a microservices-based e-commerce system consisting of services like ProductService, OrderService, and UserService. These services need to find each other dynamically.

Step 1: Service Registration

Each service registers itself with the Eureka service registry when it starts up. You can use Eureka or other suitable libraries for this purpose. Ensure that you include essential information such as the service name, version, and network location in the registration.

Step 2: Service Discovery

When one service (e.g., OrderService) needs to communicate with another (e.g., ProductService), it uses a service discovery client (e.g., EurekaHttpClient) to discover available instances of the target service.

Step 3: Load Balancing

To achieve load balancing, you can integrate a load balancing library like Polly into your service. This library can be used to implement retry and failover strategies.

Best Practices and Considerations

Service discovery is a powerful pattern, but it's important to consider some best practices and potential challenges:

  • Scalability: Ensure that your service registry can handle the scalability needs of your microservices.
  • Security: Secure your service registry and communication between services to prevent unauthorized access.
  • Monitoring: Implement monitoring and alerting to detect and respond to service outages or issues.
  • Timeouts: Set appropriate timeouts for service discovery and communication to prevent blocking.
  • Documentation: Document the service registration and discovery process to make it easier for developers to work with.

In conclusion, service discovery is a fundamental microservices design pattern that simplifies the dynamic registration and discovery of services within a distributed system. Implementing service discovery in C# and .NET can greatly improve the scalability, resilience, and maintainability of your microservices architecture. As you continue to explore microservices design patterns, consider how service discovery can benefit your specific use cases.


References:

https://steeltoe.io/

https://docs.steeltoe.io/api/v3/discovery/index.html

https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance



?#microservices #servicediscovery #webapi #eureka #steeltoe


Jimmy Cumbicos

Consultor Informático

1 年

Hay que aplicarlo en sistemas de misión crítica para disminuir los problemas.

回复

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

Amir Doosti的更多文章

  • Network Programming in c# - Part 2 (HTTP programming)

    Network Programming in c# - Part 2 (HTTP programming)

    In the previous article I talked about Socket programming. Today, I’m going to cover another type of network…

  • Network Programming in C# - Part 1

    Network Programming in C# - Part 1

    Network programming in C# involves using .NET libraries to communicate between systems over a network.

    2 条评论
  • Locking (Synchronization) in C#

    Locking (Synchronization) in C#

    Concurrency and multithreading are powerful features in modern programming, but they bring challenges, especially in…

    6 条评论
  • Plotting in C# (Part 4 - ScottPlot)

    Plotting in C# (Part 4 - ScottPlot)

    ScottPlot is an open-source, .NET-based charting library designed for creating high-performance, interactive plots in…

  • Plotting in C# (Part 3 - OxyPlot)

    Plotting in C# (Part 3 - OxyPlot)

    OxyPlot is a lightweight, open-source plotting library designed specifically for .NET applications, supporting…

    2 条评论
  • Plotting in C#.Net (Part2 - LiveCharts2)

    Plotting in C#.Net (Part2 - LiveCharts2)

    LiveCharts is a versatile and modern charting library that supports a variety of charts and visualizations with smooth…

  • Plotting in C#.Net (Part 1 - General)

    Plotting in C#.Net (Part 1 - General)

    Plotting is a crucial tool for data analysis, visualization, and communication. There are many reasons why we need to…

    2 条评论
  • Half-Precision floating point in C#

    Half-Precision floating point in C#

    Recently I encountered a problem in a system where we needed to use floating point but we had just two bytes memory for…

    3 条评论
  • Working with Excel files in .Net

    Working with Excel files in .Net

    Using Excel files in software applications is common for several reasons, as they provide a practical and versatile…

  • ReadOnly Collections vs Immutable Collections

    ReadOnly Collections vs Immutable Collections

    In C#, both readonly collections and immutable collections aim to prevent modifications to the collection, but they…

社区洞察

其他会员也浏览了