Skip to content

adobe-apiplatform/api-gateway-request-tracking

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

80 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

api-gateway-request-tracking

Usage and Tracking Handler for the API Gateway.

Table of Contents

Status

This module is under active development and is considered production ready.

Dependencies

This library requires an nginx build with OpenSSL, the ngx_lua module, LuaJIT 2.0, api-gateway-zmq-logger, api-gateway-request-validation, and api-gateway-hmac module.

Design considerations

This module is usually used for enforcing rate limiting and throttling policies. It is by design that business rules for the policies are not implemented in this module. A separate microservice should obtain usage information in order to track the requests and then notify the gateways when an action needs to be taken.

Bellow are the design principles used by this module:

  • Async. Requests are not blocked while checking the remaining quotas.
    • Quotas are tracked into a centralized microservice.
    • The microservice should notify all the Gateway nodes when a limit is reached.
    • The Gateways only enforce policies but are not aware of the business rules of the policies.
  • Non blocking. The impact on the request time should be as little as possible. Some actions are still blocking when stopping or delaying requests but for other cases such as logging the requests the actions should be non-blocking.
  • High-performance. Be able to sustain hundreds of thousands of requests per second.
    • Enforcing limits should not downgrade the performance of the Gateway.
      • It should avoid congesting the network.
      • It should avoid congesting the number of available ports.
      • It should cause no downtime of the Gateway.
  • Adaptive. Nodes may come up or go down at any time.
  • There's an allowance of 2 - 5% overflow on the imposed limits. ( a limit of 10,000 requests per second may allow 10,500 requests per second in reality ).
  • Fail-safe. In the event that the microservice component goes down all traffic should be permitted until it recovers.

Back to TOC

Definitions

Tracking Domain

A Tracking Domain is a formula involving a group of variables and their associated values. It represents an identifier used to match requests that require a special action ( track, block, delay, rewrite).

The table bellow provides some examples of tracking domains:

Variable Expected Value Used for tracking requests ...
$service_id service_1 ... hitting the service_1 service.
$app_name my_application ... generated by my_application calling any service.
$service_id;$app_name service_1;my_application ... generated by my_application but limited to the service_1 service.
$service_id;$request_method service_1;POST ... hitting the service_1 service using the POST http method.

Pretty much any NGINX variable or user defined variable can be used to create the domain for tracking requests. This includes HTTP headers, query parameters, or URI parts.

The Expected Value can also be a wildcard (*) besides a static value. This is useful to track any value for a given variable.

Tracking Rules

A Tracking Rule is a tuple created from a tracking domain, an expiration time, and an action. For example to block all requests having the header x-api-key:1234 the following rule can be added:

{
  "id": 777,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "BLOCK"
}
  • id - is an identifier for the rule
  • domain and format - is the actual domain as defined above. domain field holds the Expected Value for the variables defined in the format field.

    $http_x_api_key is an NGINX variable holding the value of the header x-api-key.

  • expire_at_utc - the timestamp in UTC when this rule should expire. In order to enforce this rule for 100ms the rule has to specify a timestamp with +100ms in the future from the current time.
  • action - describes what to do when a request matches the tracking domain and in this example the action is to BLOCK the request returning most probably a 429 Status Code.

This module implements the following actions:

TRACK

This action is useful to log usage information. This info is pushed into a message queue. The default implementation uses ZMQ. As mentioned above this module relies on a microservice to use this data and decide which action to be taken next.

In the example bellow the following Tracking Rule logs all requests containing a header named X-Api-Key with a value of 1234:

{
  "id": 777,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "TRACK"
}

It's also possible to specify a wildcard for the http_x_api_key variable in order to precisely capture all its possible values:

{
  "id": 777,
  "domain" : "example.com;*",
  "format": "$host;$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "TRACK"
}

In this case the Gateway logs the value of the $host along with the value of the X-Api-Key header. This information can potentially be used to enforce a different quota per individual api-keys.

BLOCK

This action blocks requests with a 429 HTTP response code until the rule expires.

{
  "id": 10,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "BLOCK"
}

DELAY

