2017-02-08 23 views
0

我正在評估nginx充當多租戶REST API系統的速率限制器。我需要通過tenant-id來限制API調用。 例如,我想租戶1允許100 r/s,租戶2只允許50 r/s。nginx作爲基於http正文的速率限制器

它可以當有喜歡的網址歧容易achived:「me.com/tenant1/api」和「me.com/tenant2/api」(與位置指令)。

但是,在我的案件的網址是所有租戶「me.com/api」一樣(我不能改變這一點)。 要找到tenant-id,我需要從請求的正文中提取JSON屬性,然後檢查數據庫以獲取真正的tenant-id。

是否有可能與我的要求limit_req?

感謝您的幫助!

+0

如果你可以把這個ID作爲一個HTTP頭,那麼你應該能夠做到像'limit_req_zone $ http_tenant_id ...'。否則,您可能可以使用[map](http://nginx.org/en/docs/http/ngx_http_map_module.html#map)從$ response_body中提取該值。 –

+0

謝謝@FaisalMemon。可悲的是,我無法添加http頭。找到tenant-id的唯一方法是解碼Base64 JSON體,提取一些變量並在數據庫(或緩存)中搜索它。 – Yarix

+0

做複雜的東西一樣,你可能不得不使用Lua的:https://github.com/openresty/lua-nginx-module#readme –

回答

0

我決定建立另一個服務getTenant解析正文並從數據庫中提取租戶。此服務由Nginx內部調用。 我不知道這是最好的nginx(/ openresty)解決方案,但是這是我想出了:

limit_req_zone t1Limit zone=t1Zone:10m rate=200r/s; 
limit_req_zone t2Limit zone=t2Zone:10m rate=90r/s; 

server { 

    location /api{ 
     content_by_lua_block { 
      ngx.req.read_body(); 
      local reqBody = ngx.req.get_body_data() 
      local res = ngx.location.capture("/getTenant", {method=ngx.HTTP_POST,body=reqBody}); 
      local tenantId= res.body; 
      if tenantId== "none" then 
       ngx.log(ngx.ERR, "Tenant not found!"); 
       ngx.say(tenantId); 
      else 
       ngx.req.set_header("x_myTenantId", tenantId) 
       local res2 = ngx.location.capture("/" .. tenantId .."/doApi", {method=ngx.HTTP_POST,body=reqBody}); 
       if res2.status == ngx.HTTP_OK then      
        ngx.say(res2.body); 
        ngx.exit(res2.status); 
       else 
        ngx.status = res2.status 
        ngx.exit(res2.status) 
       end 
      end; 
     } 
    } 

    location /getTenant { 
     internal; #this is not accessible from outside.    
     proxy_pass    http://UpStream1/getCustomer; 
     proxy_set_header  X-Original-URI $request_uri; 
    } 

    location /tenant1/doApi { 
     internal; #this is not accessible from outside. 
     # Proxy all requests to the AReqUpStream server group 
     proxy_pass http://UpStream2/doApi; 
     limit_req zone=tenant1Zone burst=25;    
     limit_req_log_level notice; 
    } 

    location /tenant2/doApi { 
     internal; #this is not accessible from outside.  
     # Proxy all requests to the AReqUpStream server group 
     proxy_pass http://UpStream2/doApi; 
     limit_req zone=tenant2Zone burst=10 ;#nodelay;   
     limit_req_status 409; 
     limit_req_log_level notice; 
    } 
} 

基本上,當me.com/api被調用時,一個新的子請求頒發給服務/getTenant。該呼叫的響應用於建立另一個子請求調用/tenant [X]/doApi服務。這樣我可以定義每個租戶的位置併爲每個租戶提供不同的rate_limis。

上的評論非常歡迎!