vADC Docs

Slowing down busy users - driving the REST API from TrafficScript

by on ‎04-30-2013 06:52 AM - edited on ‎07-08-2015 10:57 AM by PaulWallace (1,849 Views)

Imagine you're running a popular image hosting site, and you're concerned that some users are downloading images too rapidly.  Or perhaps your site publishes airfares, or gaming odds, or auction prices, or real estate details and screen-scraping software is spidering your site and overloading your application servers.  Wouldn't it be great if you could identify the users who are abusing your web services and then apply preventive measures - for example, a bandwidth limit - for a period of time to limit those users' activity?

 

ratelimit.png

In this example, we'll look at how you can drive the control plane (the traffic manager configuration) from the data plane (a TrafficScript rule):

 

  • Identify a user by some id, for example, the remote IP address or a cookie value
  • Measure the activity of each users using a rate class
  • If a user exceeds the desired rate (their terms of service), add a resource file identifying the user and their 'last sinned' time
  • Check the resource time to see if we should apply a short-term limit to that user's activity

 

Basic rule

 

# We want to monitor image downloads only

if( !string.wildMatch( http.getPath(), "*.jpg" ) ) break;

# Identify each user by their remote IP.  
# Could use a cookie value here, although that is vulnerable to spoofing
# Note that we'll use $uid as a filename, so it needs to be secured

$uid = request.getRemoteIP();


if( !rate.use.noQueue( "10 per minute", $uid ) ) {

   # They have exceeded the desired rate and broken the terms of use
   # Let's create a config file named $uid, containing the current time

   http.request.put( 

      "http://localhost:9070/api/tm/1.0/config/active/extra/".$uid,

      sys.time(), 

      "Content-type: application/octet-stream\r\n".

      "Authorization: Basic ".string.base64encode( "admin:admin" )

   );

}


# Now test - did the user $uid break their terms of use recently?

$lastbreach = resource.get( $uid );

if( ! $lastbreach ) break;  # config file does not exist


if( sys.time()-$lastbreach < 60 ) {

   # They last breached the limits less than 60 seconds ago

   response.setBandwidthClass( "Very slow" );

} else {

   # They have been forgiven their sins.  Clean up the config file

   http.request.delete( 

      "http://localhost:9070/api/tm/1.0/config/active/extra/".$uid,

      "Authorization: Basic ".string.base64encode( "admin:admin" )

   );

}

 

This example uses a rate class named '10 per minute' to monitor the request rate for each user, and a bandwidth class named ‘Very slow’ to apply an appropriate bandwidth limit.  You could potentially implement a similar solution using client-side cookies to identify users who should be bandwidth-limited, but this solution has the advantage that the state is stored locally and is not dependent on trusting the user to honor cookies.

 

There's scope to improve this rule.  The biggest danger is that if a user exceeds the limit consistently, this will result in a flurry of http.request.put() calls to the local REST daemon.  We can solve this problem quite easily with a rate class that will limit how frequently we update the configuration.  If that slows down a user who has just exceeded their terms of service, that's not really a problem for us!

 

rate.use( "10 per minute" ); # stall the user if necessary to avoid overload
http.request.put( ... );

 

Note that we can safely use the rate class in two different contexts in one rule.  The first usage (rate.use( "name", $uid )) will rate-limit each individual value of $uid; the rate.use( "name" ) is a global rate limit that will limit all calls to the REST API .

 

Read more

 

Check out the other prioritization and rate shaping suggestions on splash, including:

 

Contributors