For more information regarding the security incident at F5, the actions we are taking to address it, and our ongoing efforts to protect our customers, click here.

Forum Discussion

AllrecipesDCO_1's avatar
AllrecipesDCO_1
Icon for Nimbostratus rankNimbostratus
Mar 12, 2014

Re-balance requests from a single client

Hello,

 

We have an internal device hitting our public site with a large number of requests per second, and due to persistent connections (and the rate at which the requests are coming in from the device), it's rendering the server it hits basically useless to customer traffic. Because it's a licensed device, our hands are tied as to altering the configuration on that end, so we'd like to use the load balancer to balance the requests from the device.

 

I've compiled an iRule that I think should work, but I'm pretty new to writing them, so I'd love anyone's input as to the following questions:

 

Will it work as written? Is it logical? Is there a more efficient or effective iRule to perform this function?

 

Thanks in advance for your help and advice!

 

 

9 Replies

  • Hi!

     

    Welcome to the world of iRules!

     

    A few tips:

     

    • The LB::reselect can't be used in the HTTP_REQUEST.
    • Also, tables has an inbuilt expiration you can use.

    /Patrik

     

  • Try this one:

    when RULE_INIT {
    
        set static::lifetime 60
        set static::requestlimit 10
    
    }
    
    when LB_SELECTED {
    
        if { [HTTP::header "User-Agent"] contains "Mozilla" } {
    
            Add a table entry with a lifetime in seconds of the value of $lifetime.
            table add [IP::client_addr] 1 indefinite $static::lifetime
    
            if { [table incr [IP::client_addr]] > $static::requestlimit } {
                If the user has surpassed the request limit the pool member is reselected
                log local0. "This server was selected: [LB::server]"
                LB::reselect
                log local0. "Selected [LB::server]"
            }
        }
    }
    

    /Patrik

  • Hi Patrik,

     

    Thanks for your reply! I'll edit the static::lifetime portion down to 2 and test this out.

     

  • Hi Patrik,

     

    I looked over your suggestion again and I have a concern/question about using the LB_SELECTED tag. Will this rule only come into affect when a connection from the specified user-agent is initially opened?

     

    Specifically, the issue we're seeing is that there is only one connection from the device, so to my thinking, the LB is only selected once, but a very large number of requests.

     

    Does LB::reselect only work within LB_SELECTED, and if so, is there a similar function that would work under HTTP_REQUEST?

     

    Thanks again! Jody

     

  • Hi Jody!

    I see what you mean, and you have a point, and I did not know the answer. So I did an experiment. Set the maximum connections in firefox to 1 and then added the following rule on my VIP:

    when CLIENT_ACCEPTED {
        log local0. "New connection. Client port [TCP::client_port]"
    }
    
    when LB_SELECTED {
        log local0. "LB Selected. Client port [TCP::client_port]"
    }
    

    This is the log

    Mar 14 09:09:16 LB1 info tmm2[12112]: Rule /Partition/iruletest : New connection. Client port 18482
    Mar 14 09:09:17 LB1 info tmm2[12112]: Rule /Partition/iruletest : LB Selected. Client port 18482
    Mar 14 09:09:17 LB1 info tmm2[12112]: Rule /Partition/iruletest : LB Selected. Client port 18482
    

    Looks like it's triggered everytime.

    IF it does not work, we could try to reselect the member via the pool command in the HTTP_REQUEST event, but it will be messier. 🙂

    /Patrik

  • Hi again!

     

    Have you managed to solve your issue yet?

     

    I did some further tests today for another thing and it turns out that what I saw must have been a fluke.

     

    The LB_SELECTED event is only activated once (per pool selection), and this causes a problem for you since the LB::reselect command only seems to be valid in the LB_SELECTED event.

     

    I have an idea for a solution using tables but I'd rather know if you've solved it before tampering with it later tonight.

     

    /Patrik

     

  • I think I figured it out. I wrote and iRule and tried it on a virtual server having a default pool with two members. The load balancing method was predictive.

    The result seems to be what you want. There was no interruptions to the requests and the server was changed as planned.

    when RULE_INIT {
    
        set static::lifetime 60
        set static::requestlimit 5
        set static::debug 1
    }
    when CLIENT_ACCEPTED {
        log local0. "New connection"
    }
    
    when HTTP_REQUEST {
    
        if { [HTTP::header "User-Agent"] contains "Mozilla" } {
            Add a table entry with a lifetime in seconds of the value of $lifetime.
            table add [IP::client_addr] 1 indefinite $static::lifetime
    
            Check if the limit has been reached
            if { [table incr [IP::client_addr]] > $static::requestlimit } {
                Delete the table entry to restart the timeout
                table delete [IP::client_addr]
                if { $static::debug } { log local0. "Before select [LB::server addr]" }
                Disconnect the server session
                LB::detach
                Force a new load balancing decision based on the current load
                eval [LB::select]
                if { $static::debug } { log local0. "After select [LB::server addr]" }
            }   else {
                if { $static::debug } { log local0. "Connections still ok" }
            }
        }
    }
    

    Added some debug options for testing it, but you can remove them (and the whole CLIENT_ACCEPTED event.

    To test it I wrote a powershell script that sends a request every 3 seconds and echoes the result. Since it's only using one session (can be verified by the fact that CLIENT_ACCEPTED logging only is triggered once) it could be used to simulate the calls made by your service.

    Create a web client object
    $wc = New-Object System.Net.WebClient 
    
    while(0 -lt 1){
        Add headers
        $wc.Headers.Add("User-Agent", "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0;)") 
        "Sending a request"
        $result = $wc.DownloadString("https://www.mysite.com") 
        Echo request result
        $result
        Wait for 3 seconds before sending the next one
        sleep 3
    }
    

    Hope it works for you!

    /Patrik

  • do you know/can you explain why LB::reselect can't be used within HTTP_REQUEST, but LB::detach and LB::select can be?

     

    For lack of a better answer, this has to do with event timing. The HTTP_REQUEST event triggers when the VIP consumes the request headers, but has not yet made a load balancing decision. You can preemptively cause a load balancing selection with LB::select (and perhaps detach that with LB::detach), but you can't reselect something that hasn't been selected yet.