Ocelot\Consul\.NetCore的微服务应用案例
案例资料链接: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/getailpublic 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/getailpublic 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个业务服务和网关服务