.NET+Consul+Ocelot实现微服务分布式部署

发布时间 2023-03-24 18:39:42作者: D调灬仔

一、ConSul和Ocelot

Consul:是一个服务网格解决方案,提供了一个功能齐全的控制平面,具有服务发现、配置和分段功能。这些功能中的每一项都可以根据需要单独使用,也可以一起使用来构建一个完整的服务网格。Consul需要一个数据平面,并支持代理和原生集成模型。Consul提供了一个简单的内置代理,因此一切都可以开箱即用,具有注册中心、配置中心、健康检查等很好用的功能。

Ocelot:Ocelot首先通过配置将HttpRequest对象保存到一个指定的状态直到它到达用来创建HttpRequestMessage对象并将创建的HttpRequestMessage对象发送到下游服务中的请求构造中间件。通过中间件来发出请求是Ocelot管道中做的最后一件事。它不会再调用下一个中间件。下游服务的响应会存储在每个请求 scoped repository中,并作为一个请求返回到Ocelot管道中。有一个中间件将HttpResponseMessage映射到HttpResponse对象并返回给客户端。
接下来是你使用Ocelot是可能会使用的配置。

二、环境和版本

Demo框架:.Net6

Docker:20.10.18

Consul:1.6.10.8

Ocelot:18.0.0

三、Docker里面安装Consul

Docker安装可参考:https://www.cnblogs.com/chj929555796/p/16779240.html

执行拉取镜像命令:

docker pull consul

创建Consul容器:

docker run -d --name consul -p 8500:8500 consul:latest 

四、Demo事例

一、Demo结构说明

Client-WebService:网关项目端口5001

Order-OrderApi:订单服务,端口5002、5004、5006

Product-ProductApi:产品服务,端口5003、5005

 二、Consul扩展类

ConsulRegisterOptions:Consul注册服务节点类

namespace OrderApi.ConsulExtends
{
    public class ConsulRegisterOptions
    {
        public string ConsulAddress { get; set; }        
        public string Healthcheck { get; set; }
        public string ServiceName { get; set; }
        public string Ip { get; set; }
        public int Port { get; set; }
    }
}

ConsulExend:Consul注入服务延伸类

namespace OrderApi.ConsulExtends
{
    public static class ConsulExend
    {
        public static void AddConsulRegister(this IServiceCollection services)
        {
            services.AddTransient<IConsulRegister, ConsulRegister>();
        }
    }

}

IConsulRegister:注册服务接口类

namespace OrderApi.ConsulExtends
{
    public interface IConsulRegister
    {
        Task ConsulRegisterAsync();
    }
}

ConsulRegister:注册服务实现类

using Consul;
using Microsoft.Extensions.Options;

namespace OrderApi.ConsulExtends
{
    public class ConsulRegister : IConsulRegister
    {
        private readonly ConsulRegisterOptions _consulRegisterOptions;
        public ConsulRegister(IOptionsMonitor<ConsulRegisterOptions> consulRegisterOptions)
        {
            _consulRegisterOptions = consulRegisterOptions.CurrentValue;
        }

        public async Task ConsulRegisterAsync()
        {
            var client = new ConsulClient(options =>
            {
                options.Address = new Uri(_consulRegisterOptions.ConsulAddress);// Consul客户端地址
            });
            var registration = new AgentServiceRegistration
            {
                ID = Guid.NewGuid().ToString(),//唯一ID
                Name = _consulRegisterOptions.ServiceName,//服务名
                Address = _consulRegisterOptions.Ip,//服务绑定IP
                Port = _consulRegisterOptions.Port,//服务绑定端口
                Check = new AgentServiceCheck
                {
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(1),//服务启动后多久注册
                    Interval = TimeSpan.FromSeconds(5),//健康检查时间
                    HTTP = $"http://{_consulRegisterOptions.Ip}:{_consulRegisterOptions.Port}{_consulRegisterOptions.Healthcheck}",//检查地址
                    Timeout = TimeSpan.FromSeconds(10),//超时时间
                }

            };

            await client.Agent.ServiceRegister(registration);
        }
    }
}

