Consul in .NET — A Service Mesh Solution And Service Discovery Tool

Keivan Damirchi
6 min readFeb 16, 2023

--

Service discovery is the process of automatically detecting and registering the availability and location of services within a distributed system. It is typically used in modern software architectures such as microservices and cloud-native applications, where services are often ephemeral and may be frequently created, updated, or destroyed.

Service discovery enables applications to dynamically locate and communicate with each other.

Service discovery enables applications to dynamically locate and communicate with each other, without requiring manual configuration or hard-coding of network addresses. Instead, services register themselves with a central registry, which other services can query to discover the location and availability of the desired service.

One of the primary benefits of service discovery is increased flexibility and scalability. As services are added, removed, or scaled up or down, the service discovery system can dynamically update the registry, ensuring that applications can always find the services they need. This can help to reduce downtime, improve fault tolerance, and increase overall system performance.

Use cases for service discovery include:

  1. Load balancing: Service discovery can be used to balance traffic across multiple instances of a service, improving performance and resiliency.
  2. Service composition: Services can be dynamically composed and recomposed to create new applications and workflows on the fly.
  3. High availability: Service discovery can be used to ensure that services remain available even in the event of a failure or outage.
  4. Scaling: Service discovery can be used to dynamically scale services up or down based on demand, without requiring manual intervention.

What is Consul?

Consul is a popular open-source service mesh solution and service discovery tool. It provides a central registry for service discovery and configuration, enabling dynamic service discovery and routing, as well as health checking, load balancing, and failover across a distributed system.

In addition to service discovery, Consul also provides a number of other features, including:

  1. Service segmentation: Consul allows services to be segmented into different logical groups, with policies and configurations that are specific to each group.
  2. Key-value store: Consul includes a distributed key-value store that can be used to store and manage configuration data and other application settings.
  3. Secure communication: Consul supports secure communication using Transport Layer Security (TLS) and mutual authentication, ensuring that all communication between services is encrypted and authenticated.
  4. Monitoring and logging: Consul includes built-in monitoring and logging capabilities, enabling operators to track the health and performance of their services in real time.

brief guide on how to install and configure Consul for your ASP.NET Core application:

Install Consul
You can download Consul from the official Consul downloads page: consul.io. Choose the appropriate version for your operating system and download the binary file.
Once you have downloaded the binary file, you can install Consul by following the instructions for your operating system. For example, on Linux or macOS, you can copy the binary to /usr/local/bin and set the appropriate permissions:

sudo chmod +x /usr/local/bin/consul

Start Consul

You can start Consul in development mode using the following command:

consul agent -dev

This will start Consul in development mode with default settings. You can access the Consul UI by navigating to http://localhost:8500 in your web browser. For production use, you will need to configure Consul with the appropriate settings for your environment. You can do this by creating a configuration file and starting Consul with the -config-file flag:

consul agent -config-file=/path/to/config.json

Configure Consul

To configure Consul for your ASP.NET Core application, you will need to define the services that your application provides and register them with Consul.
You can do this by creating a JSON file that defines your services and their properties. Here’s an example configuration file:

{
"service": {
"id": "my-service",
"name": "My Service",
"address": "127.0.0.1",
"port": 5000,
"check": {
"http": "http://localhost:5000/health",
"interval": "10s",
"timeout": "1s"
}
}
}

In the above example, we are defining a service with an ID of “my-service” and a name of “My Service”. The service is hosted on the local machine with an IP address of 127.0.0.1 and a port of 5000. We are also defining a health check for the service that checks the endpoint http://localhost:5000/health every 10 seconds with a timeout of 1 second.

Register Service with Consul

You can register your ASP.NET Core application with Consul by using the Consul API:

var client = new ConsulClient();

var registration = new AgentServiceRegistration()
{
ID = "my-service",
Name = "My Service",
Address = "127.0.0.1",
Port = 5000,
Check = new AgentServiceCheck
{
HTTP = "http://localhost:5000/health",
Interval = TimeSpan.FromSeconds(10),
Timeout = TimeSpan.FromSeconds(1)
}
};

client.Agent.ServiceRegister(registration);

In the above example, we are using the ConsulClient to register our ASP.NET Core application with Consul. We are defining a AgentServiceRegistration object that contains the properties of our service, including the ID, name, address, port, and health check settings. We are then calling the ServiceRegister method on the Agent object to register the service with Consul. That’s it! You have now installed and configured Consul for your ASP.NET Core application. You can use the Consul API to discover other services in your network and manage your application’s configuration data.

We will use the Consul .NET library to register an ASP.NET Core application with Consul and to retrieve configuration data from Consul.

