Ocelot(五)- 流量限制、服務質量
作者:markjiang7m2
原文地址:https://www.cnblogs.com/markjiang7m2/p/10965300.html
源碼地址:https://gitee.com/Sevenm2/OcelotDemo
本文是我關於Ocelot系列文章的第五篇,流量限制、服務質量。Ocelot允許針對具體的服務接口進行流量限制,以便下游服務不會過載而影響響應速度。服務質量則是Ocelot根據下游服務響應的結果做出判斷,當超過一定次數的響應失敗時,Ocelot認為該服務不可用,自動產生熔斷,在一定的時間范圍內不再向該服務轉發請求,同時Ocelot也支持自定義的請求超時時間,當下游響應超過設定的時間,會認為該服務響應失敗。
關於更多的Ocelot功能介紹,可以查看我的系列文章
本文中涉及案例的完整代碼都可以從我的代碼倉庫進行下載。
案例六 流量限制
Ocelot支持流量限制,只要在路由中添加RateLimitOptions
配置即可
"RateLimitOptions": {
"ClientWhiteList": [
"markadmin"
],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 30,
"Limit": 5
}
- ClientWhiteList:數組,在請求頭中包含ClientId=xxx的請求不受限流控制,其中ClientId這個key可以修改,xxx表示配置的白名單。
- EnableRateLimiting:是否啟用限流
- Period:限流控制的時間周期,輸入單位支持s(秒), m(分), h(時), d(天)
- PeriodTimespan:恢復等待時間,當訪問超過限流限制的次數后,需要等待的時間,單位為s,如當一分鍾內訪問超過5次,就需要等待30秒后,訪問才會重新有效
- Limit:一個時間周期內允許訪問的最大次數。
下面來看案例,在ReRoutes
中添加一組路由
{
"DownstreamPathTemplate": "/api/ocelot/{postId}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 8001
}
],
"UpstreamPathTemplate": "/ocelot/ratelimit/{postId}",
"UpstreamHttpMethod": [ "Get" ],
"Priority": 2,
"RateLimitOptions": {
"ClientWhiteList": [
"markadmin"
],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 30,
"Limit": 5
}
}
在這里我只是添加多了一組路由,但下游服務接口是重用了之前使用過的接口,流量限制部分配置上面已經介紹過了。下面運行來看結果。
當我第一次訪問http://localhost:4727/ocelot/ratelimit/5
的時候,得到了正常的返回結果
而且,在請求頭可以看到流量限制的相關信息
然后,我又很快地繼續發出請求,可以看到,當我第六次發出請求的時候,得到了429 To Many Requests
的狀態
而此時的請求頭信息也會不一樣,這里可以看到Retry-After →28
,意思是在28秒后可以恢復請求
這證明,我們的Ocelot網關流量限制的作用起效了,而且與我們的配置一致。
在等待30秒之后,我重新發出請求,又得到了正常的返回結果
當我在請求頭中加上[ClientId]=markadmin
后,清空Postman的請求記錄,重新開始發出請求,無論請求多少次,Ocelot也不會對我的訪問進行限流
這里對於PeriodTimespan(恢復等待時間)的算法,我倒是有點疑問的。我一開始的理解是,基於上面案例的配置,當一分鍾內訪問超過5次時,就需要等待
Period
+PeriodTimespan
,也就是從第一個有效請求開始算起,一分半鍾之后Ocelot才會重新正常轉發請求,但是我做了幾次重復實驗,得到的結果都是:當一分鍾內訪問到第1次時,Ocelot開始進入PeriodTimespan
時間內的倒計時,也就是實際的重置時間為PeriodTimespan
。
為了更加明確地驗證這個問題,我使用OcelotConsole
項目進行測試。
首先,修改路由配置如下:
{
"DownstreamPathTemplate": "/api/ocelot/{postId}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 8001
}
],
"UpstreamPathTemplate": "/ocelot/ratelimit/{postId}",
"UpstreamHttpMethod": [ "Get" ],
"Priority": 2,
"RateLimitOptions": {
"ClientWhiteList": [
"markadmin"
],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 10,
"Limit": 5
}
}
然后,在OcelotConsole
項目中添加代碼如下:
public static async Task Main(string[] args)
{
using (var client = new HttpClient())
{
for (var i = 0; i < 100; i++)
{
Console.WriteLine(DateTime.Now);
var result = await client.GetAsync("http://localhost:4727/ocelot/ratelimit/5");
Console.WriteLine($"{result.StatusCode}, {result.Content.ReadAsStringAsync().Result}");
System.Threading.Thread.Sleep(1000);
}
Console.Read();
}
}
每隔1s就發出一次請求,運行,在命令窗口得到以下輸出:
2019/6/3 13:33:31
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:32
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:33
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:34
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:35
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:36
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:37
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:38
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:39
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:40
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:41
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:43
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:44
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:45
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:46
OK, This is from localhost:8001, path: /api/ocelot/5
2019/6/3 13:33:47
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:48
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:49
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:50
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
2019/6/3 13:33:51
TooManyRequests, API calls quota exceeded! maximum admitted 5 per 1m.
然后,我又通過修改參數,得出如下結果:
- PeriodTimespan=10, Limit=5:5個成功,5個失敗
- PeriodTimespan=10, Limit=6:6個成功,4個失敗
- PeriodTimespan=20, Limit=5:5個成功,15個失敗
- PeriodTimespan=30, Limit=5:5個成功,25個失敗
似乎都是與我前面得到的結論相同,與官方文檔不一致。
我在GitHub上提了一個issue:https://github.com/ThreeMammals/Ocelot/issues/910,期待能得到答復。
流量限制的全局配置
"RateLimitOptions": {
"DisableRateLimitHeaders": true,
"QuotaExceededMessage": "Customize Tips!",
"HttpStatusCode": 999,
"ClientIdHeader": "Test"
}
- DisableRateLimitHeaders:當設為true時,請求頭中就不會輸出流量限制的相關信息,默認值:"false"
- QuotaExceededMessage:當請求數量超出流量限制時,輸出的信息,默認值:"API calls quota exceeded! maximum admitted
{Limit}
per{Period}
." - HttpStatusCode:當請求數量超出流量限制時,輸出的狀態碼,默認值:"429"
- ClientIdHeader:標識為白名單中的客戶端的請求頭key,默認值:"ClientId"
案例七 服務質量
Ocelot支持服務質量與熔斷,意味着當下游服務不可用時,Ocelot會進行自動熔斷,不再將請求轉發給該下游服務。來看看配置
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking":3,
"DurationOfBreak":3000,
"TimeoutValue":5000
}
- ExceptionsAllowedBeforeBreaking:允許異常次數,當Ocelot轉發給該下游服務連續出現異常次數達到該數字時,Ocelot會進行自動熔斷,一段時間內不再向該下游服務轉發請求
- DurationOfBreak:熔斷時間,單位為ms(毫秒),持續多長時間不向該下游服務轉發請求
- TimeoutValue:服務質量配置項,超時時間,單位為ms(毫秒),當Ocelot向下游服務轉發請求多長時間后,自動認為該請求超時
ExceptionsAllowedBeforeBreaking 必須跟 DurationOfBreak一起使用,TimeoutValue可以單獨使用。
首先需要安裝Polly
支持程序,通過Nuget搜索Ocelot.Provider.Polly
或者通過以下命令安裝
Install-Package Ocelot.Provider.Polly
然后在Startup.cs中的ConfigureServices
方法注冊該中間件
using Ocelot.Provider.Polly;
public void ConfigureServices(IServiceCollection services)
{
……
services.AddOcelot()
.AddPolly();
……
}
在ReRoutes
中添加一組路由
{
"DownstreamPathTemplate": "/api/ocelot/{postId}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 8001
}
],
"UpstreamPathTemplate": "/ocelot/qos/{postId}",
"UpstreamHttpMethod": [ "Get" ],
"Priority": 2,
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 3000,
"TimeoutValue": 5000
}
}
為了看出熔斷效果,我將8001
端口的下游服務停止了,然后運行OcelotDemo項目
當第一次向網關發出請求時,得到500
的狀態碼
連續3次請求過后,就會得到503
的狀態碼,證明Ocelot已經產生熔斷
總結
在這篇文章中就跟大家介紹了Ocelot的兩個基礎功能,在路由中進行配置即可使用,不需要依賴於第三方的服務。當然在我實踐的過程中產生的一個疑問暫時還沒得到答案,如果有知道原因的朋友也可以給我留言,感激不盡。
結合前面的幾篇文章,大家在設計項目API網關的時候就可以綜合地考慮到底哪些功能應該配置使用,如何適當地建立路由表。例外,我在這幾個案例中為了突出要介紹的功能,基本上都是一組路由單獨配置一個功能,而在實際項目中通常都是需要在一組路由當中同時配置多個功能的,希望大家在實際項目中能夠靈活運用。今天就先跟大家介紹到這里,希望大家能持續關注我們。