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.
- docker run -p 8500:8500 consul
After running up, we can get the following result.
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.
- [Route("api/[controller]")]
- [ApiController]
- public class ValuesController : ControllerBase
- {
-
- [HttpGet]
- public ActionResult<IEnumerable<string>> Get()
- {
- var port = Request.Host.Port;
-
- return new string[] { "value1", "value2", port.Value.ToString() };
- }
- }
Then, we should register it to Consul. The following code shows you a sample way to do that.
- public static class AppExtensions
- {
- public static IServiceCollection AddConsulConfig(this IServiceCollection services, IConfiguration configuration)
- {
- services.AddSingleton<IConsulClient, ConsulClient>(p => new ConsulClient(consulConfig =>
- {
- var address = configuration.GetValue<string>("Consul:Host");
- consulConfig.Address = new Uri(address);
- }));
- return services;
- }
-
- public static IApplicationBuilder UseConsul(this IApplicationBuilder app)
- {
- var consulClient = app.ApplicationServices.GetRequiredService<IConsulClient>();
- var logger = app.ApplicationServices.GetRequiredService<ILoggerFactory>().CreateLogger("AppExtensions");
- var lifetime = app.ApplicationServices.GetRequiredService<IApplicationLifetime>();
-
- if (!(app.Properties["server.Features"] is FeatureCollection features)) return app;
-
- var addresses = features.Get<IServerAddressesFeature>();
- var address = addresses.Addresses.First();
-
- Console.WriteLine($"address={address}");
-
- var uri = new Uri(address);
- var registration = new AgentServiceRegistration()
- {
- ID = $"MyService-{uri.Port}",
-
- Name = "MyService",
- Address = $"{uri.Host}",
- Port = uri.Port
- };
-
- logger.LogInformation("Registering with Consul");
- consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
- consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);
-
- lifetime.ApplicationStopping.Register(() =>
- {
- logger.LogInformation("Unregistering from Consul");
- consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
- });
-
- return app;
- }
- }
We also should modify the Startup class to enable registration.
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddConsulConfig(Configuration);
- services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
- }
-
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- app.UseConsul();
-
- app.UseMvc();
- }
- }
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.
To see more details of this new service, we can click it, and we will find out the information of the nodes.
The next step is to build the API gateway.
Step 3
Add the following two packages via .NET Core CLI.
- dotnet add package Ocelot --version 13.5.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.
- {
- "ReRoutes": [
- {
- "UseServiceDiscovery": true,
- "DownstreamPathTemplate": "/{url}",
- "DownstreamScheme": "http",
- "ServiceName": "MyService",
- "LoadBalancerOptions": {
- "Type": "RoundRobin"
- },
- "UpstreamPathTemplate": "/{url}",
- "UpstreamHttpMethod": [ "Get" ],
- "ReRoutesCaseSensitive": false
- }
- ],
- "GlobalConfiguration": {
- "ServiceDiscoveryProvider": {
- "Host": "localhost",
- "Port": 8500,
- "Type":"PollConsul",
- "PollingInterval": 100
- }
- }
- }
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.
- public class Program
- {
- public static void Main(string[] args)
- {
- CreateWebHostBuilder(args).Build().Run();
- }
-
- public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .UseUrls("http://*:9000")
- .ConfigureAppConfiguration((hostingContext, config) =>
- {
- config
- .SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
- .AddJsonFile("ocelot.json")
- .AddEnvironmentVariables();
- })
- .ConfigureServices(services =>
- {
- services.AddOcelot()
- .AddConsul();
- })
- .Configure(app =>
- {
- app.UseOcelot().Wait();
- });
- }
After running up the API gateway, visit it via http://localhost:9000/api/values.
Let's take a look on the logs.
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.