Gateway introduction
In fact, the gateway is to put all the APIs we have written in a unified address, expose them to the public network, and provide an entrance for access. Under the. NET Core, Ocelot can be used to help us easily access the API gateway. Similar libraries include ProxyKit , Microsoft has also released a reverse proxy library YARP.
There are not many introductions about the gateway, and there are many online articles. These are good choices. I heard that Ocelot will use YARP to rewrite it in the later stage. This article mainly practices using Ocelot in the. NET Core environment.
- Ocelot official website: https://threemammals.com/ocelot
- Ocelot documentation: https://ocelot.readthedocs.io
- GitHub: https://github.com/ThreeMammals/Ocelot
- Ocelot resource summary: https://www.cnblogs.com/shanyou/p/10363360.html
Access use
Interface example
First, create several projects for testing, and create two default API projects, Api_A and Api_B. Creating a gateway project Api_Gateway, you can select an empty template for the gateway project.
Now in Api_A and API_ Write several APIs in B, and add a field Source to the return model WeatherForecast in the default WeatherForecast controller to distinguish which API returns the data.
using System; namespace Api_A { public class WeatherForecast { public string Source { get; set; } = "Api_A"; public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } } } using System; namespace Api_B { public class WeatherForecast { public string Source { get; set; } = "Api_B"; public DateTime Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string Summary { get; set; } } }
Directly use the default method of WeatherForecastController to add api prefix to the route.
using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; namespace Api_A.Controllers { [ApiController] [Route("api/[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }).ToArray(); } } } using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; namespace Api_B.Controllers { [ApiController] [Route("api/[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random(); return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }).ToArray(); } } }
Then in Api_A and Api_B adds two controllers: ApiAController and ApiBController, and then adds several simple restful APIs.
using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; namespace Api_A.Controllers { [Route("api/[controller]")] [ApiController] public class ApiAController : ControllerBase { [HttpGet] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } [HttpGet("{id}")] public string Get(int id) { return $"Get: {id}"; } [HttpPost] public string Post([FromForm] string value) { return $"Post:{value}"; } [HttpPut("{id}")] public string Put(int id, [FromForm] string value) { return $"Put:{id}:{value}"; } [HttpDelete("{id}")] public string Delete(int id) { return $"Delete:{id}"; } } }
using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; namespace Api_B.Controllers { [Route("api/[controller]")] [ApiController] public class ApiBController : ControllerBase { [HttpGet] public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } [HttpGet("{id}")] public string Get(int id) { return $"Get: {id}"; } [HttpPost] public string Post([FromForm] string value) { return $"Post:{value}"; } [HttpPut("{id}")] public string Put(int id, [FromForm] string value) { return $"Put:{id}:{value}"; } [HttpDelete("{id}")] public string Delete(int id) { return $"Delete:{id}"; } } }
It is convenient to view the interface. Here, we add the swagger component so that we can Api_A and API_ Project B has six interfaces respectively.
Then package the docker image and run the two api projects in docker. This step can be run in any way you are familiar with.
docker build -t api_a:dev -f ./Api_A/Dockerfile . docker build -t api_b:dev -f ./Api_B/Dockerfile .
After the build is successful, specify two ports to run the api project.
docker run -d -p 5050:80 --name api_a api_a:dev docker run -d -p 5051:80 --name api_b api_b:dev
Api_A specifies the 5050 port through http://localhost:5050/swagger Open the swagger document interface, Api_B specifies the 5051 port through http://localhost:5051/swagger You can see the swagger document interface when you open it, and you're done. The next step is to focus on configuring the two api projects to Api_Gateway is in the gateway project.
configure gateway
In Gateway project API_ Add Ocelot component package in gateway.
Install-Package Ocelot
The most important thing in Ocelot is to configure routing information. Create a new ocelot.json configuration file and put our two API interface matching rules into it.
{ "Routes": [ //ApiA { "DownstreamPathTemplate": "/api/WeatherForecast", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5050 } ], "UpstreamPathTemplate": "/ApiA/WeatherForecast", "UpstreamHttpMethod": [ "Get" ] }, { "DownstreamPathTemplate": "/api/ApiA", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5050 } ], "UpstreamPathTemplate": "/ApiA", "UpstreamHttpMethod": [ "Get", "POST" ] }, { "DownstreamPathTemplate": "/api/ApiA/{id}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5050 } ], "UpstreamPathTemplate": "/ApiA/{id}", "UpstreamHttpMethod": [ "Get", "Put", "Delete" ] }, //ApiB { "DownstreamPathTemplate": "/api/WeatherForecast", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5051 } ], "UpstreamPathTemplate": "/ApiB/WeatherForecast", "UpstreamHttpMethod": [ "Get" ] }, { "DownstreamPathTemplate": "/api/ApiB", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5051 } ], "UpstreamPathTemplate": "/ApiB", "UpstreamHttpMethod": [ "Get", "POST" ] }, { "DownstreamPathTemplate": "/api/ApiB/{id}", "DownstreamScheme": "http", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 5051 } ], "UpstreamPathTemplate": "/ApiB/{id}", "UpstreamHttpMethod": [ "Get", "Put", "Delete" ] } ], "GlobalConfiguration": { "BaseUrl": "https://localhost:44335" } }
For the specific meaning of each item in the configuration file, please refer to the introduction in the official document. It is mainly to convert the DownstreamPathTemplate template template content into the UpstreamPathTemplate template content for interface access, and specify the HTTP request method, etc. BaseUrl in GlobalConfiguration is the exposed gateway address.
After setting ocelot.json, you need to use it in the code and add the configuration file in Program.cs.
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; namespace Api_Gateway { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }
Use Ocelot in Startup.cs.
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Ocelot.DependencyInjection; using Ocelot.Middleware; namespace Api_Gateway { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddOcelot(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); }); app.UseOcelot().Wait(); } } }
After completing the above operations, we try to call the interface to see if the expected data can be obtained correctly.
curl -X GET "https://localhost:44335/ApiA" curl -X GET "https://localhost:44335/ApiB" curl -X POST "https://localhost:44335/ApiA" -H "Content-Type: multipart/form-data" -F "value=ApiA" curl -X POST "https://localhost:44335/ApiB" -H "Content-Type: multipart/form-data" -F "value=ApiB" curl -X GET "https://localhost:44335/ApiA/12345" curl -X GET "https://localhost:44335/ApiB/12345" curl -X PUT "https://localhost:44335/ApiA/12345" -H "Content-Type: multipart/form-data" -F "value=ApiA" curl -X PUT "https://localhost:44335/ApiB/12345" -H "Content-Type: multipart/form-data" -F "value=ApiB" curl -X DELETE "https://localhost:44335/ApiA/12345" curl -X DELETE "https://localhost:44335/ApiB/12345" curl -X GET "https://localhost:44335/ApiA/WeatherForecast" curl -X GET "https://localhost:44335/ApiB/WeatherForecast"
It can be seen that all the interfaces in the two projects can be transferred through the address exposed by the gateway project. Is it very convenient?
This article is just a simple application. The functions of Ocelot are far more than that. It is very powerful. It can also realize request aggregation, service discovery, authentication, authentication, current limiting fuse, and built-in load balancer. Moreover, these functions can be completed only by simple configuration. If there are actual development requirements and problems, you can view the official documents and examples.
Ocelot is an excellent. NET API gateway framework
1 what is Ocelot?
Ocelot is an open source API gateway implemented with. NET Core. It has powerful functions, including routing, request aggregation, service discovery, authentication, authentication, current limiting fuse, and built-in load balancer to integrate with Service Fabric and Butterfly Tracing.
2 how to use Ocelot?
First, create two WebApi projects, WebApi01 and WebApi02, with addresses respectively https://localhost:44313 and https://localhost:44390 Among them, WebApi01 is used as the gateway and WebApi02 is used as the specific micro service Api.
Then, install Ocelot's NuGet package into the WebApi01 project.
Ocelot
Note that I installed version 17.0.0 here, and the configuration will be a little different.
Then, add services.AddOcelot in Startup.ConfigureServices;
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Autofac.WebApi", Version = "v1" }); }); services.AddOcelot(); }
Next, add app.UseOcelot().Wait() in Startup.Configure;
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ocelot.WebApi01 v1")); } app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); app.UseOcelot().Wait(); }
Next, create the ocelot.json file
{ "Routes": [ //Routing configuration (Note: ReRoutes is replaced by Routes in version 16.1) { "DownstreamPathTemplate": "/{url}", // Downstream (service provider) service routing template "DownstreamScheme": "https", // Downstream Uri scheme, http, https "DownstreamHostAndPorts": [ // Service address and port. If it is a cluster, set multiple { "Host": "localhost", "Port": 44390 } ], "UpstreamPathTemplate": "/api/{url}", // Upstream (client, service consumer) request routing template "UpstreamHttpMethod": [ "GET" ] // The allowed upstream HTTP request methods can write multiple } ], "GlobalConfiguration": { //Global configuration "BaseUrl": "https://localhost:44313 "/ / gateway external address } }
Finally, add AddJsonFile("ocelot.json", optional: false, reloadOnChange: true) in Program.CreateHostBuilder;
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, builder) => { builder.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
Ok, let's test it, https://localhost:44313/api/WeatherForecast Will you jump https://localhost:44390/WeatherForecast .
Getting started with. net web api gateway
- dotNet 5
- ocelot + consul
ocelot adds the 404 problem of the consumer service discovery gateway access prompt
After repeated comparison between Ocelot and consumer configurations, no problem is found. The service discovery is removed and the downstream list definition is restored. The gateway is available (although the list method is available, the load balancing still sends requests when the site service is not available, which does not achieve the effect of automatic switching). It is inferred that the problem is at the ocelot injection end.
- Original injection ocelot mode
services .AddOcelot(new ConfigurationBuilder() .AddJsonFile("ocelot.json", optional: false, reloadOnChange: true) .Build()) .AddConsul();
- Need to be adjusted to
services .AddOcelot() .AddConsul();
ocelot.json is injected into the program:
Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { config.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .AddJsonFile("ocelot.json") .AddEnvironmentVariables(); }) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
Then runs perfect as expected.
ocelot & consul nuget package
- Ocelot
- Ocelot.Provider.Consul
ocelot config
{ "Routes": [ { "DownstreamPathTemplate": "/api/{controller}/{action}", "UpstreamPathTemplate": "/yourPrefix/{controller}/{action}", "UpstreamHttpMethod": [ "Get", "POST" ], "DownstreamScheme": "http", "UseServiceDiscovery": true, "ReRouteIsCaseSensitive": false, "ServiceName": "YourServiceName", "LoadBalancerOptions": { "Type": "RoundRobin" } }, { "DownstreamPathTemplate": "/yourPrefix/{everything}", "UpstreamPathTemplate": "/yourPrefix/{everything}", "UpstreamHttpMethod": [ "Get", "POST" ], "DownstreamScheme": "http", "UseServiceDiscovery": true, "ReRouteIsCaseSensitive": false, "ServiceName": "YourServiceName", "LoadBalancerOptions": { "Type": "RoundRobin" } } ], "GlobalConfiguration": { "ServiceDiscoveryProvider": { "Scheme": "http", "Host": "localhost", "Port": 8500, "Type": "Consul" } } }
consul services config
{ "services": [ { "id": "webapi1", "name": "YourServiceName", "tags": [ "WebApi" ], "address": "127.0.0.1", "port": 51012, "checks": [ { "id": "AbcCheck1", "name": "Abc_Check", "http": "http://127.0.0.1:51012/weatherforecast", "interval": "10s", "tls_skip_verify": false, "method": "GET", "timeout": "1s" } ] }, { "id": "webapi2", "name": "YourServiceName", "tags": [ "WebApi" ], "address": "127.0.0.1", "port": 51013, "checks": [ { "id": "AbcCheck2", "name": "Abc1_Check", "http": "http://127.0.0.1:51013/weatherforecast", "interval": "10s", "tls_skip_verify": false, "method": "GET", "timeout": "1s" } ] } ] }
Consul, Important: Run Consul as Windows Service
- Download and install consumer https://www.consul.io/.
ps: the download is slow, more than 1 hour
2. sc create a service (change the startup mode to "automatic (delayed startup)" in the service after creation)
sc.exe create "Consul" binPath= "D:\Consul\consul.exe agent -server -ui -bootstrap-expect=1 -data-dir=D:\Consul -node=consulNode1 -client=0.0.0.0 -bind=YourLocalIP -datacenter=dc1 -config-dir=D:\Consul\consulServices.json" start= auto
Note:
(1) Bootstrap expect = 1, windows stand-alone
(2) "&" is not required to be brought into the command line, otherwise the windows service starts abnormally.
(3) Automatic startup is delayed, and the service cannot be started during system restart (it may be related to the fact that the consumer.exe system startup process is not recognized)
Postman test
- When all downstream services are available, they can be sent in turn.
- When some downstream services are unavailable, they can still be sent to the available services (without consumer, ocelot is sent even if the service is unavailable)
Create project (one server, two webapi s)
server download nuget Ocelot
Create Ocelot.json Reference address
{ "ReRoutes": [//Routing configuration //The universal template has the lowest priority. As long as there are other routing templates, other routing templates will take effect first. { "DownstreamPathTemplate": "/api/{url}",//Downstream address "DownstreamScheme": "https",//Downstream service http "DownstreamHostAndPorts": [//Downstream address and port { "Host": "localhost", "Port": 44323 }, { "Host": "localhost", "Port": 44308 } ], "UpstreamPathTemplate": "/api/{url}",//Upstream address "UpstreamHttpMethod": [ "Get","Post" ],//Upstream request mode "LoadBalancerOptions": { "Type": "RoundRobin"//When adding multiple hostandports, you need to specify routing load balancing. Leadconnection - send the request to the idle server RoundRobin - send NoLoadBalance in turn - always send it to the first request or service discovery }, "Priority":0, //Priority sets the optimization level for multiple conflicting routes "Key":"ThisKey", //Unique primary key, used by Aggregates, "FileCacheOptions": {//cache "TtlSeconds": 0, "Region": "" }, "AuthenticationOptions": {//authentication "AuthenticationProviderKey": "TestKey", "AllowedScopes": [] } "RateLimitOptions": {//Limiting the flow of requests can prevent downstream servers from crashing due to access overload "ClientWhitelist": [],//White list "EnableRateLimiting": true,//Current limiting or not "Period": "1s",//Statistical time period: 1s, 5m, 1h, 1d "PeriodTimespan": 1,//How many seconds can the client retry "Limit":2 //Maximum number of requests allowed in the statistical time period }, "QoSOptions": {//The Nuget package (Ocelot.Provider.Polly) needs to be installed to fuse the service quality, which means to stop forwarding requests to downstream services. When the downstream service has failed, the re request is also a return, and increases the burden on the downstream server and API gateway. This function is implemented by Polly "ExceptionsAllowedBeforeBreaking": 3, //How many exception requests are allowed "DurationOfBreak": 10, //Time of fusing, in seconds "TimeoutValue": 5000 //If the processing time of downstream requests exceeds how much, you can set the request to timeout }, "RouteClaimsRequirement": {//After obtaining claims through AllowedScopes in authentication, if you want to authenticate permissions, you need to add the following configuration "UserType": "registered" }, //Request header conversion UpstreamHeaderTransform and DownstreamHeaderTransform "UpstreamHeaderTransform": { "Test": "http://www.bbc.co.uk/, http://ocelot.com/", }, "DownstreamHeaderTransform": { "Location": "{DownstreamBaseUrl}, {BaseUrl}" }, "HttpHandlerOptions": { "AllowAutoRedirect": false, }, "AddClaimsToRequest": {//Claims conversion the claims conversion function can convert the values in claims into request headers, query strings, or downstream claims. For the conversion of claims, a special point is that it provides a method for parsing strings. "UserType": "Claims[sub] > value[0] > |", "UserId": "Claims[sub] > value[1] > |" } }, { "DownstreamPathTemplate": "/api/values/sex/{sex}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 44308 } ], "UpstreamPathTemplate": "/MapApi/values/sex/{sex}", "UpstreamHttpMethod": [ "Get" ], "Key": "sex" }, { "DownstreamPathTemplate": "/api/values/name/{name}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 44308 } ], "UpstreamPathTemplate": "/MapApi/values/name/{name}", "UpstreamHttpMethod": [ "Get" ], "Key": "name" }, { "DownstreamPathTemplate": "/api/values/{id}", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ { "Host": "localhost", "Port": 44308 } ], "UpstreamPathTemplate": "/MapApi/values/{id}", "UpstreamHttpMethod": [ "Get" ], "ReRouteIsCaseSensitive": true }, { "DownstreamPathTemplate": "/api/values", "DownstreamScheme": "https", "DownstreamHostAndPorts": [ //{ // "Host": "localhost", // "Port": 44323 //}, { "Host": "localhost", "Port": 44308 } ], "UpstreamPathTemplate": "/MapApi/values", "UpstreamHttpMethod": [ "Get" ], "QoSOptions": {//Quality of service and quality of service means stopping forwarding requests to downstream services. When the downstream service has failed, the re request is also a return, and increases the burden on the downstream server and API gateway. This function is implemented by Polly "ExceptionsAllowedBeforeBreaking": 3, //How many exception requests are allowed "DurationOfBreak": 10, //Time of fusing, in seconds "TimeoutValue": 5000 //If the processing time of downstream requests exceeds how much, you can set the request to timeout }, //"HttpHandlerOptions": { // "AllowAutoRedirect": false, // "UseCookieContainer": false //}, //"AuthenticationOptions": { // "AuthenticationProviderKey": "", // "AllowedScopes": [] //} } ], "Aggregates": [ //For aggregation operation, you need to add a key in ReRotes. At present, the aggregation service only supports returning json. At present, it only supports requesting the downstream service in the Get mode. Any downstream response header will be discarded. If the downstream service returns 404, the aggregation service will only return 404 if the value of the key is empty { "ReRouteKeys": [ "sex", "name" ], "UpstreamPathTemplate": "/user/{sex}/{name}" } ], "GloabalConfiguration": { "RateLimitOptions": {//Current limiting "DisableRateLimitHeaders": false,//Whether the Http header X-Rate-Limit and retry after are disabled "QuotaExceededMessage": "Customize Tips!", // Message returned when request overload is truncated "HttpStatusCode": 999, //http status returned when request overload is truncated "ClientIdHeader" : "Test" //It is used to identify the request header of the client. The default is ClientId } // "RequestIdKey": "OcrequestId", // "AdministrationPath": "/administration", //"BaseUrl": "https://api.mybusiness.com "Gateway portal" } }
Add consumer, nuget import ocelot.provider.consult, and I recreate Ocelot consult.json
{ "ReRoutes": [ { "DownstreamPathTemplate": "/api/{url}", "DownstreamScheme": "http", "UpstreamPathTemplate": "/api/{url}", "UpstreamHttpMethod": [ "Get" ], "ServiceName": "servicea",//consul node "LoadBalancerOptions": { "Type": "RoundRobin"//Responsible for balance }, "UseServiceDiscovery": true } ], "GlobalConfiguration": { // "BaseUrl": "http://127.0.0.1:8887/" "ServiceDiscoveryProvider": {//Global address "Host": "localhost", "Port": 8500, "Type": "PollConsul", "PollingInterval": 100 } } }
Import the json file into the project Program.cs
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, builder) => { builder.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath) .AddJsonFile("Ocelot.json", false, true); //.AddJsonFile("OcelotConsul.json", false, true); }) .UseStartup<Startup>();
Injection service Startup.cs
var authenticationProviderKey = "TestKey"; services.AddAuthentication() .AddJwtBearer(authenticationProviderKey, x => { x.Authority = "test"; x.Audience = "test"; });//Certification (to be studied in detail later) services.AddOcelot() //Basic call (Ocelot) .AddConsul()//Add consul service registration (nuget ocelot. Provider. Consul needs to be added) .AddPolly();//Service fuse (Ocelot.Provider.Polly)
app.UseOcelot().Wait();