Forum Discussion
iRule for rate limit unexpected behaviour
I have a requirement to implement rate limiting for HTTP requests and have acheived this somewhat through reading discussions and receving advice via this community. The iRules I have are not behaving as I'd expect. I've been carrying out some testing on two iRules referenced below. The results are not what I expect to see and I was hoping someone could help me understand what's going on and if it can be fixed.
Basically I am using httperf to send 20 requests per second for 10 seconds. The iRules are set to rate limit at 30 requests per second but are still being triggered. The below screenshot shows Perfmon configured to count GET requests received per second on our server.
The first block of httperf requests is against the VIP with no iRule enabled as you can see we are getting a nice steady flow of 20 requests per second.
The 2nd block of requests is whilst iRule 1 mentioned below is enabled. There seems to be rate limiting going on even though it's set to 30 per second.
The 3rd block of requests is whilst iRule 2 mentioned below is enabled. Again we see the same thing.
Any ideas? The rate limit also performs quite poorly with bursts of traffic, is there anything you can suggest to improve this?
iRule 1:
when RULE_INIT {
set static::maxRate 30
set static::timeout 1
}
when HTTP_REQUEST {
if { (([string tolower [HTTP::host]] equals "demo.company.com") or ([string tolower [HTTP::host]] equals "demo.company.com")) and [string tolower [HTTP::uri]] starts_with "/test/index" } then {
if { [set methodCount [table incr -mustexist "Count_[HTTP::method]"]] ne "" } then {
if { $methodCount > $static::maxRate } then {
log local0. "[IP::client_addr] exceeded max HTTP requests per second. URL is [HTTP::host][HTTP::uri]"
HTTP::respond 429 content "Request blockedExceeded requests/sec limit." Retry-After 30
return
}
} else {
table set "Count_[HTTP::method]" 1 indef $static::timeout
}
log local0. "[IP::client_addr]: methodCount=$methodCount using [HTTP::method]"
}
}
- Nigel88_321901Nimbostratus
iRule 2 https://devcentral.f5.com/questions/irule-rate-limiting-with-queuing-of-requests-57583comment64016:
when RULE_INIT { Tweak the request limit and interval set static::request_limit 30 ;count set static::request_interval 1 ;sec Tweak the queue size, timeout and retry interval set static::queue_size 250 ;count set static::queue_timeout 5 ;sec set static::queue_retry_interval 1000 ;msec } when HTTP_REQUEST { Enforce limits only on GET requests... if { [HTTP::method] eq "GET" } then { Calculate a unique request ID to track the request... set request_id "[TMM::cmp_unit][clock clicks]" Compute table labels for request and queued limiter... set request_label "R_[HTTP::host]:[IP::client_addr]" set queue_label "Q_[HTTP::host]" Checking if request limit has been reached... if { [set request_rate [table keys -count -subtable $request_label]] < $static::request_limit } then { Max request rate is not exceeded. Insert a new entry to request limiter table... table set -subtable $request_label $request_id "1" indef $static::request_interval log local0.debug "Debug: $request_id : Allow: Request rate for $request_label is $request_rate / $static::request_interval seconds" Allowing the request to pass... } else { Max request rate is exceeded. Holding the request to check queue slots... log local0.debug "Debug: $request_id : Hold: Request rate for $request_label is $request_rate / $static::request_interval seconds" Checking if queue has a free slot.... if { [set queue_size [table keys -count -subtable $queue_label]] < $static::queue_size } then { Queue is not exceeded. Insert a new entry to queue limiter table... table set -subtable $queue_label $request_id "1" indef $static::queue_timeout log local0.debug "Debug: $request_id : Queued: Queue counter for $queue_label is $queue_size" Initialize queue timer... set queue_time 0 Enforcing the queue loop timeout timer... while { $static::queue_timeout * 1000 > $queue_time } { Pausing the ongoing HTTP request for queue retry interval after $static::queue_retry_interval Checking if request limit has been lowered... if { [set request_rate [table keys -count -subtable $request_label]] < $static::request_limit } then { Max request rate has been lowered. Insert a new entry to our request rate limiter... table set -subtable $request_label $request_id "1" indef $static::request_interval log local0.debug "Debug: $request_id : Queue Release: Requests rate for $request_label is now $request_rate / $static::request_interval seconds" Removing this request from request queue table delete -subtable $queue_label $request_id Exiting this iRule and allowing the request to pass... return } else { Max request rate is still exceeded. Queueing the request another cycle... log local0.debug "Debug: $request_id : Queue Retry: Requests rate for $request_label is now $request_rate / $static::request_interval seconds" incr queue_time $static::queue_retry_interval } } Queued request has been timed out. Dropping the request... log local0.debug "Debug: $request_id : Queue Fail: Requests rate for $request_label is now $request_rate / $static::request_interval seconds" HTTP::respond 429 content "Request Denied: Exceeded requests/sec limits" } else { Maximum queue size has been exceeded. Dropping the request... log local0.debug "Debug: $request_id : Queue Exceeded: Queue counter for $queue_label is $queue_size" HTTP::respond 429 content "Request Denied: Exceeded requests/sec limits" } } } }
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com