案例资料链接:https://download.csdn.net/download/ly1h1/90733765
1.效果
实现两个微服务ServerAPI1和ServerAPI2的负载均衡以及高可用。具体原理,看以下示意图。
2.部署条件
1、腾讯云的轻量化服务器
2、WindowServer2016
3、.NETCore7.0
4、Negut 包:Consul1.7.14.7、Ocelot16.0.1、Ocelot.Provider.Conusl16.0.1
5、.NET捆绑托管 dotnet-hosting-9.0.4-win.exe
3.开放端口
2.1 云服务安全组端口开放
端口:5000,是服务1
端口:5001,是服务2
端口:5010,是Ocelot网关
端口:8050/8030/8031/8032,是Consul的端口
-
8500
是核心 API 端口,必知; -
8030/31/32
需结合具体配置,可能是用户自定义的服务或工具端口。
2.2 服务器配置入栈站规则
3.Consul配置(部署可参考链接Consul安装部署(Windows环境)-CSDN博客)
{
"datacenter": "dc1",
"data_dir": "C:\\consul\\data",
"node_name": "node-10-4-0-7",
"bind_addr": "0.0.0.0",
"advertise_addr": "10.0.4.7",(云服务内网的IP)
"client_addr": "0.0.0.0",
"ports": {
"http": 8500,
"dns": 8600,
"grpc": -1
},
"ui_config": {
"enabled": true
},
"server": true,
"bootstrap": true,
"log_level": "ERROR",
"disable_update_check": true,
"performance": {
"raft_multiplier": 3,
"leave_drain_time": "5s"
}
}
4.Ocelot代码
4.1 Progrm.CS
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul; // 引入 Consul 支持
var builder = WebApplication.CreateBuilder(args);
// 1. 配置 Kestrel 只监听 5010 端口(不绑定特定 IP)
builder.WebHost.UseUrls("http://*:5010"); // 监听所有网络接口
// 或者用 ConfigureKestrel(更灵活):
// builder.WebHost.ConfigureKestrel(serverOptions => serverOptions.ListenAnyIP(5010));
// 2. 加载 Ocelot 配置
builder.Configuration.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
// 3. 添加 Ocelot 和 Consul 支持
builder.Services
.AddOcelot(builder.Configuration)
.AddConsul(); // 关键:注入 Consul 提供程序
var app = builder.Build();
// 4. 使用 Ocelot 中间件
await app.UseOcelot();
app.Run();
4.2 Ocelot.JSON
{
"Routes": [
{
"DownstreamPathTemplate": "/api/values/{action}",
"UpstreamPathTemplate": "/api/values/{action}",
"ServiceName": "ServerAPI",
"LoadBalancerOptions": {
"Type": "RoundRobin"
},
"ServiceDiscoveryProvider": {
"Type": "Consul",
"Host": "43.162.118.209",
"Port": 8500,
"PollingInterval": 2000, // 每2秒从Consul拉取最新健康实例
"SkipItemsWithUnhealthyStatus": true
}
}
]
}
5.服务1代码
5.1 Program.CS
using Consul;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// 添加控制器和Swagger
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ServerAPI1", Version = "v1" });
});
// 注册Consul客户端
builder.Services.AddSingleton<IConsulClient>(new ConsulClient(c =>
{
c.Address = new Uri("http://192.168.0.102:8500");
}));
// 配置Kestrel(可选,根据实际需求)
builder.WebHost.UseUrls("http://*:5000");
var app = builder.Build();
// 健康检查端点
//app.MapGet("/health", () => Results.Ok("Healthy"));
// 健康检查端点(模拟5000端口故障)
//app.MapGet("/health", (HttpContext context) =>
//{
// var port = context.Request.Host.Port;
// return port == 5000 ? Results.StatusCode(503) : Results.Ok("Healthy (Port: " + port + ")");
//});
//app.MapGet("/health", () => Results.Ok("Healthy (Port: 5000)"));
app.MapGet("/health", () =>
{
// 基础存活检查(不依赖任何业务逻辑)
return Results.Ok("Alive");
});
// 注册到Consul
var consulClient = app.Services.GetRequiredService<IConsulClient>();
var registration = new AgentServiceRegistration
{
ID = "ServerAPI-5000",
Name = "ServerAPI",
Address = "192.168.0.102",
Port = 5000,
Check = new AgentServiceCheck
{
HTTP = "http://192.168.0.102:5000/health",
Interval = TimeSpan.FromSeconds(3), // 每3秒检查一次
Timeout = TimeSpan.FromSeconds(1), // 1秒无响应视为失败
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10) // 10秒后自动移除
}
};
consulClient.Agent.ServiceRegister(registration).Wait();
// 其他中间件
app.UseRouting();
app.MapControllers();
// 配置Swagger(开发环境)
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ServerAPI1 v1"));
}
// 路由和控制器
app.UseRouting();
app.MapControllers();
// 应用停止时注销服务
app.Lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(registration.ID).Wait();
});
app.Run();
5.2 接口
using Microsoft.AspNetCore.Mvc;
using ServerAPI1.Models;
namespace ServerAPI1.Controllers
{
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class ValuesController : ControllerBase
{
/// <summary>
/// 获取服务基本信息
/// </summary>
[HttpGet("info")]
[ProducesResponseType(200)]
public IActionResult GetInfo()
{
return Ok(new { Service = "ServerAPI1", Port = 5000 });
}
/// <summary>
/// 计算服务 (A+B)
/// </summary>
/// <param name="model">输入参数</param>
[HttpPost("calculate")]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
public IActionResult Calculate([FromBody] TestModel model)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
return Ok(new { Result = 123 + 321, Input = model });
}
[HttpGet("getail")] // 实现 /api/values/getail
public IActionResult Get()
{
return Ok("This is /api/values/getail from ServerAPI1");
}
[HttpGet("setail")]
public IActionResult Gett()
{
return Ok("This is /api/values/setail from " + GetType().Assembly.GetName().Name);
}
}
}
5.3 数据模型
using System.ComponentModel.DataAnnotations;
namespace ServerAPI1.Models
{
public class TestModel
{
[Required]
[StringLength(100)]
public string AAA { get; set; }
[StringLength(200)]
public string BBB { get; set; }
}
}
6.服务2代码
6.1 Program.CS
using Consul;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
// 添加控制器和Swagger
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ServerAPI2", Version = "v1" });
});
// 注册Consul客户端
builder.Services.AddSingleton<IConsulClient>(new ConsulClient(c =>
{
c.Address = new Uri("http://192.168.0.102:8500");
}));
// 配置Kestrel(可选,根据实际需求)
builder.WebHost.UseUrls("http://*:5001");
var app = builder.Build();
// 健康检查端点
//app.MapGet("/health", () => Results.Ok("Healthy"));
// 健康检查端点(模拟5000端口故障)
//app.MapGet("/health", (HttpContext context) =>
//{
// var port = context.Request.Host.Port;
// return port == 5000 ? Results.StatusCode(503) : Results.Ok("Healthy (Port: " + port + ")");
//});
//app.MapGet("/health", () => Results.Ok("Healthy (Port: 5001)"));
app.MapGet("/health", () =>
{
// 基础存活检查(不依赖任何业务逻辑)
return Results.Ok("Alive");
});
// 注册到Consul
var consulClient = app.Services.GetRequiredService<IConsulClient>();
var registration = new AgentServiceRegistration
{
ID = "ServerAPI-5001",
Name = "ServerAPI",
Address = "192.168.0.102",
Port = 5001,
Check = new AgentServiceCheck
{
HTTP = "http://192.168.0.102:5001/health",
Interval = TimeSpan.FromSeconds(3), // 每3秒检查一次
Timeout = TimeSpan.FromSeconds(1), // 1秒无响应视为失败
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10) // 10秒后自动移除
}
};
consulClient.Agent.ServiceRegister(registration).Wait();
// 其他中间件
app.UseRouting();
app.MapControllers();
// 配置Swagger(开发环境)
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ServerAPI2 v1"));
}
健康检查端点
//app.MapGet("/health", () => Results.Ok("Healthy"));
// 路由和控制器
app.UseRouting();
app.MapControllers();
// 应用停止时注销服务
app.Lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(registration.ID).Wait();
});
app.Run();
6.2 接口
using Microsoft.AspNetCore.Mvc;
using ServerAPI2.Models;
namespace ServerAPI2.Controllers
{
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class ValuesController : ControllerBase
{
/// <summary>
/// 获取服务元数据
/// </summary>
[HttpGet("info")]
[ProducesResponseType(200)]
public IActionResult GetInfo()
{
return Ok(new { Service = "ServerAPI2", Port = 8016 });
}
/// <summary>
/// 字符串转换服务
/// </summary>
/// <param name="model">输入参数</param>
[HttpPost("calculate")]
[ProducesResponseType(200)]
[ProducesResponseType(400)]
public IActionResult Transform([FromBody] TestModel model)
{
if (!ModelState.IsValid) return BadRequest(ModelState);
return Ok(new { Result = $"{model.AAA}-{model.BBB}".ToUpper() });
}
[HttpGet("getail")] // 实现 /api/values/getail
public IActionResult Get()
{
return Ok("This is /api/values/getail from ServerAPI2");
}
[HttpGet("setail")]
public IActionResult Gett()
{
return Ok("This is /api/values/setail from " + GetType().Assembly.GetName().Name);
}
}
}
6.3 数据模型
using System.ComponentModel.DataAnnotations;
namespace ServerAPI2.Models
{
public class TestModel
{
[Required]
[StringLength(100)]
public string AAA { get; set; }
[StringLength(200)]
public string BBB { get; set; }
}
}
7.发布形成Publish文件包
7.发布文件效果
8.运行Consul
8.1 显示双击文件夹内的Consul.exe
8.2 cmd进入Consul文件夹,运行指令:consul agent --config-file=C:\consul\config.json
9.运行服务
8.Publish文件夹内的Server1.exe\Server2.exe\OcelotDemo.exe,运行2个业务服务和网关服务