.NET Core Consul Ocelot Demo

本地环境

Win10

虚拟机 CentOS7

CentOS中提前安装好 docker-compose 和 dotnet 运行环境

 

WebAPI (.NET 5.0):

  NuGet:  

    Consul  (1.6.10.4)

APIGateway(.NET 5.0):

  NuGet:

    Ocelot (17.0.0)

    Ocelot.Provider.Consul (17.0.0)

 

一、Consul  集群

使用 docker-compose.yaml 快速部署一个 Consul  集群

version: '3'

services:
  Consul-1: 
    image: consul
    command: agent -server -client=0.0.0.0 -bootstrap-expect=3 -node=Consul-1 -data-dir=/data
    volumes:
      - ./Consul-1:/data
  Consul-2:
    image: consul
    command: agent -server -client=0.0.0.0 -retry-join=Consul-1 -node=Consul-2 -data-dir=/data
    volumes:
      - ./Consul-2:/data
    depends_on:
      - Consul-1
  Consul-3:
    image: consul
    command: agent -server -client=0.0.0.0 -retry-join=Consul-1 -node=Consul-3 -data-dir=/data
    volumes:
      - ./Consul-3:/data
    depends_on:
      - Consul-1
  Consul-4:
    image: consul
    command: agent -client=0.0.0.0 -retry-join=Consul-1 -ui -node=Consul-4 -data-dir=/data  
    ports:
      - 8500:8500
    volumes:
      - ./Consul-4:/data
    depends_on:
      - Consul-2
      - Consul-3

 

 

docker-compose up 

启动

win10 中访问 http://CentOSIP:8500/

 

 

 

 二、WebAPI 

定义两个控制器

using Microsoft.AspNetCore.Mvc;

namespace WebAPI.Controllers
{
    [Route("health")]
    [ApiController]
    public class healthController : Controller
    {
        public IActionResult Get() => Ok("ok");
    }
}




using Microsoft.AspNetCore.Mvc;

namespace WebAPI.Controllers
{
    [Route("api/Demo")]
    [ApiController]
    public class DemoController : Controller
    {
        [HttpGet]
        public IActionResult Get(string id)
        {
            return Json(new
            {
                id = id
            });
        }

        [HttpPost]
        public IActionResult Post(UserDto userDto)
        {
            return Json(new
            {
                UserName = userDto.UserName,
                UserId = userDto.UserId
            });
        }
    }
}

注册 Consul  

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "Consul": {
    "ServiceId": "11111111111111",
    // 当前项目的名字,如果部署多个实例,该名字必须一致
    "ServiceName": "WebAPI",
    // 当前项目部署后的IP地址
    "ServiceIP": "192.168.214.129",
    // 当前项目部署后的端口号
    "ServicePort": 8001,
    // 当前项目部署后 Consul 调用该接口判断服务是否健康
    "ServiceHealthCheck": "http://192.168.214.129:8001/health",

    // Consul 的地址
    "ConsulAddress": "http://192.168.214.129:8500"
  }
}

安装 appsettings.json 中的 Consul 节点定义一个类型

namespace WebAPI
{
    public class ServiceEntity
    {
        public string ServiceId { get; set; }
        public string ServiceName { get; set; }
        public string ServiceIP { get; set; }
        public int ServicePort { get; set; }
        public string ServiceHealthCheck { get; set; }
        public string ConsulAddress { get; set; }
    }
}

Startup

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    ServiceEntity serviceEntity = new ServiceEntity
    {
        ServiceId = Configuration["Consul:ServiceId"],
        ServiceIP = Configuration["Consul:ServiceIP"],
        ServiceName = Configuration["Consul:ServiceName"],
        ServicePort = Convert.ToInt32(Configuration["Consul:ServicePort"]),
        ServiceHealthCheck = Configuration["Consul:ServiceHealthCheck"],
        ConsulAddress = Configuration["Consul:ConsulAddress"]
    };
    app.UseConsul(lifetime, serviceEntity);
    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

ConsulBuilderExtensions.cs

public static class ConsulBuilderExtensions
{
    public static IApplicationBuilder UseConsul(this IApplicationBuilder app, IHostApplicationLifetime lifetime, ServiceEntity serviceEntity)
    {
        var consulClient = new ConsulClient(x =>
        {
            x.Address = new Uri(serviceEntity.ConsulAddress);
        });

        var registration = new AgentServiceRegistration()
        {
            ID = Guid.NewGuid().ToString(),
            Name = serviceEntity.ServiceName,// 服务名
            Address = serviceEntity.ServiceIP, // 服务绑定IP
            Port = serviceEntity.ServicePort, // 服务绑定端口
            Check = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔
                HTTP = serviceEntity.ServiceHealthCheck,//健康检查地址
                Timeout = TimeSpan.FromSeconds(5)
            }
        };

        // 服务注册
        consulClient.Agent.ServiceRegister(registration).Wait();

        // 应用程序终止时,服务取消注册
        lifetime.ApplicationStopping.Register(() =>
        {
            consulClient.Agent.ServiceDeregister(registration.ID).Wait();
        });
        return app;
    }
}

代码部分就完成了,打包,发布到 CentOS中

 

 WebAPI1 和 WebAPI2 各放一份

 其中 WebAPI2 中的 appsettings.json 中的 ServicePort 和 ServiceHealthCheck 中的端口号修改一下,两个实例启动的时候 WebAPI1 会占用

dotnet WebAPI.dll --urls="http://*:8001"
dotnet WebAPI.dll --urls="http://*:8002"

启动两个 WebAPI 实例

 

 

 

 启动完成后过稍等片刻 

等待的时间通过下面这句代码控制

DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5), //服务启动多久后注册

 

 

 

 浏览器中验证一下两个节点是否 OK

 

三、网关

新建一个 WebAPI项目

添加一个 Ocelot.json 配置文件

{
  "Routes": [
    {
      "UpstreamPathTemplate": "/{url}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put" ],

      "DownstreamPathTemplate": "/{url}",
      "DownstreamScheme": "http",
      "ServiceName": "WebAPI",
      "LoadBalancerOptions": {
        "Type": "LeastConnection"
      }
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "192.168.214.129",
      "Port": 8500,
      "Type": "PollConsul",
      "PollingInterval": 100 
    }
  }
}

 Program.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace APIGateway
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                .ConfigureAppConfiguration(configure => {
                    configure.AddJsonFile("Ocelot.json");
                });
    }
}

StartUp.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;

namespace APIGateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot().AddConsul();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseOcelot().Wait();
        }
    }
}

 

 

 只需要这么多就够了,其他的都可以删掉

发布后部署到CentOS中

dotnet APIGateway.dll --urls="http://*:9001"

启动网关

 

 

 

 

 

 浏览器通过网关访问 某个 WebAPI 的实例

 

 

Demo中通过 Consul 实现网关与 WebAPI实例 之间的解耦 

网关不停的轮询从 Consul 中获取 WebAPI可用的实例

只要 Consul  中还有一个 WebAPI的实力,整个服务都是OK的

posted @ 2022-03-21 15:44  乔安生  阅读(157)  评论(0编辑  收藏  举报