Introduction

In the previous articles of this series, we discussed how to build the API Gateway in ASP.NET Core. In this article, we will discuss the service discovery module of Ocelot with Consul.
If you want to look at the previous articles of this series, please visit the links given below.
I will use version 13.5.2 of Ocelot to show you this feature.
Step 1
Running up the Consul at first.
For the demonstration, I will use Docker to run up an instance of Consul.
  1. docker run -p 8500:8500 consul
After running up, we can get the following result.
Building API Gateway Using Ocelot In ASP.NET Core - Service Discovery (Consul)
Step 2
Create an API service that should be registered in Consul.
For the demonstration, I will create two Web API projects with different port numbers but their service names are the same.
Write some code in controller.
  1. [Route("api/[controller]")]
  2. [ApiController]
  3. public class ValuesController : ControllerBase
  4. {
  5. // GET api/values
  6. [HttpGet]
  7. public ActionResult<IEnumerable<string>> Get()
  8. {
  9. var port = Request.Host.Port;
  10. return new string[] { "value1", "value2", port.Value.ToString() };
  11. }
  12. }
Then, we should register it to Consul. The following code shows you a sample way to do that.
  1. public static class AppExtensions
  2. {
  3. public static IServiceCollection AddConsulConfig(this IServiceCollection services, IConfiguration configuration)
  4. {
  5. services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
  6. {
  7. var address = configuration.GetValue<string>("Consul:Host");
  8. consulConfig.Address = new Uri(address);
  9. }));
  10. return services;
  11. }
  12. public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
  13. {
  14. var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();
  15. var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("AppExtensions");
  16. var lifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
  17. if (!(app.Properties["server.Features"] is FeatureCollection features)) return app;
  18. var addresses = features.Get<IServerAddressesFeature>();
  19. var address = addresses.Addresses.First();
  20. Console.WriteLine($"address={address}");
  21. var uri = new Uri(address);
  22. var registration = new AgentServiceRegistration()
  23. {
  24. ID = $"MyService-{uri.Port}",
  25. // servie name
  26. Name = "MyService",
  27. Address = $"{uri.Host}",
  28. Port = uri.Port
  29. };
  30. logger.LogInformation("Registering with Consul");
  31. consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
  32. consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);
  33. lifetime.ApplicationStopping.Register(() =>
  34. {
  35. logger.LogInformation("Unregistering from Consul");
  36. consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
  37. });
  38. return app;
  39. }
  40. }
We also should modify the Startup class to enable registration.
  1. public class Startup
  2. {
  3. public Startup(IConfiguration configuration)
  4. {
  5. Configuration = configuration;
  6. }
  7. public IConfiguration Configuration { get; }
  8. public void ConfigureServices(IServiceCollection services)
  9. {
  10. services.AddConsulConfig(Configuration);
  11. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  12. }
  13. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  14. {
  15. if (env.IsDevelopment())
  16. {
  17. app.UseDeveloperExceptionPage();
  18. }
  19. app.UseConsul();
  20. app.UseMvc();
  21. }
  22. }
After running up those two projects, we will find that there is a new service named MyService that was registered in Consul with 2 nodes.
Building API Gateway Using Ocelot In ASP.NET Core - Service Discovery (Consul)
To see more details of this new service, we can click it, and we will find out the information of the nodes.
Building API Gateway Using Ocelot In ASP.NET Core - Service Discovery (Consul)
The next step is to build the API gateway.
Step 3
Add the following two packages via .NET Core CLI.
  1. dotnet add package Ocelot --version 13.5.2
  2. dotnet add package Ocelot.Provider.Consul --version 13.5.2
Add a new JSON configuration file named ocelot.json. And here is the content of it.
  1. {
  2. "ReRoutes": [
  3. {
  4. "UseServiceDiscovery": true,
  5. "DownstreamPathTemplate": "/{url}",
  6. "DownstreamScheme": "http",
  7. "ServiceName": "MyService",
  8. "LoadBalancerOptions": {
  9. "Type": "RoundRobin"
  10. },
  11. "UpstreamPathTemplate": "/{url}",
  12. "UpstreamHttpMethod": [ "Get" ],
  13. "ReRoutesCaseSensitive": false
  14. }
  15. ],
  16. "GlobalConfiguration": {
  17. "ServiceDiscoveryProvider": {
  18. "Host": "localhost",
  19. "Port": 8500,
  20. "Type":"PollConsul",
  21. "PollingInterval": 100
  22. }
  23. }
  24. }
ServiceDiscoveryProvider is required in the GlobalConfiguration section when we use service discovery!
For more details of this section, you can take a look at the following table.
Name Description
Host Specify the host of Consul
Port
Specify the port of Consul
Type 1. Consul, means that Ocelot will get service information from consul per request
2. PollConsul, means that Ocelot will poll Consul for latest service information
PollingInterval Tells Ocelot how often to call Consul for changes in the service configuration
The ReRoute section is also very important here! Because it tells Ocelot to use which service name and load balancer we wish to use when making requests. If no load balancer is specified, Ocelot will not load balance requests.
When this is set up, Ocelot will look up the downstream host and port from the service, discover a provider, and load balance requests across any available services.
At last, we should configure Ocelot in the Program class.
  1. public class Program
  2. {
  3. public static void Main(string[] args)
  4. {
  5. CreateWebHostBuilder(args).Build().Run();
  6. }
  7. public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
  8. WebHost.CreateDefaultBuilder(args)
  9. .UseUrls("http://*:9000")
  10. .ConfigureAppConfiguration((hostingContext, config) =>
  11. {
  12. config
  13. .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
  14. .AddJsonFile("ocelot.json")
  15. .AddEnvironmentVariables();
  16. })
  17. .ConfigureServices(services =>
  18. {
  19. services.AddOcelot()
  20. .AddConsul();
  21. })
  22. .Configure(app =>
  23. {
  24. app.UseOcelot().Wait();
  25. });
  26. }
After running up the API gateway, visit it via http://localhost:9000/api/values.
Building API Gateway Using Ocelot In ASP.NET Core - Service Discovery (Consul)
Let's take a look on the logs.
Building API Gateway Using Ocelot In ASP.NET Core - Service Discovery (Consul)
As we can see, the logs show us lots of information about the request.
Here is the source code you can find in my GitHub page.

Summary

This article introduced the basic usage of service discovery with Consul in Ocelot.
I hope this helps you.