Forum Discussion

Jason_Hook_4092's avatar
Jason_Hook_4092
Icon for Nimbostratus rankNimbostratus
Feb 15, 2012

1 VS, 2 pool routing requirements...help?

I have the following iRule. It's working as needed. It might be clumbsy, but it works. (thoughts on a cleaner implementation?).

 

 

It chooses a pool based on a cookie, or a response from the server in the pool it it hit the wrong pool.

 

 

What I need...is to add more pool-selection logic to it to choose a pool (after determining which client it is) based on URI in order to separate some apps from the rest due to performance/capacity concerns.

 

 

Thoughts on how I can accoplish this in the most effective/maintainable way?

 

 

I hope copy/paste looks ok, not sure how to paste 'code' and make it look pretty here:

 

 

 

set iRule priority higher than the default to better it's functionality

 

priority 300

 

when HTTP_REQUEST {

 

Look for existing pool cookie otherwise determine where to go based on

 

a lookup of client IP in the Client 'proxy' Server addresses datalist

 

 

 

set to 1 to enable LTM logging, 0 to disable LTM logging

 

set debugWPPBF 0

 

init flag whether to run iRule or just exit

 

set ShouldExitWPPBF 0

 

 

if {[string tolower [HTTP::path]] starts_with "/streamer"} {

 

stop running the iRule since PERSIST profile will pick up /streamer calls

 

set ShouldExitWPPBF 1

 

return

 

}

 

 

if {$debugWPPBF}{log local0.info "Cookie TFPoolId Exists: [HTTP::cookie exists "TFPoolId"]"}

 

 

if {[HTTP::cookie exists "TFPoolId"] && ![info exists retriesWPPBF]} {

 

Pool cookie exists...just send the request

 

switch [HTTP::cookie "TFPoolId"] {

 

"1" { set PoolIdWPPBF "BLTCP_WEB" }

 

"2" { set PoolIdWPPBF "WSI_WEB" }

 

default { set PoolIdWPPBF "BLTCP_WEB" }

 

}

 

pool $PoolIdWPPBF

 

if {$debugWPPBF}{log local0.info "Cookie Pooling [IP::client_addr] to $PoolIdWPPBF"}

 

} else {

 

no pool cookie so we need to determine where to go

 

if { [info exists retriesWPPBF] } {

 

We are in retry so just send it to the new pool

 

if {$debugWPPBF}{log local0.info "isRetry to $NewPoolIdWPPBF"}

 

Update content length header with updated payload length

 

HTTP::header replace Content-Length [string length [HTTP::payload]]

 

pool $NewPoolIdWPPBF

 

} else {

 

we have no pool cookie and are in the 1st request...get setup to determine if we need to retry to a new pool

 

if {$debugWPPBF}{log local0.info "No Cookie"}

 

set noCookie 1

 

Set request, HTTP post header.

 

set requestWPPBF [HTTP::request]

 

if {$debugWPPBF}{log local0.info "HTTP::request: $requestWPPBF"}

 

Set payload content legth to capture.

 

if {[HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] <= 4000000}{

 

set content_lengthWPPBF [HTTP::header "Content-Length"]

 

} else {

 

set content_lengthWPPBF 4000000

 

}

 

Capture HTTP payload from request, HTTP::collect triggers event HTTP_REQUEST_DATA

 

if { [info exists content_lengthWPPBF] && $content_lengthWPPBF > 0} {

 

if {$debugWPPBF}{log local0.info "Collecting Payload"}

 

HTTP::collect $content_lengthWPPBF

 

}

 

set PoolIdWPPBF ""

 

set isMatchWPPBF [class match [IP::client_addr] equals WFAPriceServerList]

 

if {$debugWPPBF}{log local0.info "Returned '$isMatchWPPBF' from WFA price server lookup"}

 

if {$isMatchWPPBF} {

 

pool WSI_WEB

 

if {$debugWPPBF}{log local0.info "Pooling to WSI_WEB"}

 

} else {

 

pool BLTCP_WEB

 

if {$debugWPPBF}{log local0.info "Pooling to BLTCP_WEB"}

 

}

 

}

 

}

 

}

 

when HTTP_REQUEST_DATA {

 

Set payload, depends on HTTP::collect done in HTTP_REQUEST event.

 

set payloadWPPBF [HTTP::payload]

 

 

Append HTTP payload to HTTP request header to form a complete HTTP post request (request + payload)

 

append requestWPPBF [HTTP::payload [HTTP::payload length]]

 

 

Log payload

 

if {$debugWPPBF} { log local0.info "Payload: $payloadWPPBF - length: [HTTP::payload length]" }

 

}

 

 

