Ocelot 的【限流】【熔断】 【负载均衡】等基础功能已经实现,比较难实现的是Ocelot集成Consul这个服务发现以及服务治理,原来里面有几个比较隐秘的坑,废话不多说,上代码,中间有可能需要引用对应的dll
using Ocelot.Cache.CacheManager;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
using Ocelot.Provider.Polly;
Ocelot搭建
首先在Program.cs引用Ocelot
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(conf => conf.AddJsonFile("ocelot.json", false, true))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
然后在Startup.cs注入Ocelot
public void ConfigureServices(IServiceCollection services)
{
//services.AddControllers();
services.AddOcelot(Configuration).AddPolly().AddConsul().AddCacheManager(u=>u.WithDictionaryHandle()); //AddPolly 负责熔断
}
**************************************************
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseOcelot().Wait();
}
附上Ocelot.json文件写法,里面有对应的注释。
{
"Routes": [
{
"UpstreamPathTemplate": "/api/user/{url}",
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "47.XX.XX.XX",
"Port": 8080
}
],
"UpstreamHttpMethod": [
"Get",
"Post"
]
},
{
"UpstreamPathTemplate": "/api/server/{url}",
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"UseServiceDiscovery": true,
"ServiceName": "HelloServer", //ServiceName 服务名称要跟Consul的服务名称一致【ServiceName DownstreamHostAndPorts】不可以同时为空,也不可以同时存在
"Key": "server", //声明聚合路由
//"DownstreamHostAndPorts": [ //使用服务发现后需要注释掉
// {
// "Host": "localhost",
// "Port": 8082
// },
// {
// "Host": "localhost",
// "Port": 8083
// }
//],
"UpstreamHttpMethod": [
"Get",
"Post"
],
"LoadBalancerOptions": {
"Type": "RoundRobin" //负载均衡,轮询机制 LeastConnection/RoundRobin/NoLoadBalancer/CookieStickySessions
},
"ReRouteIsCaseSensitive": false, //重写路由是否区分大小写
//缓存
//"FileCacheOptions": {
// "TtlSeconds": 30, //缓存时间(秒)
// "Region": "CacheArea" //缓存区(名称自定义),表示改配置缓存放到哪个区域,可以在配置管理中进行维护
//},
//限流
"RateLimitOptions": {
"ClientWhitelist": [], //数组中的客户机不会受到路由限制的影响使用方式:在客户端请求头中添加key:ClientId,value为网关配置白名单。
"EnableRateLimiting": true, //是否启用路由限制。
"Period": "1s", //指定周期,如1s、5m、1h、1d等。
"PeriodTimespan": 1, //指定我们可以在一定秒数后重试【限流后多久可以重试】。
"Limit": 1 //指定客户端在定义的时间段内可以发出的最大请求数
}
}
],
"GlobalConfiguration": {
//"BaseUrl": "http://localhost:9070"
//全局限流
"RateLimitOptions": {
"DisableRateLimitHeaders": false,
"QuotaExceededMessage": "Customize Tips!", //限流返回的信息。
"HttpStatusCode": 999 //限流返回状态码
},
//服务发现
"ServiceDiscoveryProvider": {
"Host": "localhost", //上线后需要取线上地址
"Port": 8500,
"Token": "footoken",
"Provider": "consul", //Consul PollConsul
"PollingInterval": 100
},
//服务质量 熔断?
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 2, //在熔断之前允许的异常次数
"DurationOfBreak": 5000, //熔断时长 ,单位毫秒
"TimeoutValue": 2000 //请求超时设置, 单位毫秒
}
}
}
主要要关注是以下的坑
- Ocelot 最新版本的V17.0的Routes跟以前版本不一样,以前版本是ReRoutes
- "UseServiceDiscovery": true 开启服务发现的时候需要定义ServerName,ServerName的名字需要跟Consul的服务名字一致,同时需要注释DownstreamHostAndPorts,如果不开启服务发现"UseServiceDiscovery": false 的话需要定义DownstreamHostAndPorts,否则将会报错
Consul搭建
新建一个demo项目,appsettings.json新增Consul配置
"Consul": {
"ServiceName": "HelloServer",
"ServiceIP": "localhost",
"ServicePort": 8082,
"Tags": "Server_Tag1,Server_Tag2",
"ServiceHealthCheck": "http://localhost:8082/Heather/HealthCheck",
"ConsulAddress": "http://localhost:8500"
}
string serverName = Configuration.GetSection("Consul:ServiceName").Value;
string clientUrl = Configuration.GetSection("Consul:ConsulAddress").Value;
string agentIP = Configuration.GetSection("Consul:ServiceIP").Value;
string port = Configuration.GetSection("Consul:ServicePort").Value;
string heartbeatUrl = Configuration.GetSection("Consul:ServiceHealthCheck").Value;
string tags = Configuration.GetSection("Consul:Tags").Value;
var agentTags = new string[] { };
if (!string.IsNullOrEmpty(tags))
{
agentTags = tags.Split(',');
}
ConsulClient client = new ConsulClient(obj =>
{
obj.Address = new Uri(clientUrl);
obj.Datacenter = serverName;
});
client.Agent.ServiceRegister(new AgentServiceRegistration()
{
ID = $"{serverName}_{Guid.NewGuid()}",
Name = serverName,
Address = agentIP,
Port = Convert.ToInt32(port),
Tags = agentTags,
Check = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
Interval = TimeSpan.FromSeconds(15),
HTTP = heartbeatUrl,
Timeout = TimeSpan.FromSeconds(5)
}
}).Wait();
接着新建心跳Controller
[ApiController]
[Route("[controller]/[action]")]
public abstract class AbstractController : ControllerBase
{
}
public class HeatherController: AbstractController
{
[HttpGet]
public IActionResult HealthCheck()
{
return Ok();
}
}
将Demo打包运行即可