This action slows down requests by delaying them. The current implementation uses a random number between the specified delay and delay/2. For example for a delay of 5 the incoming requests may be delayed with 2.5 seconds to 5 seconds. Delaying strategy works great for short spikes in traffic. It is best to start delaying requests before blocking them.

The following rule delays requests with the header X-Api-Key:1234 for max 5 seconds:

{
  "id": 10,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1408065588203,
  "action" : "DELAY",
  "data": 5
}

The data field is used to specify the delay.

REWRITE

This action enables a per request routing override. It can be used for blue-green deployments or canary traffic. The following rule enables a rewrite for a specific X-Api-Key header:

{
  "id": 200,
  "domain" : "1234",
  "format": "$http_x_api_key",
  "expire_at_utc": 1583910454,
  "action" : "REWRITE",
  "meta": "canary.example.com"
}

Back to TOC

Sample usage

# initial setup 
# ...
 init_worker_by_lua_block {
        -- initialize a ZMQ Logger 
        local ZmqLogger = require "api-gateway.zmq.ZeroMQLogger"
        local zmqLogger = ZmqLogger:new()
        zmqLogger:connect(ZmqLogger.SOCKET_TYPE.ZMQ_PUB, "ipc:///tmp/nginx_queue_listen")
            
        ngx.apiGateway = ngx.apiGateway or {}
        ngx.apiGateway.zmqLogger = zmqLogger                                  
        ngx.apiGateway.validation = require "api-gateway.validation.factory"  -- enforce ACTIONs
        ngx.apiGateway.tracking = require "api-gateway.tracking.factory"      -- track requests
     }
 # dictionaries used for storing the throttling / rate limiting rules
 lua_shared_dict tracking_rules_dict 5m;
 lua_shared_dict blocking_rules_dict 5m;
 lua_shared_dict delaying_rules_dict 5m;
 lua_shared_dict rewriting_rules_dict 5m;    
# ... 

#
# default validator for service plans that looks for Blocking rules to see if they match the request
#
location /validate_service_plan {
    internal;
    content_by_lua_block  { 
        ngx.apiGateway.tracking.validateServicePlan()
    }
}    

location /t {
    set $service_plan_validator "on; path=/validate_service_plan; ";                                            
    access_by_lua_block {
        ngx.apiGateway.validation.validateRequest()          -- enforce ACTIONS
    }
    ...
    log_by_lua_block {
           ngx.apiGateway.tracking.track()                   --  track requests
    }
}

Expose a simple HTTP Service to register rules and list the active ones:

server {
    listen 5000;
    server_name $hostname;
    
    include tracking_service.conf;
}

See tracking_service.conf that details the HTTP endpoints.

The following request adds a new blocking rule (expiring on 7/1/2016):

curl -i -X POST http://<docker_host_ip>:5000/tracking \ 
    --data '{  "id": 10,  "domain" : "1234",  "format": "$http_x_api_key",  "expire_at_utc": 1467331200000,  "action" : "BLOCK"}'
    
{"result":"success"}

To check which rules are active:

curl http://<docker_host_ip>:5000/tracking/block

[{"domain":"1234","format":"$http_x_api_key","id":10,"action":"BLOCK","expire_at_utc":1467331200000}]

Back to TOC

Developer guide

Running the tests

 make test-docker

Test files are located in test/perl folder and are based on the test-nginx library. This library is added as a git submodule under test/resources/test-nginx/ folder, from https://github.com/agentzh/test-nginx.

The other libraries such as Redis, test-nginx would be located in test/resources/. Other files used when running the test are also located in test/resources.

If you want to run a single test edit docker-compose.yml and replace in entrypoint /tmp/perl with the actual path to the test ( i.e. /tmp/perl/my_test.t)

The complete entrypoint config would look like:

 entrypoint: ["prove", "-I", "/usr/local/test-nginx-0.24/lib", "-I", "/usr/local/test-nginx-0.24/inc", "-r", "/tmp/perl/my_test.t"]

This will only run my_test.t test file.

Running the tests with a native binary

The Makefile also exposes a way to run the tests using a native binary:

 make test

This is intended to be used when the native binary is present and available on $PATH.

Back to TOC