(8) Asp.net core3.1 Ocelot consult service registration and discovery

Time:2021-11-18

1. Service discovery

● service registration:By writing the registration code in each service instance, the instance will first register in the registry (such as consult, zookeeper, etcd, Eureka) when it is started. Then the client can know the address, port number, health status and other information of each service instance through the registry, or delete the service instance through the registry. The registry here is equivalent to the control center responsible for maintaining service instances.
● service discovery:After service instances are registered in the registry, the client can understand the health status of these service instances through the registry.

2.Consul

If you want to implement service registration and discovery, you need a registration center. The main introduction here is consult.
Consul official website: https://www.consul.io/ Its main functions include: service registration and discovery, health check, key / value and multi data center.
If consum is deployed on windows, execute the command line consum.exe agent – Dev in the consum.exe directory.
If you deploy consul on Linux, you can move forward. I wrote this article before“Cluster deployment of consul in Linux Environment”See below.

3. ASP. Net core registers service instances with Consul

The calling process of ASP. Net core registering a service instance with consul is shown in the following figure (the picture is from https://www.cnblogs.com/zhouandke/p/10534836.html ):

The ASP. Net core needs to reference the nuget package supported by consul in the gateway project to register the service instance with Consul. The installation command is as follows:

Install-Package Ocelot.Provider.Consul

Then add the following to your configureservices method:

services.AddOcelot().AddConsul();

In the example of Ocelot service discovery project, service registration and discovery can be configured through the globalconfiguration option of apigateway project. The specific code of file configuration is as follows:

{
  "Routes": [
    {
      "UseServiceDiscovery": true,
      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "ServiceName": "MyService",
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },
      "UpstreamPathTemplate": "/{url}",
      "UpstreamHttpMethod": [ "Get" ],
      "ReRoutesCaseSensitive": false
    }
  ],
  "GlobalConfiguration": {
    //Service discovery configuration
    "ServiceDiscoveryProvider": {
      //Address of the registry
      "Host": "192.168.113.128",
      //Registry consul port number
      "Port": 8500,
      "Type": "Consul",
      //In milliseconds, tell Ocelot how often to call consult to change the service configuration.
      "PollingInterval": 100,
      //If you have configured key / value on consult, enter the configuration key here.
      "ConfigurationKey": "MyService_AB"
    }
  }
}

Servicediscoveryprovider option Description:
●Host:Address of the registry.
●Port:Registry consul port number.
●Type:Registry type.
●PollingInterval:In milliseconds, tell Ocelot how often to call consult to change the service configuration.
●ConfigurationKey:If you have configured key / value on consult, enter the configuration key here.

4. Project demonstration

4.1apigateway project

Add Ocelot and Consul injection to configureservices:

services.AddOcelot().AddConsul();

Configure add using Ocelot:

app.UseOcelot().Wait();

The service discovery configuration is the same as the ocelot service discovery project example.

4.2common project

First install the nuget package of consul, and the installation command is as follows:

Install-Package Consul

An appextensions extension class is added to the project to register instances of apiservicea and apiserviceb projects in consul. In order to show the effect, the specific code is slightly modified as follows:

public static class AppExtensions
{
    public static IServiceCollection AddConsulConfig(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddSingleton(p => new ConsulClient(consulConfig =>
        {
            var address = configuration.GetValue("Consul:Host");
            consulConfig.Address = new Uri(address);
        }));
        return services;
    }
    public static IApplicationBuilder UseConsul(this IApplicationBuilder app, string host = null, string port = null)
    {
        //Get consumer client instance
        var consulClient = app.ApplicationServices.GetRequiredService();
        var logger = app.ApplicationServices.GetRequiredService().CreateLogger("AppExtensions");
        var lifetime = app.ApplicationServices.GetRequiredService();

        if (!(app.Properties["server.Features"] is FeatureCollection features)) return app;

        //var addresses = features.Get();
        //var address = addresses.Addresses.FirstOrDefault();
        //if (address == null)
        //{
        //    return app;
        //}

        var address = host + ":" + port;
        if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port))
        {
            Console. Writeline ($"host or port is empty!");
            return app;
        }

        Console.WriteLine($"address={address}");
        var uri = new Uri(address);
        Console.WriteLine($"host={uri.Host},port={uri.Port}");

        var registration = new AgentServiceRegistration()
        {
            ID = $"MyService-{uri.Port}",
            Name = "MyService",
            Address = $"{uri.Host}",
            Port = uri.Port,
            Check = new AgentServiceCheck()
            {
                Deregistercriticalserviceafter = timespan. Fromseconds (5), // how long after the service is started
                Interval = timespan. Fromseconds (10), // health check interval
                HTTP = $"{address} / healthcheck", // health check address
                Timeout = timespan. Fromseconds (5) // timeout
            }
        };
        logger.LogInformation("Registering with Consul");
        logger.LogInformation($"Consul RegistrationID:{registration.ID}");
        //Write off
        consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
        //Register
        consulClient.Agent.ServiceRegister(registration).ConfigureAwait(true);
        //When the application closes
        lifetime.ApplicationStopping.Register(() =>
        {
            //Logging off
            logger.LogInformation("Unregistering from Consul");
            consulClient.Agent.ServiceDeregister(registration.ID).ConfigureAwait(true);
        });
        //Each service needs to provide an interface for health check, which does not have business functions. During service registration, the address of this interface is also told to the registry. The registry will call this interface regularly to detect whether the service is normal. If it is not normal, it will be removed, so as to ensure the availability of the service.
        app.Map("/HealthCheck", s =>
        {
            s.Run(async context =>
            {
                await context.Response.WriteAsync("ok");
            });
        });
        return app;
    }
}

4.3 apiservicea project

Add a get method to the project, corresponding to the upstream and downstream configuration of the apigateway project. The specific code is as follows:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public ActionResult> Get()
    {
        var port = Request.Host.Port;
        return new string[] { "value1", "value2", port.Value.ToString() };
    }
}

Appsettings.json configuration to add consul address:

"Consul": {
  "Host": "http://192.168.113.128:8500"
}

4.4 apiserviceb project

Add a get method to the project, corresponding to the upstream and downstream configuration of the apigateway project. The specific code is as follows:

[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public ActionResult> Get()
    {
        var port = Request.Host.Port;
        return new string[] { "value3", "value4", port.Value.ToString() };
    }
}

Appsettings.json configuration to add consul address:

"Consul": {
  "Host": "http://192.168.113.128:8500"
}

4.5 project operation

Add consul configuration in configureservices of apiservicea and apiserviceb projects:

services.AddConsulConfig(Configuration);

Add consul service registration in configure:

APIServiceA:app.UseConsul("http://172.168.18.73", "9999");
APIServiceB:app.UseConsul("http://172.168.18.73", "9998");

Deploy apigateway, apiservicea and apiserviceb to IIS:

After the three projects are running, you can see the myservice node service through the browser consul client:

Click to open the myservice node to see the service status of apiservicea and apiserviceb registered with Consul:

If the apiserviceb service instance site is stopped:

Through the consult client, you can see that the apiserviceb service instance has been eliminated:

If you enter Ctrl + C to close a consult service in the cluster, the cluster will re elect a new leader to handle the queries and transactions of all service instances:

reference:
Ocelot official website