Forum Discussion

Terje_Gravvold's avatar
Terje_Gravvold
Icon for Nimbostratus rankNimbostratus
Jul 03, 2009

iRule - Retry HTTP post request, including payload, to a secondary pool if primary pool fails

I've scripted a short iRule to handle resending of HTTP post requests, including XML payload/post data, to a secondary pool if primary fails. The rule is based iRule examples and forum posts.

 

 

The iRule below seems to pass the first simple web-service test. It would have been very nice if someone with similar use cases could comment this post. If members of the comunity decides to test this iRule in their own environment please comment with any results.

 

 

Case is as folows:

 

1. Two pools (Cells) of IBM Web-sphere Process Server (WPS)

 

2. The two pools runs the same processess (servces) with some exceptions.

 

3. Service request errors agianst primary pool should result in a retry request to secondary pool.

 

 

Service request errors are here defined as HTTP 3xx, 4xx and 5xx + connection problems. Only HTTP 2xx is accepted as a "good" reply.

 

 

iRule Attached.

 

 

Best regards

 

Terje Gravvold

 

 

 
 when CLIENT_ACCEPTED { 
    Set initial retry count to zero 
   set retries 0 
    Set debug level for iRule 
   set debug 6 
    Set HTTP status code to look for 
   set status_code 5 
    Set info about connecting client (IP:port) 
   set client [IP::remote_addr]:[TCP::remote_port] 
  
    Log client info about connecting client 
   if { $debug > 0 } { log local0.alert "From [IP::remote_addr]:[TCP::remote_port] To: [IP::local_addr]:[TCP::local_port]" } 
 } 
  
 when HTTP_REQUEST { 
    On first try, set request variables and capture request payload (XML post data) 
   if { $retries == 0 } { 
      Set request, HTTP post header. 
     set request [HTTP::request] 
      Retrive HTTP uri from HTTP request header 
     set uri [HTTP::uri] 
      Retrive HTTP method from HTTP request header 
     set method [HTTP::method] 
      Retrive host from HTTP request header 
     set host [HTTP::host] 
  
      Set payload content legth to capture. 
     if {[HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] <= 2048}{ 
       set content_length [HTTP::header "Content-Length"] 
       } 
     else { 
       set content_length 2048 
       } 
  
      Capture HTTP payload from request, HTTP::collect triggers event HTTP_REQUEST_DATA 
     if { [info exists content_length] && $content_length > 0} { 
       HTTP::collect $content_length 
       } 
  
      Set default pool to send first request to. 
     pool pool_tst_wps-celle-1  
  
      Log request 
     if { $debug > 5 } { log local0.alert "Request: $request" } 
     } 
 } 
  
 when HTTP_REQUEST_DATA { 
    Set payload, depends on HTTP::collect done in HTTP_REQUEST event. 
   set payload [HTTP::payload] 
    
    Append HTTP payload to HTTP request header to form a complete HTTP post request (request + payload) 
   append request [HTTP::payload [HTTP::payload length]] 
  
    Log payload 
   if { $debug > 5 } { log local0.alert "Payload: $payload" } 
 } 
  
 when LB_SELECTED { 
   if { $retries == 0 } { 
      Log LB pool/server selection 
     if { $debug > 0 } { log local0.alert "Server Selected $retries: [LB::server pool] [LB::server addr]:[LB::server port]" } 
     } 
   else { 
      Reselect LB pool if retries > 0 
     LB::reselect pool pool_tst_wps-celle-2  
  
      Log LB pool/server selection 
     if { $debug > 0 } { log local0.alert "Server Selected $retries: [LB::server pool] [LB::server addr]:[LB::server port]" } 
     } 
   if { $debug > 5 } { log local0.alert "Request: $request" } 
 } 
  
 when HTTP_RESPONSE {     
   if { [HTTP::status] starts_with 2 } { 
       Check for HTTP 2xx response, else retry. 
      if { $debug > 0 } { log local0.alert "Process found in pool [LB::server pool], request served by pool member [LB::server addr]:[LB::server port]" } 
      } 
   else { 
       Retry if not HTTP 2xx response and retry count equals zero. 
      if { $retries == 0 } { 
          Increment retries by 1 
         incr retries 
          Retry HTTP request/payload to another another server. This command triggers LB_SELECTED event. 
         HTTP::retry "$request" 
         if { $debug > 0 } { log local0.alert "Process NOT found in pool [LB::server pool], detaching pool member [LB::server addr]:[LB::server port]" } 
         if { $debug > 5 } { log local0.alert "Request retry: $request" } 
         } 
      else { 
         if { $debug > 0 } { log local0.alert "Process NOT found in any pool, [HTTP::status] sent to client." } 
         } 
      } 
 } 
  
 when LB_FAILED { 
   if { $retries == 0 } { 
       Increment retries by 1 
      incr retries 
       Retry HTTP request/payload to another another server. This command triggers LB_SELECTED event. 
      HTTP::retry "$request" 
      if { $debug > 0 } { log local0.alert "Process NOT found in pool [LB::server pool], detaching pool member [LB::server addr]:[LB::server port]" } 
      if { $debug > 5 } { log local0.alert "Request retry: $request" } 
      } 
   else { 
      if { $debug > 0 } { log local0.alert "Process NOT found in any pool, [HTTP::status] sent to client." } 
      } 
 } 
 

7 Replies

  • That looks very good. You could add this to the Codeshare (Click here).

     

     

    You might consider only retrying if the response code is a 50x. An app will respond with a 30x redirect in many scenarios. Likewise, clients (particularly search engine spiders) will often make requests to non-existent objects which generate a 40x response. It would add unnecessary latency and load to retry these requests.

     

     

    Aaron
  • Thanks for this code. We just implemented it and I wanted to share some changes we made to make it a little more robust.

     

     

    Firstly, you set a lot of variables that you don't use: client, uri, method and host

     

     

    Limiting content-length to 2048 doesn't allow large form submissions. Looking through the forum we realized we can increase this to 4000000 without running into the 4Mb limitation.

     

     

    In rare occasions it wasn't resetting retries to 0, which was causing it to go to the second pool when it shouldn't. So in the else of LB_SELECTED we set retries to 0 again to get around this.

     

     

    Hope this helps someone :)

     

     

    Dave

     

  • Hi David,

     

     

    Do you want to post your cleaned up version? You or we could post it to the iRule Codeshare:

     

     

    http://devcentral.f5.com/wiki/default.aspx/iRules/codeshare

     

     

    Thanks, Aaron
  • Thanks a lot for the fixes and posting it in the Codeshare :)

     

     

    Aaron
  • I'm having some trouble doing file uploads (jpg images) with this rule. Any ideas?

     

     

    Does the HTTP::collect or HTTP::payload not work with binary data?

     

     

    Is there another function I should be using?

     

     

    Dave
  • thank you for sharing! helped me in a use case I am working on it.