Add Consul Package

Add the Consul package to your ASP.NET Core application using the NuGet Package Manager.

Register ASP.NET Core application with Consul

services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
{
consulConfig.Address = new Uri("http://localhost:8500");
}));

services.AddSingleton<IHostedService, ConsulHostedService>();
services.Configure<ConsulConfig>(Configuration.GetSection("consul"));

In the above code, we are registering an instance of the ConsulClient class with the DI container as a singleton. We are also registering an instance of the ConsulHostedService class as a hosted service, which will start when the application starts and will register the application with Consul. Finally, we are registering an instance of the ConsulConfig class, which contains the configuration data needed to register the application with Consul.

Create ConsulHostedService Class

Create a class named ConsulHostedService that implements the IHostedService interface. Add the following code to the class:

public class ConsulHostedService : IHostedService
{
private readonly IConsulClient _consulClient;
private readonly IConfiguration _configuration;
private readonly ILogger<ConsulHostedService> _logger;

public ConsulHostedService(IConsulClient consulClient, IConfiguration configuration, ILogger<ConsulHostedService> logger)
{
_consulClient = consulClient;
_configuration = configuration;
_logger = logger;
}

public async Task StartAsync(CancellationToken cancellationToken)
{
var serviceConfig = _configuration.GetSection("consul").Get<ConsulConfig>();
var registration = new AgentServiceRegistration
{
ID = serviceConfig.ServiceId,
Name = serviceConfig.ServiceName,
Address = serviceConfig.ServiceHost,
Port = serviceConfig.ServicePort
};

var check = new AgentServiceCheck
{
HTTP = serviceConfig.HealthCheckUrl,
Interval = TimeSpan.FromSeconds(serviceConfig.HealthCheckIntervalSeconds),
Timeout = TimeSpan.FromSeconds(serviceConfig.HealthCheckTimeoutSeconds)
};

registration.Checks = new[] { check };

_logger.LogInformation($"Registering service with Consul: {registration.Name}");

await _consulClient.Agent.ServiceDeregister(registration.ID, cancellationToken);
await _consulClient.Agent.ServiceRegister(registration, cancellationToken);
}

public async Task StopAsync(CancellationToken cancellationToken)
{
var serviceConfig = _configuration.GetSection("consul").Get<ConsulConfig>();
var registration = new AgentServiceRegistration { ID = serviceConfig.ServiceId };

_logger.LogInformation($"Deregistering service from Consul: {registration.ID}");

await _consulClient.Agent.ServiceDeregister(registration.ID, cancellationToken);
}
}

In the above code, we are implementing the StartAsync method, which will register the ASP.NET Core application with Consul, and the StopAsync method, which will deregister the application from Consul when the application is stopped.

Create ConsulConfig Class

Create a class named ConsulConfig that represents the configuration data needed to register the ASP.NET Core application with Consul. Add the following code to the class:

public class ConsulConfig
{
public string ServiceId { get; set; }
public string ServiceName { get; set; }
public string ServiceHost { get; set; }
public int ServicePort { get; set; }
public string HealthCheckUrl { get; set; }
public int HealthCheckIntervalSeconds { get; set; }
public int HealthCheckTimeoutSeconds { get; set; }
}

Retrieve Configuration Data from Consul

Add the following code to the ConfigureServices method in the Startup class to retrieve configuration data from Consul:

var consulConfig = Configuration.GetSection("consul").Get<ConsulConfig>();
var kv = _consulClient.KV;
var result = await kv.Get(consulConfig.ServiceName, cancellationToken);

if (result.Response != null)
{
var data = Encoding.UTF8.GetString(result.Response.Value);
var configData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
foreach (var item in configData)
{
Configuration[item.Key] = item.Value;
}
}

In the above code, we are using the ConsulClient to retrieve configuration data from Consul. We are retrieving the data from the key/value store using the service name as the key. We are then deserializing the data into a Dictionary<string, string> and adding the values to the Configuration object.

Use Configuration Data in ASP.NET Core

You can now use the configuration data in your ASP.NET Core application using the IConfiguration object. For example, you can retrieve a value from the configuration in a controller by adding the following code:

public class HomeController : Controller
{
private readonly IConfiguration _configuration;

public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}

public IActionResult Index()
{
var myValue = _configuration["MyKey"];
return View();
}
}

In the above code, we are retrieving a value from the configuration using the key “MyKey”. This value was retrieved from Consul in step 5.

That’s it! You can now use Consul in your ASP.NET Core application to manage configuration data and to discover other services in the network.

For further reading:

  1. HashiCorp
  2. Consul

--

--