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打包運作即可