Building API Gateway Using Ocelot In ASP.NET Core - Service Discovery (Consul)

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.
 
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.   
  11.         return new string[] { "value1""value2", port.Value.ToString() };  
  12.     }  
  13. }  
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.   
  13.     public static IApplicationBuilder UseConsul(this IApplicationBuilder app)  
  14.     {  
  15.         var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();  
  16.         var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("AppExtensions");  
  17.         var lifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();  
  18.   
  19.         if (!(app.Properties["server.Features"is FeatureCollection features)) return app;  
  20.   
  21.         var addresses = features.Get<IServerAddressesFeature>();  
  22.         var address = addresses.Addresses.First();  
  23.   
  24.         Console.WriteLine($"address={address}");  
  25.   
  26.         var uri = new Uri(address);  
  27.         var registration = new AgentServiceRegistration()  
  28.         {  
  29.             ID = $"MyService-{uri.Port}",  
  30.             // servie name  
  31.             Name = "MyService",  
  32.             Address = $"{uri.Host}",  
  33.             Port = uri.Port  
  34.         };  
  35.   
  36.         logger.LogInformation("Registering with Consul");  
  37.         consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);  
  38.         consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);  
  39.   
  40.         lifetime.ApplicationStopping.Register(() =>  
  41.         {  
  42.             logger.LogInformation("Unregistering from Consul");  
  43.             consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);  
  44.         });  
  45.   
  46.         return app;  
  47.     }  
  48. }  
 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.   
  8.     public IConfiguration Configuration { get; }  
  9.   
  10.     public void ConfigureServices(IServiceCollection services)  
  11.     {  
  12.         services.AddConsulConfig(Configuration);  
  13.         services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);  
  14.     }  
  15.   
  16.     public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
  17.     {  
  18.         if (env.IsDevelopment())  
  19.         {  
  20.             app.UseDeveloperExceptionPage();  
  21.         }  
  22.   
  23.         app.UseConsul();  
  24.   
  25.         app.UseMvc();  
  26.     }  
  27. }  
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.   
  8.     public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>  
  9.         WebHost.CreateDefaultBuilder(args)  
  10.          .UseUrls("http://*:9000")  
  11.          .ConfigureAppConfiguration((hostingContext, config) =>  
  12.          {  
  13.             config  
  14.                 .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)  
  15.                 .AddJsonFile("ocelot.json")  
  16.                 .AddEnvironmentVariables();  
  17.          })  
  18.         .ConfigureServices(services =>  
  19.         {  
  20.             services.AddOcelot()  
  21.                 .AddConsul();  
  22.         })  
  23.         .Configure(app =>  
  24.         {  
  25.             app.UseOcelot().Wait();  
  26.         });  
  27. }  
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.