HTTP throttle alternative
Problem this snippet solves:
An alternative HTTP throttle iRule
Features
2 modes of operations: strict and window (burst) mode
-
Strict mode: Strictly enforces maximum number of requests per Time Slot. For example, one time slot may allow 50 requests.
- Window mode: Enforce number of requests per Time Window (which is a combination of multiple Time Slots). However, number of requests per Time Window is updated every Time Slot. For example, one time window, which is equals to 100 time slots, may allow 5000 requests. However, request statistics will be updated every time slot.
- Support multiple policies.
- Customizable unique key. This iRule use source IP as a unique key by default.
- Statistics profile
- Customizable action when limit is reached
- Not CMP compatible. (This iRule relies heavily on global variable)
Code :
# Configuration steps * configure stats profile * create iRule * configure http virtual and apply http profile 1) Stats profile Create stats profile containing policy name follow by request and blocked as follow profile stats http_throttle { defaults from stats field1 total request field2 total blocked field3 default_policy request field4 default_policy blocked field5 request_post request field6 request_post blocked } 2) Create iRule Copy the following iRule to bigip.conf or configuration GUI when RULE_INIT { # set default policy or set to "" if no default policy is required # set static::default_policy "" set static::default_policy "default_policy" # Customize per-policy parameter here # mode of operation # 0 - strict mode: rate is strictly enforced per time slot # 1 - window mode: rate is enforced per time window array set static::Mode { "default_policy" 1 "request_post" 1 } # Request per time slot array set static::Rs { "default_policy" 10 "request_post" 1 } # Time slot in millisecond array set static::Ts { "default_policy" 10 "request_post" 3000 } # Number of slot array set static::Slot { "default_policy" 100 "request_post" 5 } # Tw - Time window = Time slot x Slot foreach p [array name static::Mode] { if {![info exists static::Ts($p)]}{ set static::Ts($p) $static::Ts("default_policy") } if {![info exists static::Slot($p)]}{ set static::Slot($p) $static::Slot("default_policy") } set static::Tw($p) [expr $static::Ts($p) * $static::Slot($p)] } # Rw - Request per time window foreach p [array name static::Mode] { if { ![info exists static::Rs($p)] } { set static::Rs($p) $static::Rs("default_policy") } set static::Rw($p) [expr $static::Rs($p) * $static::Slot($p)] } # clean up after 1000 -periodic { foreach p [array name static::Mode] { foreach k [array name ::rate_${p}_key] { set delete 1 foreach t [array name ::rate_${p}_${k}] { set delete 0 set ctime [clock clicks -milliseconds] if { $ctime < 0 } { set ctime [expr 4294967296 + $ctime] } set ltime [expr ($ctime / $static::Ts(${p})) - $static::Slot(${p})] if { $t < $ltime } { unset rate_${p}_${k}(${t}) } } foreach t [array name ::sum_${p}_${k}] { set delete 0 set ctime [clock clicks -milliseconds] if { $ctime < 0 } { set ctime [expr 4294967296 + $ctime] } set ltime [expr ($ctime / $static::Ts(${p})) - $static::Slot(${p})] if { $t < $ltime } { unset sum_${p}_${k}(${t}) } } if { $delete } { unset ::rate_${p}_key($k) } } } } } when HTTP_REQUEST { # Policy assignment # - default policy set p $static::default_policy # - custom routine to assign policy # (customize policy assignment here) if { [HTTP::method] eq "POST" } { set p "request_post" } STATS::incr http_throttle "total request" # Policy enforcement if { $p eq "" } { # request does not match any policy # and there is no default policy configured return } STATS::incr http_throttle "$p request" set block 0 # current time (millisecond) set ctime [clock clicks -milliseconds] if { $ctime < 0 } { set ctime [expr 4294967296 + $ctime] } set ctime [expr $ctime / $static::Ts($p)] # Customize unique key here set k [IP::client_addr] if { ![info exists ::rate_${p}_key(${k})] } { set ::rate_${p}_key(${k}) 1 } set static::Mode($p) 1 if { $static::Mode($p) == 0 } { # strict mode if { ![info exist ::rate_${p}_${k}(${ctime}) ] } { set ::rate_${p}_${k}(${ctime}) 1 } elseif { [subst \$\{::rate_${p}_${k}(${ctime})\}] < $static::Rs(${p}) } { # in strict mode, don't allow rate in each time slot to go beyond Rs incr ::rate_${p}_${k}(${ctime}) } else { set block 1 } } else { # window mode if { ![info exist ::sum_${p}_${k}(${ctime}) ] } { # calculate sum of requests for last ($static::Slot - 1) slots set ptime [expr $ctime - 1] set ltime [expr $ctime - $static::Slot($p)] if { [info exist ::sum_${p}_${k}(${ptime}) ] } { set ::sum_${p}_${k}(${ctime}) [subst \$\{::sum_${p}_${k}(${ptime})\}] if { [info exist ::rate_${p}_${k}(${ltime}) ] } { incr ::sum_${p}_${k}(${ctime}) -[subst \$\{::rate_${p}_${k}(${ltime})\}] } } else { set ::sum_${p}_${k}(${ctime}) 0 for { set xtime [expr $ctime - 1] } { $xtime > $ltime } { incr xtime -1 } { if { [info exist ::rate_${p}_${k}(${xtime}) ] } { incr ::sum_${p}_${k}(${ctime}) [subst \$\{::rate_${p}_${k}(${xtime})\}] } } } # in window mode, allow rate to go beyond Rs but not Rw if { [subst \$\{::sum_${p}_${k}(${ctime})\}] < $static::Rw(${p}) } { # update statistics set ::rate_${p}_${k}(${ctime}) 1 incr ::sum_${p}_${k}(${ctime}) } else { set block 1 } } else { # sum of requests exists if { [subst \$\{::sum_${p}_${k}(${ctime})\}] < $static::Rw($p) } { incr ::rate_${p}_${k}(${ctime}) incr ::sum_${p}_${k}(${ctime}) } else { set block 1 } } } # Customize throttle action here # default is responding with 500 status code (no content) if { $block } { HTTP::respond 500 STATS::incr http_throttle "$p blocked" STATS::incr http_throttle "total blocked" } } 3) HTTP virtual server Configure http virtual and apply http profile to it. This version of iRule does not support CMP so CMP should be disabled. virtual http { cmp disable pool http destination 10.10.71.200:http ip protocol tcp rules http_throttle_test profiles { http {} http_throttle {} tcp {} } } # Modifying Parameters # 1) Default Policy set static::default_policy variable to desired value or set to "" if no default policy is required. For example: set static::default_policy "default_policy" # 2) Set mode of operation (0=strict mode, 1=window mode) Add policy name follow by selected mode of operation to array static::Mode. For example: array set static::Mode { "default_policy" 1 "request_post" 1 } # 3) Customize routine to assign policy - The following example use switch statement to select policy based on HTTP::method and HTTP::uri. Set variable p to selected policy name. if { [HTTP::method] eq "POST" } { set p "request_post" } # 4) Set request limit Strict mode - In Strict mode, 2 parameters are needed to be configured: Ts and Rs. - Ts (or Time Slot) is a period of time (in millisecond). - Rs is a number of requests allowed per Time Slot interval (Ts). - Ts and Rs for each policy have to be put together in array. For example, # Request per time slot array set static::Rs { "default_policy" 10 "request_post" 1 } # Time slot in millisecond array set static::Ts { "default_policy" 10 "request_post" 3000 } - In this example, the “request_post” policy allows 1 request per every 3000 milliseconds. Window mode - In Window mode, 3 parameters are needed to be configured: Ts, Rs and Slot. - Ts (or Time Slot) is a period of time (in millisecond). - Rs is a number of requests allowed per Time Slot interval (Ts). - In window mode, number of requests allowed is not enforced per Time slot but per Sliding Window - Slot is a number of Time Slot (Ts) to be combined as a Sliding Window. Maximum requests per Sliding Window equal to Slot x Rs. - Rs, Ts and Slot should be configured per policy in an array. Here is an example: # Request per time slot array set static::Rs { "default_policy" 10 "request_post" 1 } # Time slot in millisecond array set static::Ts { "default_policy" 10 "request_post" 3000 } # Number of slot array set static::Slot { "default_policy" 100 "request_post" 5 } - For the request_post policy -- Rs=1, Ts=3,000 and Slot=5. -- Sliding window = Slot x Ts = 5 x 3,000 = 15,000 milliseconds -- Maximum requests allowed per sliding window = Slot x Rs = 5 x 1 = 5 requests -- Request statistics will be updated every 3000 milliseconds.
Published Mar 18, 2015
Version 1.0Nat_Thirasuttakorn
Employee
Joined September 25, 2004
Nat_Thirasuttakorn
Employee
Joined September 25, 2004
No CommentsBe the first to comment