歡迎光臨
每天分享高質量文章

阿裡最新開源配置中心和註冊中心: Nacos 限流最佳實踐

  • 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官網提出來。

赞(0)

分享創造快樂