Forum Discussion

Narendra_26827's avatar
Narendra_26827
Icon for Nimbostratus rankNimbostratus
Jul 27, 2011

Websocket failover by LB_FAILED

Hi,

 

 

We have an application which uses websockets to connect to the server from the client side.

 

 

Now in the iRule we have mentioned in HTTP_REQUEST event that for websocket the HTTP::disable will be called and all the connection will be listen on tcp sockets opened from the client side.

 

 

If one client is connected to the server 1 with this method, now if that server went down now it will retry to another server by LB_FAILED event. So as in the HTTP_REQUEST we have disabled the HTTP requests the F5 LTM is not retrying it to the new node.

 

 

Following is the iRule :

 

 

In the iRule you can see we are disabling the HTTP when we receive /api/channel/websocket uri in header. If we don't disable the HTTP the websocket doest not gets created. Now if the one server goes down the request will trigger LB_FAILED event with HTTP as disabled. So the request doest not get to the new node and it fails. Can we have mechanism we can save HTTP headers over the tcp websockets by iRule.

 

 

 

when HTTP_REQUEST {

 

 

 

set uri [HTTP::uri]

 

 

if { [HTTP::header exists "objectId"] or [string tolower $uri] contains "/api/channel/websocket" }

 

{

 

set orgid [crc32 [HTTP::header objectId]]

 

set key [expr $orgid % [llength [members -list default_pool]]]

 

set default_member [lsort [members -list default_pool]]

 

set channel_member [lsort [members -list channel-pool]]

 

set gateway_member [lsort [members -list gateway-pool]]

 

set space_member [lsort [members -list space-pool]]

 

 

log "[HTTP::uri]"

 

 

if { [string tolower $uri] contains "/api/channel/websocket" }

 

{

 

HTTP::disable

 

log "[lindex [lindex $channel_member $key] 0] [lindex [lindex $channel_member $key] 1]"

 

pool channel-pool member [lindex [lindex $channel_member $key] 0] [lindex [lindex $channel_member $key] 1]

 

} else {

 

switch -glob [string tolower $uri] {

 

"/api/channel*" { pool channel-pool member [lindex [lindex $channel_member $key] 0] [lindex [lindex $channel_member $key] 1] }

 

"/api/space*" {pool space-pool member [lindex [lindex $space_member $key] 0] [lindex [lindex $space_member $key] 1] }

 

"/api/gateway*" { pool gateway-pool member [lindex [lindex $gateway_member $key] 0] [lindex [lindex $gateway_member $key] 1] }

 

default { pool default_pool member [lindex [lindex $default_member $key] 0] [lindex [lindex $default_member $key] 1] }

 

}

 

}

 

} elseif { [string tolower $uri] contains "/api/gateway/sendmessage" }

 

{

 

switch [string tolower $uri] {

 

"/api/gateway/sendmessage" { pool sendmessage-pool }

 

}

 

}

 

}

 

 

 

when LB_FAILED {

 

 

set uri [HTTP::uri]

 

set orgid_new [crc32 [HTTP::header objectId]]

 

set newkey [expr $orgid_new % [active_members default_pool]]

 

set default_member_new [lsort [active_members -list default_pool]]

 

set channel_member_new [lsort [active_members -list channel-pool]]

 

set gateway_member_new [lsort [active_members -list gateway-pool]]

 

set space_member_new [lsort [active_members -list space-pool]]

 

 

if { [string tolower $uri] contains "/api/channel/websocket" }

 

{

 

log "[HTTP::uri]"

 

HTTP::disable

 

log "[lindex [lindex $channel_member_new $key] 0] [lindex [lindex $channel_member_new $key] 1]"

 

LB::reselect pool channel-pool member [lindex [lindex $channel_member_new $key] 0] [lindex [lindex $channel_member_new $key] 1]

 

} else {

 

switch -glob [string tolower $uri] {

 

"/api/channel*" {

 

log "[lindex [lindex $channel_member_new $key] 0] [lindex [lindex $channel_member_new $key] 1]"

 

LB::reselect pool channel-pool member [lindex [lindex $channel_member_new $key] 0] [lindex [lindex $channel_member $key] 1]

 

}

 

"/api/space*" {

 

log "[lindex [lindex $space_member_new $key] 0] [lindex [lindex $space_member_new $key] 1]"

 

LB::reselect pool space-pool member [lindex [lindex $space_member_new $key] 0] [lindex [lindex $space_member_new $key] 1]

 

}

 

"/api/gateway*" {

 

log "[lindex [lindex $gateway_member_new $key] 0] [lindex [lindex $gateway_member_new $key] 1]"

 

LB::reselect pool gateway-pool member [lindex [lindex $gateway_member_new $key] 0] [lindex [lindex $gateway_member_new $key] 1]

 

}

 

default {

 

log "[lindex [lindex $default_member_new $key] 0] [lindex [lindex $default_member_new $key] 1]"

 

LB::reselect pool default_pool member [lindex [lindex $default_member_new $key] 0] [lindex [lindex $default_member_new $key] 1]

 

}

 

}

 

}

 

}

 

 

 

 

If anybody can provide any help on this?

 

 

 

Thanks.

 

  • spark_86682's avatar
    spark_86682
    Historic F5 Account
    I don't know anything about websockets, or how they work, or why the HTTP filter interferes with them (which to me is scary and bad; are they using another dreaded almost-but-not-quite-HTTP protocol?), and I can't quite follow your question. I *think* you're trying to resstablish a websocket to a different server when the original server goes down.

     

     

    Once you disable HTTP (I can't remember if this happens immediately when you call HTTP::disable, or after that event finishes), all the HTTP information (e.g. HTTP::uri, etc.) and commands (e.g. HTTP::retry) are unusable. So that approach won't work.

     

     

    If you want to attempt this, I think you'll need to use the TCP:: commands. In LB_FAILED, set a flag and create a variable which contains whatever the websocket handshake/request/etc. looks like, and then if that flag is set in SERVER_CONNECTED you can use TCP::respond to send it to the server.

     

     

    All that assumes that websockets use some sort of well-defined request-response protocol with a single request per connection. If not, you'll need to do something much more complicated involving parsing the websocket stream and buffering the last request, etc.
  • Yes you are right, I want to reestablish websocket connection to different server. Can you provide any sample iRule using TCP:: for retrying to different node in the pool, so that i can construct my iRule on the basis of that.

     

     

    Thanks.
  • Hi Narendra, did you get around scripting it, I'm really interested to see how it worked, please post if you could, thanks.
  • Hi fujisen,

     

     

    Thanks for replying. Well i was not able to do the final scripting for the websocket failover to new node and failback (to original node when it comes back) with possessing the original http headers (after http disable). If you get to know some way around to solve this please do let me know.

     

     

    Thanks.