when HTTP_RESPONSE {

 

Watch for a response status of 307 from a misdirected pool

 

and change the cookie value accordingly

 

otherwise set the cookie to the selected pool

 

if {$ShouldExitWPPBF} { return }

 

if {$debugWPPBF}{log local0.info "status: '[HTTP::status]' - header: '[HTTP::header "WMErrorID"]'"}

 

if {[HTTP::status] eq "307" && [HTTP::header "WMErrorID"] eq "307" } {

 

HTTP::cookie remove "TFPoolId"

 

if {[HTTP::header value "FirmRoute"] eq 901} {

 

set NewCookieValueWPPBF "2"

 

HTTP::cookie insert name "TFPoolId" value $NewCookieValueWPPBF path "/"

 

set NewPoolIdWPPBF "WSI_WEB"

 

if {$debugWPPBF}{log local0.info "Wrong Pool, Changing to WSI_WEB"}

 

} else {

 

set NewCookieValueWPPBF "1"

 

HTTP::cookie insert name "TFPoolId" value $NewCookieValueWPPBF path "/"

 

set NewPoolIdWPPBF "BLTCP_WEB"

 

if {$debugWPPBF}{log local0.info "Wrong Pool, Changing to BLTCP_WEB"}

 

}

 

 

if { ![info exists retriesWPPBF] } {

 

Set retries to activate the retry logic

 

set retriesWPPBF 1

 

Retry HTTP request/payload to another pool.

 

if {[info exists requestWPPBF] && $requestWPPBF ne ""} {

 

if {$debugWPPBF}{log local0.info "Retrying: $requestWPPBF"}

 

HTTP::retry "$requestWPPBF"

 

return

 

} else {

 

if {$debugWPPBF}{log local0.info "No collected request - responding to client"}

 

HTTP::cookie remove "TFPoolId"

 

}

 

}

 

} else {

 

if {$debugWPPBF}{log local0.info "PoolIdWPPBF: $PoolIdWPPBF"}

 

if {[info exists noCookie] || [info exists retriesWPPBF]} {

 

switch [LB::server pool] {

 

"BLTCP_WEB" { HTTP::cookie insert name "TFPoolId" value "1" path "/" }

 

"WSI_WEB" { HTTP::cookie insert name "TFPoolId" value "2" path "/" }

 

default { HTTP::cookie insert name "TFPoolId" value "1" path "/" }

 

}

 

Update content length header with updated payload length

 

REMOVED HTTP::header replace Content-Length [string length [HTTP::payload]]

 

if {$debugWPPBF}{log local0.info "No 307, Setting Cookie for [LB::server pool] - Client: [IP::client_addr]"}

 

if {[info exists noCookie]}{ unset noCookie }

 

}

 

if {[info exists retriesWPPBF]}{

 

if {$debugWPPBF}{log local0.info "unsetting retriesWPPBF"}

 

unset retriesWPPBF

 

}

 

}

 

Insert header with responding server IP

 

HTTP::header replace "serverIP" [IP::server_addr]

 

}

 

when LB_SELECTED {

 

if {$ShouldExitWPPBF} { return }

 

if { [info exists retriesWPPBF] } {

 

if {$debugWPPBF}{log local0.info "Redirecting Request to $NewPoolIdWPPBF - member: [LB::server]"}

 

LB::reselect pool $NewPoolIdWPPBF

 

}

 

}

 

3 Replies

  • what about if we create another HTTP_REQUEST event with higher priority and disable all event when the condition matches?

     

     

    event wiki

     

    http://devcentral.f5.com/wiki/iRules.event.ashx
  • We have an n-tier application architecture. The web tier/pools are the entry point to the environment. The catch is that we have 2 production environments that reside behind the same VIP. Currently, one pool of web servers (PoolId=1) is for our web products as a service bureau offering...and the other pool (PoolId=2) is for one specific client that requires us to have a separate set of servers (pretty much a mirror of the architecture that is behind PoolId=1) so there is impact isolation (they are paying to have their own in order to not be impacted if another of our clients add load in the environment that slows it down).

     

     

    so, I have to still decide PoolId 1 or 2, but need to add additional logic to splid PoolId into more pools based on URI.

     

     

    1) Client A would route to PoolId 1 as a service bureau client, but they are accessing /appX so they need to be on service bureau side (PoolId=1) and then web pool 1B that contains /appX.

     

     

    2) Client A calls another application so they are routed to PoolId=1 again, but they are calling for /appZ which isn't defined to its own pool so it would be sent to the PoolId=1 default of web pool 1A

     

     

    3) Client B is the "special" client and will be routed to PoolId=2 and only has a subset of the applications that service bureau has so they just get routed to web pool 2A as the default.

     

     

    4) Client C is another service bureau client so they will route to PoolId=1 and then we need to check the /app? URI again to route to the correct PoolId=1 (service bureau n-tier stack) web pool.

     

     

    Make sense?

     

     

    At this time...there is no requirement to split another customer to their own set of servers (which would just be added as PoolId=3 if needed) so I'm not worried there.

     

     

    I have the PoolID 1 or 2 working...I am open to suggestions on bettering that iRule as-is, but I need to add the 2nd part where I route the request based on URI...AFTER I decide what stack (PoolID 1 or 2).

     

     

    I'm just not sure the "best" way to add this extra level of logic without making the iRule a sloppy mess of "if..then" or "switch" statements.

     

     

    Suggestions wanted...