2017-02-08 13 views
0

Я оцениваю nginx как ограничитель скорости для многопользовательской системы REST API. Мне нужно ограничить вызовы API идентификатором-арендатором. Например, я хочу разрешить 100 r/s для арендатора1 и только 50 r/s для арендатора2.nginx как ограничитель скорости на основе http body

Это может быть легко достигнуто при наличии разных URL-адресов, таких как: «me.com/tenant1/api» и «me.com/tenant2/api» (с директивой о местоположении).

Но в моем случае URL-адреса одинаковы для всех арендаторов «me.com/api» (я не могу это изменить). Чтобы найти идентификатор арендатора, мне нужно извлечь атрибут JSON из тела запроса, а затем проверить DB для реального идентификатора арендатора.

Можно ли ограничить_рек с моими требованиями?

Благодарим за помощь!

+0

Если вы можете поместить этот идентификатор в качестве HTTP-заголовка, вы должны сделать что-то вроде 'limit_req_zone $ http_tenant_id ...'. В противном случае вы можете использовать [карту] (http://nginx.org/en/docs/http/ngx_http_map_module.html#map), чтобы извлечь это значение из $ response_body. –

+0

спасибо @FaisalMemon. к сожалению, я не могу добавить http-заголовок. единственный способ найти идентификатор арендатора - это декодировать тело 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. Ответ на этот вызов используется для создания еще одного подзадачного вызова службы /арендатора [X]/doApi. Таким образом, я могу определять местоположения на одного арендатора и предоставлять каждому клиенту разные rate_limis.

Комментарии к этому более чем приветствуются!