HealthCheckMiddleware:Consul检测服务中间件类

using System.Net;

namespace OrderApi.ConsulExtends
{
    public static class HealthCheckMiddleware
    {
        public static void UseHealthCheckMiddleware(this IApplicationBuilder app, string checkPath = "/healthcheck")
        {
            int code = 0;
            app.Map(checkPath, applicationBuilder => applicationBuilder.Run(async context =>
            {
                Console.WriteLine("this is health check");
                code = context.Response.StatusCode;
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                await context.Response.WriteAsync("OK");
            }));
        }
    }
}

 以上几个类OrderApi和ProductApi都一样,用于在服务启动时注册服务到Consul中。

三、appsettings.json和Program.cs配置

1、OrderApi服务

 "ConsulRegister": {
    "ConsulAddress": "http://consul-IP:8500", //consul客户端地址
    "Healthcheck": "/healthcheck", //consul健康检查地址
    "ServiceName": "OrderService",//服务名称,服务启动时会注册到Consul中的Service
    "Ip": "consul-IP",
    "Port": "服务端口号"//定义一个就行,后期容器启动时可以通过命令映射更改
  },
using OrderApi.ConsulExtends;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//注册consul服务
builder.Services.Configure<ConsulRegisterOptions>(builder.Configuration.GetSection("ConsulRegister"));
builder.Services.AddConsulRegister();
builder.WebHost.UseUrls("http://*:5002");
var app = builder.Build();
//注册consul服务
app.Services.GetService<IConsulRegister>()!.ConsulRegisterAsync();
// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
//app.UseExceptionHandler("/Home/Error");
app.UseSwagger();
app.UseSwaggerUI();
//}

app.UseAuthorization();

app.MapControllers();
//注册consul健康检查中间件
app.UseHealthCheckMiddleware();
app.Run();

2、ProductApi服务

 "ConsulRegister": {
    "ConsulAddress": "http://consul-IP:8500", //consul客户端地址
    "Healthcheck": "/healthcheck", //consul健康检查地址
    "ServiceName": "ProductService",//服务名称,服务启动时会注册到consul中的Service
    "Ip": "consul-IP",
    "Port": "服务端口号"//定义一个就行,后期容器启动时可以通过命令映射更改
  },
using ProductApi.ConsulExtends;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//注册consul服务
builder.Services.Configure<ConsulRegisterOptions>(builder.Configuration.GetSection("ConsulRegister"));
builder.Services.AddConsulRegister();
builder.WebHost.UseUrls("http://*:5003");
var app = builder.Build();
//注册consul服务
app.Services.GetService<IConsulRegister>()!.ConsulRegisterAsync();

// Configure the HTTP request pipeline.
//if (app.Environment.IsDevelopment())
//{
app.UseSwagger();
app.UseSwaggerUI();
//}

app.UseAuthorization();

app.MapControllers();
app.UseHealthCheckMiddleware();
app.Run();

截止为止,两个服务注册Consul相关已完成,启动服务时会自动把服务注册到Consul

查看详细服务

3、WebService网关项目

需要安装Nuget包:Consul、Ocelot、Ocelot.Provider.Consul

新增文件:ocelot.json

