- Nacos 限流最佳實踐
- Tomcat 限流
- Nginx 限流
- 限制訪問速率
- 限制併發連線數
- 黑名單
- 總結
作者:風卿(Nacos 社群 committer)
本文系投稿。如果胖友有想要投稿的內容,歡迎後臺留言。哇咔咔。
Nacos 限流最佳實踐
Nacos自開源以來,版本迭代速度很快,已經釋出了0.9版本,準備發1.0的正式版本,支援企業使用Nacos生產高可用。在生產環境,Nacos首先需要保證自身服務的穩定性,在正常的執行環境下不會出現服務掛掉的情況。當然在一些依賴的系統出問題的時候(比如磁碟和DB),Nacos服務會受到影響,需要監控系統發現這些問題並能及時的介入處理。
作為高效能的服務發現和配置管理服務,Nacos也是存在自己的效能基線的,當瞬時的高峰流量超過自身的效能基線的時候,需要對高峰流量進行限流,以保證整體服務的健康執行而不影響到其他核心應用。
Tomcat 限流
Nacos基於spring boot使用內嵌的tomcat,tomcat執行緒分為acceptor執行緒和worker執行緒,acceptor執行緒負責從核心accept佇列中取出連線並交給worker執行緒,而worker執行緒則負責處理連線(讀取引數、執行處理、傳迴響應等)
- acceptCount
當tomcat不能及時處理新的連線時,核心中新建的連線將會進入連線佇列排隊,acceptCount引數能夠設定tomcat accept連線佇列大小,當新的連線數超過acceptCount則拒絕連線,立即傳回給client,不會讓client一直等待造成響應很慢或超時
- maxConnections
接受了的連線需要由worker執行緒排程處理,當worker執行緒處理的連線數越來越多時,處理的速度會越來越慢造成client響應時間變長,需要根據系統和機器情況設定合理的maxConnections,當連線數到達maxConnections時,不再接受新的連線,讓新連線排隊等待,超出佇列長度則直接拒絕。
- maxThreads
maxThreads引數設定tomcat的最大執行緒數,過高的執行緒數會讓系統執行的負載過高、響應變慢,過低的執行緒數讓系統的資源利用率太低,需要根據實際的執行情況(CPU、IO等)設定合理的最大執行緒數。
Nginx 限流
tomcat限流只能做到自身負載的調節,在實際生產環境中還遠遠不夠,需要依賴Nacos自身的限流來提高系統的限流能力。
Nacos的open API都是基於http協議,可以很方便地使用nginx來做限流,不需要自身再開發限流模組來支援各種限流策略。nginx的基本使用以及nginx+lua模組安裝網上資源很豐富,這裡就不再介紹。
Nacos每個介面執行的代價不盡相同,一般來說寫操作代價比讀操作大,與此同時還有高頻操作和低頻操作之分,SDK呼叫的介面一般來說是高頻介面,容易出現問題,所以在生產環境需要將這些介面區別對待,根據服務自身的實際情況採取合理的限流策略,以防錯用方打垮Nacos服務。下麵介紹一下Nacos在生產環境的幾種限流場景
限制訪問速率
1、限制單個介面的請求QPS
limit_get_config對讀操作進行限流,正常使用Nacos獲取動態配置一般就啟動和執行時修改配置推送到client,獲取配置相對來說是低頻操作,如果頻繁獲取配置肯定是client有錯用或者應用不正常(比如資料平臺任務failover重試任務)
limit_req_zone $limit_key zone=limit_get_config:10m rate=10r/s;
server {
listen 8080;
server_name localhost;
location /nacos/v1/cs/configs {
if ($request_method = POST ) {
rewrite ^ /limit_publish_config_url last;
}
rewrite ^ /limit_get_config_url last;
}
location /limit_get_config_url {
set $limit_key "$remote_addr+$arg_dataid+$arg_group+$arg_tenant";
limit_req zone=limit_get_config burst=10 nodelay;
proxy_pass http://127.0.0.1:8848/nacos/v1/cs/configs;
}
}
- limit_req_zone設定限流key和記憶體大小,以及請求速率
- limit_key由[ip,dataId,group,tenant]四元組組成,可以防止client錯用頻繁訪問單個配置
- burst設定漏桶演演算法的桶的大小
- nodelay設定非延遲樣式,如果桶滿了則會馬上傳回給客戶端錯誤碼
- proxy_pass指定後端Nacos的介面url
limit_publish_config對寫操作進行限流,可以有效防止熱點寫問題。對同一個資料的高頻寫會觸發mysql的行鎖,從而導致mysql的多執行緒任務因等待行鎖排隊,最終導致mysql所有操作都超時服務不可用。這裡透過nginx lua模組獲取post請求的引數,設定limit_key
limit_req_zone $limit_key zone=limit_publish_config:10m rate=5r/s;
location /limit_publish_config_url {
set $dataId $arg_dataid;
set $group $arg_group;
set $tenant $arg_tenant;
set $limit_key "$remote_addr+$dataId+$group+$tenant";
lua_need_request_body on;
rewrite_by_lua '
ngx.req.read_body()
local post_args = ngx.req.get_post_args()
if post_args["dataId"] then
ngx.var.dataId = post_args["dataId"];
ngx.var.group = post_args["group"];
ngx.var.tenant = post_args["tenant"];
ngx.var.limit_key = ngx.var.remote_addr.."+"..ngx.var.dataId.."+"..ngx.var.group;
if ngx.var.tenant then
ngx.var.limit_key = ngx.var.limit_key.."+"..ngx.var.tenant;
end
end
';
limit_req zone=limit_publish_config burst=10 nodelay;
proxy_pass http://127.0.0.1:8848/nacos/v1/cs/configs;
}
- lua_need_request_body on;用於讀取post請求的request body
- rewrite_by_lua指令在http rewrite階段執行lua程式碼
2、限制單機訪問QPS
perclient對單個client的所有請求限制低於500QPS,可以有效防止單臺client的重試攻擊
limit_req_zone $remote_addr zone=perclient:10m rate=500r/s;
server {
listen 8080;
server_name localhost;
limit_req zone=perclient burst=250 nodelay;
location / {
proxy_pass http://127.0.0.1:8848/nacos/v1/cs/configs;
}
}
3、限制 Nacos 服務 QPS
perserver限制整個Nacos服務的QPS,Nacos的服務部署在nginx之後,nginx可以保證到達Nacos的流量不會打垮Nacos
limit_req zone=perserver burst=1000 nodelay;
限制併發連線數
/nacos/v1/cs/configs/listener介面是Nacos的長連線通道,一般來說,一個client一個長連線就可以滿足生產需求。limit_conn_client限制client的連線數不超過10個,limit_conn_server限制Nacos單機(8核16G記憶體)支撐最多9000個長連線,最多可以同時服務9000個應用節點
limit_conn_zone $remote_addr zone=limit_conn_client:10m;
limit_conn_zone $server_name zone=limit_conn_server:10m;
server {
listen 8080;
server_name localhost;
location = /nacos/v1/cs/configs/listener {
limit_conn limit_conn_client 10;
limit_conn limit_conn_server 9000;
proxy_pass http://127.0.0.1:7001/diamond-server/config.co;
tcp_nodelay on;
proxy_redirect off;
proxy_set_essay-header Host $host;
proxy_set_essay-header X-Real-IP $remote_addr;
proxy_set_essay-header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
黑名單
1、IP黑名單
當生產環境發現有錯用的client影響到Nacos服務,可以使用nginx黑名單限制client的訪問
deny 30.5.125.70;
從被限制的IP訪問Nacos
curl -X GET "http://{IP}:8080/nacos/v1/cs/configs?dataId=nacos.cfg.dataId&group;=test" -i
Nginx傳回403狀態碼給client,禁止client訪問
HTTP/1.1 403 Forbidden
Server: nginx/1.13.5
Date: Fri, 15 Mar 2019 08:34:33 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
<html>
<head><title>403 Forbiddentitle>
head>
<body bgcolor=“white”>
<center><h1>403 Forbiddenh1>center>
<hr><center>nginx/1.13.5center>
body>
html>
2、讀寫黑名單分離
有時候透過IP維度直接限制client訪問所有Nacos介面粒度過大,會導致應用服務不可用,可以將讀操作和寫操作分開,禁止client寫Nacos,依然允許其進行讀
# 1 limit read, 0 no limit
map "$remote_addr" $limit_read {
#10.2.24.252 1;
default 0;
}
# 1 limit write, 0 no limit
map "$remote_addr" $limit_write {
#10.2.24.252 1;
default 0;
}
location /some_url_to_write {
if ($limit_write = 1) {
return 403;
}
}
- map指令匹配remote_addr變數,如果$remote_addr變數在ip黑名單裡面,則設定limit_read和limit_write引數為1,否則為0
- 在寫介面中對limit_write做判斷,如果禁寫則傳回403狀態碼
3、應用黑名單
IP黑名單功能是nginx提供的基礎能力,能夠限制某些IP的訪問,但是一般一個應用會有很多臺機器,當一個應用出問題的時候,會有很多IP訪問都有問題,透過IP的維度來限制訪問達不到預期,需要有應用的維度來限制
namespace(名稱空間)是一個可以區分不同應用的維度,不同的應用一般會使用不同的namespace,這樣可以在namespace維度對服務的訪問進行限制
map "$arg_tenant" $limit_namespace {
af884cf8-1719-4e07-a1e1-3c4c105ab237 1;
#a6c745b7-fd92-4c1d-be99-6dc98abfe3dc 1;
default 0;
}
location /some_url {
if ($limit_namespace = 1) {
return 403;
}
}
透過匹配namespace是否在黑名單中來設定limit_namespace變數,然後在訪問的url中判斷limit_namespace的值,如果為1傳回403狀態碼
ak維度:使用一個ak代表一個應用,不同的應用在啟動的時候設定不同的ak。client在發起請求的時候會帶上ak引數到server端,在nginx層對請求的引數進行解析,對特定的應用的ak進行訪問限制
map "$http_Spas_AccessKey" $limit_ak {
6839c164bb344cdc93107f08eda8a136 1;
default 0;
}
location /some_url {
if ($limit_ak = 1) {
return 403;
}
}
總結
本文簡單介紹了Nacos在實際生產環境中如何透過限流來提高自身服務的穩定性,除了自身設定tomcat引數,還可以透過高效能的nginx作為前端對流量進行過濾提高限流能力。文中難免會有個別錯誤或者遺漏,如果大家有更多的限流或者提高穩定性的辦法可以在Nacos官網提出來。