{
  "Routes": [
    {
      "UseServiceDiscovery": true, // 使用服务发现
      "DownstreamPathTemplate": "/orderapi/{url}", //下游的路由模板,即真实处理请求的路径模板
      "UpstreamPathTemplate": "/orderapi/{url}", //上游请求的模板,即用户真实请求的链接
      "DownstreamScheme": "http",
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "consul-ip",
      //    "Port": 5002
      //  },
      //  {
      //    "Host": "consul-ip",
      //    "Port": 5004
      //  },
      //  {
      //    "Host": "consul-ip",
      //    "Port": 5006
      //  }
      //],
      "ServiceName": "OrderService", // 服务名称-这个要跟应用注册时写的名字一致,否则无效
      //配置负载均衡
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //负载均衡 RoundRobin(轮询)/LeastConnection(最少连接数)
      },
      "UpstreamHttpMethod": [ "Options", "Get", "Post", "Put", "Delete" ],
      "ReRoutesCaseSensitive": false, // 路由大小写敏感设置
      "key": "orderapi"
    },
    {
      "UseServiceDiscovery": true, // 使用服务发现
      "DownstreamPathTemplate": "/productapi/{url}", //下游的路由模板,即真实处理请求的路径模板
      "UpstreamPathTemplate": "/productapi/{url}", //上游请求的模板,即用户真实请求的链接
      "DownstreamScheme": "http",
      //"DownstreamHostAndPorts": [
      //  {
      //    "Host": "consul-ip",
      //    "Port": 5003
      //  },
      //  {
      //    "Host": "consul-ip",
      //    "Port": 5005
      //  }
      //],
      "ServiceName": "ProductService", // 服务名称-这个要跟应用注册时写的名字一致,否则无效
      //配置负载均衡
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //负载均衡方式
      },
      "UpstreamHttpMethod": [ "Options", "Get", "Post", "Put", "Delete" ],
      "ReRoutesCaseSensitive": false, // 路由大小写敏感设置
      "key": "productapi"
    }
  ],
  "GlobalConfiguration": { // 网关全局配置
    "BaseUrl": "http://consul-ip:5001", //当前网关项目的地址
    "ServiceDiscoveryProvider": { // 服务发现的配置
      "Scheme": "http",
      "Host": "consul-ip", //Consul 服务的访问ip
      "Port": 8500, //Consul 服务的访问端口
      "Type": "Consul"
    }
  }
}

WebService-Program.cs

using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Configuration.AddJsonFile("appsettings.json", true, true);
//支持在更改时重新加载 json 配置文件
builder.Configuration.AddJsonFile("ocelot.json",false,true).AddEnvironmentVariables();
builder.Services.AddOcelot().AddConsul();
builder.WebHost.UseUrls("http://*:5001");
var app = builder.Build();
// Configure the HTTP request pipeline.
//if (!app.Environment.IsDevelopment())
//{
//    app.UseExceptionHandler("/Error");
//}
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.UseOcelot().Wait();
app.Run();

五、Docker构建

一、上传项目文件夹到云服务器

 

 

二、切换到每个目录下依次执行构建镜像及创建容器

1、OrderApi

切换目录:cd /Order
构建镜像:sudo docker build -t microservicesorder .
创建容器:
docker run -d -p 5002:5002 --name orderapitest microservicesorder --ConsulRegister:Port="5002"
docker run -d -p 5004:5002 --name orderapitest1 microservicesorder --ConsulRegister:Port="5004"
docker run -d -p 5006:5002 --name orderapitest2 microservicesorder --ConsulRegister:Port="5006"
这里需要注意ConsulRegister:Port的地方,以往我们构建容器方式是
docker run --name orderapitest -p 5002:5002 microservicesorder
但是此处多了--ConsulRegister:Port="5006",此段代码就是在访问时去替换当前服务appsettings.json文件ConsulRegister节点的Port

2、ProductApi

切换目录:cd /Product
构建镜像:sudo docker build -t microservicesproduct .
创建容器:
docker run -d -p 5003:5003 --name productapitest microservicesproduct --ConsulRegister:Port="5003"
docker run -d -p 5005:5003 --name productapitest1 microservicesproduct --ConsulRegister:Port="5005"

3、Client网关

切换目录:cd /Client
构建镜像:sudo docker build -t microservicesclient .
创建容器:docker run --name clienttest -p 5001:5001 microservicesclient

 

服务健康监测运行情况

 

 访问Order服务

 

 

  访问Product服务

 

 参考文献:https://blog.xiaobaicai.fun/share/7386.html