persistence
142 TopicsSOCKS5 SSL Persistence
Problem this snippet solves: Much requested 2005 iRule contest winner (thanks Adam!) The judges said: "This iRule addresses application persistence challenges associated with a proprietary service. By handling SOCKS and SSL protocol negotiation through binary rewrites, this iRule not only reduces server outages and increases customer satisfaction, it is a great example of the unique, far-reaching power of iRules and TMOS." The iRule first responds with the SOCKS 5 handshake so that it can get the next packet and persist based on the session identifier. When load-balancing the request, it proxies the relevant portion of the client’s initial handshake and removes the servers response to the handshake since we already spoofed that to the client earlier, and finally adds a persistence entry for the session id. Code : #Written by Adam Kramer (akramer@netifice.com) for Netifice Corporation #July, 2005 when CLIENT_ACCEPTED { TCP::collect 2 } when CLIENT_DATA { # read initial socks handshake – # the version number, and the number of auth methods supported binary scan [TCP::payload] cc socksver numauthmethods if { $socksver != 5 } { log local0. "Got non-socks connection from client [IP::remote_addr]" reject return } # set offset to the beginning of the second packet (SSL negotiation) set offset [expr {2 + $numauthmethods}] if { [TCP::payload length] == $offset } { #only respond if exactly the right amount of data was sent TCP::respond [binary format H2H2 05 86] TCP::collect [expr {$offset + 1}] return } # more data than the offset, this means we got the first packet of the SSL negotiation if { [TCP::payload length] > $offset} { # 4 bytes is the length of the SOCKS SSL header, 1 byte gets to the SSL version field # another 41 bytes past that is the session length, immediately following is the session # binary scan gracefully handles the string being too short, # so we can safely read all 3 values here binary scan [TCP::payload] "x[expr {$offset + 5}]cx41ch32" sslversion sessionlength hexid if { $sslversion != 3 } { log local0. "Received wrong SSL version in header from client [IP::remote_addr]" reject return } if { $sessionlength == 0 } { # this is a new connection, allow normal server selection return } else { persist universal $hexid return } } # this should never happen, but a bad client might do it if { [TCP::payload length] < $offset } { TCP::collect $offset return } } when SERVER_CONNECTED { # send current full payload from client to server, we need server's ssl hello # also delete client payload - replace returns the replaced characters, # doing both in one shot saves 50,000 cycles TCP::respond [clientside {TCP::payload replace 0 [TCP::payload length] ""}] # 5 bytes should do it, only 2 bytes to the first socks handshake TCP::collect 5 } when SERVER_DATA { # remove initial protocol negotiation since we already did that with client TCP::payload replace 0 2 "" # 4 bytes for socks ssl header, 44 for offset of session id binary scan [TCP::payload] "x48h32" hexid # need to add a session state for the case where the client didn't send a session ID persist add universal $hexid }5.2KViews0likes0CommentsPersistent and Persistence, What's the Difference?
The English language is one of the most expressive, and confusing, in existence. Words can have different meaning based not only on context, but on placement within a given sentence. Add in the twists that come from technical jargon and suddenly you've got words meaning completely different things. This is evident in the use of persistent and persistence. While the conceptual basis of persistence and persistent are essentially the same, in reality they refer to two different technical concepts. Both persistent and persistence relate to the handling of connections. The former is often used as a general description of the behavior of HTTP and, necessarily, TCP connections, though it is also used in the context of database connections. The latter is most often related to TCP/HTTP connection handling but almost exclusively in the context of load-balancing. Persistent Persistent connections are connections that are kept open and reused. The most commonly implemented form of persistent connections are HTTP, with database connections a close second. Persistent HTTP connections were implemented as part of the HTTP 1.1 specification as a method of improving the efficiency Related Links HTTP 1.1 RFC Persistent Connection Behavior of Popular Browsers Persistent Database Connections Apache Keep-Alive Support Cookies, Sessions, and Persistence of HTTP in general. Before HTTP 1.1 a browser would generally open one connection per object on a page in order to retrieve all the appropriate resources. As the number of objects in a page grew, this became increasingly inefficient and significantly reduced the capacity of web servers while causing browsers to appear slow to retrieve data. HTTP 1.1 and the Keep-Alive header in HTTP 1.0 were aimed at improving the performance of HTTP by reusing TCP connections to retrieve objects. They made the connections persistent such that they could be reused to send multiple HTTP requests using the same TCP connection. Similarly, this notion was implemented by proxy-based load-balancers as a way to improve performance of web applications and increase capacity on web servers. Persistent connections between a load-balancer and web servers is usually referred to as TCP multiplexing. Just like browsers, the load-balancer opens a few TCP connections to the servers and then reuses them to send multiple HTTP requests. Persistent connections, both in browsers and load-balancers, have several advantages: Less network traffic due to less TCP setup/teardown. It requires no less than 7 exchanges of data to set up and tear down a TCP connection, thus each connection that can be reused reduces the number of exchanges required resulting in less traffic. Improved performance. Because subsequent requests do not need to setup and tear down a TCP connection, requests arrive faster and responses are returned quicker. TCP has built-in mechanisms, for example window sizing, to address network congestion. Persistent connections give TCP the time to adjust itself appropriately to current network conditions, thus improving overall performance. Non-persistent connections are not able to adjust because they are open and almost immediately closed. Less server overhead. Servers are able to increase the number of concurrent users served because each user requires fewer connections through which to complete requests. Persistence Persistence, on the other hand, is related to the ability of a load-balancer or other traffic management solution to maintain a virtual connection between a client and a specific server. Persistence is often referred to in the application delivery networking world as "stickiness" while in the web and application server demesne it is called "server affinity". Persistence ensures that once a client has made a connection to a specific server that subsequent requests are sent to the same server. This is very important to maintain state and session-specific information in some application architectures and for handling of SSL-enabled applications. Examples of Persistence Hash Load Balancing and Persistence LTM Source Address Persistence Enabling Session Persistence 20 Lines or Less #7: JSessionID Persistence When the first request is seen by the load-balancer it chooses a server. On subsequent requests the load-balancer will automatically choose the same server to ensure continuity of the application or, in the case of SSL, to avoid the compute intensive process of renegotiation. This persistence is often implemented using cookies but can be based on other identifying attributes such as IP address. Load-balancers that have evolved into application delivery controllers are capable of implementing persistence based on any piece of data in the application message (payload), headers, or at in the transport protocol (TCP) and network protocol (IP) layers. Some advantages of persistence are: Avoid renegotiation of SSL. By ensuring that SSL enabled connections are directed to the same server throughout a session, it is possible to avoid renegotiating the keys associated with the session, which is compute and resource intensive. This improves performance and reduces overhead on servers. No need to rewrite applications. Applications developed without load-balancing in mind may break when deployed in a load-balanced architecture because they depend on session data that is stored only on the original server on which the session was initiated. Load-balancers capable of session persistence ensure that those applications do not break by always directing requests to the same server, preserving the session data without requiring that applications be rewritten. Summize So persistent connections are connections that are kept open so they can be reused to send multiple requests, while persistence is the process of ensuring that connections and subsequent requests are sent to the same server through a load-balancer or other proxy device. Both are important facets of communication between clients, servers, and mediators like load-balancers, and increase the overall performance and efficiency of the infrastructure as well as improving the end-user experience.4.9KViews0likes2CommentsWeblogic JSessionID Persistence
Problem this snippet solves: Contributed by: unRuleY, Summarized by: deb Note: The previous version of this iRule contained escaped newlines following the session command, which in versions 10.0 - 10.2.0 causes TMM to core as documented in CR135937 / SOL11427. This was fixed in 10.2.1. See this related Codeshare example for details on how to take advantage of session replication on the WebLogic servers with targeted node failover in an iRule. Provides persistence on the jsessionid value found in either the URI or a cookie. When a request is received, the iRule first looks for a "jsessionid" cookie, and if not found, for a "jsessionid" parameter in the requested URI. If either is found, a persistence record is created if it doesn't already exist, or followed if it does. If neither is found, the request is load balanced according to the load balancing method applied to the virtual server and persisted based on the client's IP address. In order to ensure the second and subsequent requests follow the first, LTM must create a persistence record indicating the pool member to which the first request was load balanced. If the server is setting the jsessionid in a cookie, the persistence key value may be extracted from the server response to create the persistence record. If the server is setting the jsessionid in the URLs, source address persistence with a short timeout is recommended to track the original destination until the jsessionid is sent. How to use this snippet: To ensure a new persistence record is followed when a request is re-load balanced in a client-side Keep-Alive connection, apply a OneConnect profile to the virtual server. The iRule assumes the jsessionid is in upper case when used as a cookie name. If this isn't the case, please update the example. To persist on jsessionid, create the iRule below and create a custom Universal persistence profile, with Match Across Services enabled, that uses the iRule. Then use this custom Universal persistence profile as the Default Persistence profile on your Virtual Server. Applying a Fallback Persistence profile of type Source Address Affinity with a host mask and a short timeout (the default source_addr persistence profile will do the trick) to your Virtual Server is also recommended. Attention, if you are running firmware 11.0 - 11.2.1 and enabled "Match Across Services"! There is a bug inside. SOL14061 This iRule requires LTM v10. or higher. Code : when HTTP_REQUEST { # Log details for the request set log_prefix "[IP::client_addr]:[TCP::client_port]" log local0. "$log_prefix: Request to [HTTP::uri] with cookie: [HTTP::cookie value JSESSIONID]" # Check if there is a JSESSIONID cookie if { [HTTP::cookie "JSESSIONID"] ne "" }{ # Persist off of the cookie value with a timeout of 1 hour (3600 seconds) persist uie [string tolower [HTTP::cookie "JSESSIONID"]] 3600 # Log that we're using the cookie value for persistence and the persistence key if it exists. log local0. "$log_prefix: Used persistence record from cookie. Existing key? [persist lookup uie [string tolower [HTTP::cookie "JSESSIONID"]]]" } else { # Parse the jsessionid from the path. The jsessionid, when included in the URI, is in the path, # not the query string: /path/to/file.ext;jsessionid=1234?param=value set jsess [findstr [string tolower [HTTP::path]] "jsessionid=" 11] # Use the jsessionid from the path for persisting with a timeout of 1 hour (3600 seconds) if { $jsess != "" } { persist uie $jsess 3600 # Log that we're using the path jessionid for persistence and the persistence key if it exists. log local0. "$log_prefix: Used persistence record from path: [persist lookup uie $jsess]" } } } when HTTP_RESPONSE { # Check if there is a jsessionid cookie in the response if { [HTTP::cookie "JSESSIONID"] ne "" }{ # Persist off of the cookie value with a timeout of 1 hour (3600 seconds) persist add uie [string tolower [HTTP::cookie "JSESSIONID"]] 3600 log local0. "$log_prefix: Added persistence record from cookie: [persist lookup uie [string tolower [HTTP::cookie "JSESSIONID"]]]" } }4.7KViews1like7CommentsiRule to Redirect to Another VS on the same LTM
Hi guys - I'm trying to get the following scenario working: My domain "; maps to a public IP "1.1.1.1" and I have a virtual server configured on my F5 with "1.1.1.1:443" I have two unrelated applications with competing requirements running on different URIs on the same domain , one is a HR application (/abc) and the other is a inventory application (/def) I need to create a design which accomodates both applications with a single domain name, and find a way to accomodate both their conflicting requirements under the same domain name To solve this problem, I thought that I should do something like this, on the main virtual server 1.1.1.1:443, I put in an iRule with the following code: when HTTP_REQUEST { if { [string tolower [HTTP::uri]] starts_with "/abc" } { virtual VS2 } else if { [string tolower [HTTP::uri]] starts_with "/def" } { virtual VS3 } else { HTTP::respond 200 content "Unrecognised URI, please recheck your address" } } This would redirect incoming requests with "/abc" in the URI string to VS2 and incoming requests with "/def" in the URI string to VS3. Then I could create customised solutions for both applications in their respective virtual servers. So drawing this flow out, I think this is how it would look: But what I don't understand is how the Return Traffic and the Persistence Profiles work in this scenario. How do I make sure that the traffic flows this way: User to Server: User > VS1 > VS2 > Pool 2 (Server) Server to User: Pool 2 (Server) > VS2 > VS1 > User Do I do SNAT Auto Map on both VS1 and VS2 or do I just do SNAT Auto Map on VS2 with Auto Last Hop enabled? And on the question of persistence, if I intend to use source_addr persistence - I would definitely not be able to do a SNAT Auto Map on VS1, how do I then ensure that when the server responds, VS2 sends the traffic back to the user via VS1?3.7KViews0likes3CommentsSingle Node Persistence
Problem this snippet solves: A really slick & reliable way to stick to one and only one server in a pool. Requirement: Direct traffic to only a single node in a pool at a time. Initially, traffic should always go to node A. If Node A fails, then traffic will go to Node B. When Node A comes back online, traffic should continue to go to Node B. When Node B fails, then the traffic should go to Node A. To send traffic to only 1 pool member at a time, you can use an iRule and Universal Persistence to set a single persistence record that applies to all connections. Create a virtual server. Create a pool with the real servers in it. Create an iRule like this: Create a Persistence profile of type Universal which uses the iRule you just created. Set the timeout high enough so it will never expire under typical traffic conditions. In the virtual server definition, apply pool as the default pool, and the new persistence profile as the default persistence profile (both on the virtual server "resources" screen). The first connection will create a single universal persistence record with a key of "1". All subsequent connections will look up persistence using "1" as the key, resulting in truly universal persistence for all connections. (Use 1 or any constant value. 0 will have the same affect as using 1. One of my customers uses "persist uie TCP__local_port" When one node fails, the other is persisted to by all comers. When the 2nd node fails, the 1st again becomes the preferred node for all, ad infinitum. Doesn't offer the capability of manual resume after failure, or true designation of a "primary" and "secondary" instance (sometimes required for db applications), but it sure does solve the problem of "only use one node at a time, I don't care which one, please" (You can use priority to gravitate towards the top of a list...) Note: Priority-based load balancing with or without dynamic persistence doesn't quite address this requirement. Priority load balancing allows you to set a preferred server to which traffic should return once it recovers. With just Priority, and with dynamic persistence of any kind enabled, when a higher priority nodes come back up after failing, you will see traffic distributed across multiple pool members until old connections/sessions die off. With just Priority and no persistence, existing sessions will break once the preferred node again becomes available. Code : rule PriorityFailover { when CLIENT_ACCEPTED { persist uie 1 } }3.4KViews0likes25CommentsWeblogic JSessionID Persistence for Session Replication
Problem this snippet solves: Persists HTTP requests on the primary and secondary server values found in the JSESSIONID cookie when the WebLogic servers implement session replication across two servers. The actual JSESSIONID session ID is not used for peristence. The server-specific token is used to create one persistence record per pool member. This should use less LTM memory than the standard JSESSIONID perstistence iRule which creates one persistence record per client session. See this article for details on the WebLogic behavior when replicating application session replication: Load Balancers and the WebLogic Session Cookie When an HTTP request is received, the iRule looks for a JESSSIONID cookie. If found, the primary and secondary server ID hashes are parsed from the JSESSIONID cookie. If a persistence record is found for the primary server ID, it is used to persist the client request. If there isn't a primary server ID persistence record, the secondary server ID is checked for a persistence record. If neither is found or the JSESSIONID cookie is null, the request is load balanced according to the load balancing method applied to the virtual server. In order to ensure the second and subsequent requests follow the first, LTM must create a persistence record indicating the pool member to which the first request was load balanced. If the server is setting the jsessionid in a cookie, the persistence key value may be extracted from the server response to create the persistence record. If the server is setting the jsessionid in the URLs, source address persistence with a short timeout is recommended to track the original destination until the jsessionid is sent. Please post any questions or issues to the iRules forum. This version of the iRule has not been tested in production, so I'm keen to find out about any issues you may encounter. Thanks, Aaron How to use this snippet: To ensure a new persistence record is followed when a request is re-load balanced in a client-side Keep-Alive connection, apply a OneConnect profile to the virtual server. The iRule assumes the JSESSIONID cookie is in upper case when used as a cookie name. If this isn't the case, please update the example. To persist on jsessionid, create the iRule below and create a custom Universal persistence profile. Then use this custom Universal persistence profile as the Default Persistence profile on your Virtual Server. Applying a Fallback Persistence profile of type Source Address Affinity with a host mask and a short timeout (the default source_addr persistence profile will do the trick) to your virtual server is also recommended. Once done testing, comment out the LBSELECTED, LBFAILED and SERVER_CONNECTED events as they're there just for debug logging. This iRule requires LTM v10. or higher. Code : # jsessionid persistence with primary and secondary server fallback # # Uses Universal Inspection Engine (UIE) persistence to select the primary server. # If the primary is down, then the secondary server is selected. # # Cookie format from http://download.oracle.com/docs/cd/E11035_01/wls100/cluster/load_balancing.html#wp1028843 # Assumes a cookie format of: # sessionid!primary_server_id!secondary_server_id # # Use UIE persistence based on the first server ID in the application's jsessionid cookie. # If that fails, then try the second server ID. # Check server responses to determine the secondary server ID's hash token and save the mappings in the session table # when RULE_INIT { # Log debug to /var/log/ltm? 1=yes, 0=no set static::j_debug 1 # LTM persistence timeout set static::persist_timeout 3600 # Java sessiond ID cookie name set static::j_cookie "JSESSIONID" } when HTTP_REQUEST { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: [HTTP::method] to [HTTP::host][HTTP::uri]\ cookie: [HTTP::cookie $static::j_cookie]"} # Check if cookie has a value if {[HTTP::cookie $static::j_cookie] ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Scanning cookie: [HTTP::cookie $static::j_cookie]"} # Parse the three ! delineated fields from the cookie if {[scan [HTTP::cookie $static::j_cookie] {%[^!]!%[^!]!%[^!]} session pri_server_hash sec_server_hash] == 3}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie] pri: $pri_server_hash; sec: $sec_server_hash;\ pri UIE: [persist lookup uie $pri_server_hash]; sec UIE: [persist lookup uie $sec_server_hash]"} # Persist off of the primary server token if it is present, not a null value and has a current persistence record if {$pri_server_hash ne "" and $pri_server_hash ne "NONE" and [persist lookup uie $pri_server_hash] ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using existing primary UIE persistence"} persist uie $pri_server_hash $static::persist_timeout } elseif {$sec_server_hash ne "" and $sec_server_hash ne "NONE" and [persist lookup uie $sec_server_hash] ne ""}{ # Persist off of the secondary server token if it is present, not a null value and has a current persistence record if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Using existing secondary UIE persistence"} persist uie $sec_server_hash $static::persist_timeout } else { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No existing persistence record"} } } } } # Debug logging only. Remove this event after testing. when LB_SELECTED { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Selected server [LB::server]"} } # Debug logging only. Remove this event after testing. when LB_FAILED { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: LB failure for selected server [LB::server]"} } # Debug logging only. Remove this event after testing. when SERVER_CONNECTED { if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Connected server [IP::server_addr]:[TCP::server_port]"} } when HTTP_RESPONSE { # Check if cookie has a value if {[HTTP::cookie $static::j_cookie] ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie]"} # Parse the second field of the cookie value. # The current server always sets its hash as the primary server hash in the second field of the cookie. set pri_server_hash [getfield [HTTP::cookie $static::j_cookie] "!" 2] if {$pri_server_hash ne ""}{ if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Adding: persist add uie $pri_server_hash $static::persist_timeout"} # Add a persistence record for the primary server persist add uie $pri_server_hash $static::persist_timeout if {$static::j_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Cookie: [HTTP::cookie $static::j_cookie] set by [IP::server_addr]:[TCP::server_port],\ UIE: [persist lookup uie $pri_server_hash]"} } } }2.5KViews0likes2CommentsHow to generate the persistence cookie with an iRule
Problem this snippet solves: When you configure a cookie persistence profile to use the HTTP Cookie Insert or HTTP Cookie Rewrite method, the BIG-IP system inserts a cookie into the HTTP response. The cookie value contains the encoded IP address and port of the destination server. Exemple of a cookie value : 1677787402.36895.0000 (See SOL6917 for more information about this topic) Let's assume that you want your pool member to receive a copy of this cookie value in an HTTP header. Because for example you want your application to forge an url where the cookie value is in a GET parameter. (NOTE : I cannot modify the behavior of the application, I can only play with headers) Retrieving the cookie value is pretty easy with iRule : [HTTP::cookie value $cookie_name] But you'll notice that there is a little issue with this feature: when you are a new visitor, the persistence cookie is inserted in the HTTP response ... Meaning that for the very first hit made by the visitor, there will be NO cookie value to retrieve ... In my scenario it was an issue to miss this cookie value on the first hit, so I had to come up with a solution to forge the cookie value based on pool member IP and port when the persistence cookie is missing. I chose to adapt the code found here and there (thanks !) EDIT : Well I figured out that if you are not using a default route-domain the persistence cookie value will be different (see https://support.f5.com/csp/article/K6917 ) Here is the alternative code bloc to use IPv4 non-default route domains: set ADDR "[format %02x $a][format %02x $b][format %02x $c][format %02x $d]" set PORT [LB::server port] set COOKIE "rd2o00000000000000000000ffff${ADDR}o${PORT}" How to use this snippet: To summarize what the iRule does : if the persistence cookie doesn't exist (most likely because it's the very first hit), then calculate it from member IP and PORT (it obviously has to be after the "When LB_SELECTED" statement) ; else just read the existing cookie. You can set the $cookie_name parameter manually, or let the iRule identify it Code : when LB_SELECTED { #set cookie_name SERVERID # following function could determine persistence cookie name being used if not manually set by the previous line if {not [info exists cookie_name]} { if { [set cookie_name [PROFILE::persist mode cookie cookie_name]] eq "" } { set cookie_name "BIGipServer[getfield [LB::server pool] "/" 3]" } #Default cookie name requires the getfield "/" 3 purge otherwise it's /Common/pool_name } if { [set COOKIE [HTTP::cookie value $cookie_name]] == "" } { scan [LB::server addr] {%d.%d.%d.%d} a b c d set ADDR [expr { $a + $b * 256 + $c * 65536 + $d * 16777216 }] set PORT [ntohs [LB::server port]] set COOKIE "${ADDR}.${PORT}.0000" ## Following bloc must be used instead if you are using non-default route domains, see K6917 #set ADDR "[format %02x $a][format %02x $b][format %02x $c][format %02x $d]" #set PORT [LB::server port] #set COOKIE "rd2o00000000000000000000ffff${ADDR}o${PORT}" ######### unset a b c d ADDR PORT #log local0. "$cookie_name = $COOKIE created for [HTTP::uri]" } else { #log local0. "$cookie_name = $COOKIE already exists for [HTTP::uri]" } HTTP::header insert X-F5-persist $COOKIE } Tested this on version: 11.52.4KViews2likes1CommentLoad Balancing Fu: Beware the Algorithm and Sticky Sessions
The choice of load balancing algorithms can directly impact – for good or ill – the performance, behavior and capacity of applications. Beware making incompatible choices in architecture and algorithms. One of the most persistent issues encountered when deploying applications in scalable architectures involves sessions and the need for persistence-based (a.k.a. sticky) load balancing services to maintain state for the duration of an end-user’s session. It is common enough that even the rudimentary load balancing services offered by cloud computing providers such as Amazon include the option to enable persistence-based load balancing. While the use of persistence addresses the problem of maintaining session state, it introduces other operational issues that must also be addressed to ensure consistent operational behavior of load balancing services. In particular, the use of the Round Robin load balancing algorithm in conjunction with persistence-based load balancing should be discouraged if not outright disallowed. ROUND ROBIN + PERSISTENCE –> POTENTIALLY UNEQUAL DISTRIBUTION of LOAD When scaling applications there are two primary concerns: concurrent user capacity and performance. These two concerns are interrelated in that as capacity is consumed, performance degrades. This is particularly true of applications storing state as each request requires that the application server perform a lookup to retrieve the user session. The more sessions stored, the longer it takes to find and retrieve the session. The exactly efficiency of such lookups is determined by the underlying storage data structure and algorithm used to search the structure for the appropriate session. If you remember your undergraduate classes in data structures and computing Big (O) you’ll remember that some structures scale more efficiently in terms of performance than do others. The general rule of thumb, however, is that the more data stored, the longer the lookup. Only the amount of degradation is variable based on the efficiency of the algorithms used. Therefore, the more sessions in use on an application server instance, the poorer the performance. This is one of the reasons you want to choose a load balancing algorithm that evenly distributes load across all instances and ultimately why lots of little web servers scaled out offer better performance than a few, scaled up web servers. Now, when you apply persistence to the load balancing equation it essentially interrupts the normal operation of the algorithm, ignoring it. That’s the way it’s supposed to work: the algorithm essentially applies only to requests until a server-side session (state) is established and thereafter (when the session has been created) you want the end-user to interact with the same server to ensure consistent and expected application behavior. For example, consider this solution note for BIG-IP. Note that this is true of all load balancing services: A persistence profile allows a returning client to connect directly to the server to which it last connected. In some cases, assigning a persistence profile to a virtual server can create the appearance that the BIG-IP system is incorrectly distributing more requests to a particular server. However, when you enable a persistence profile for a virtual server, a returning client is allowed to bypass the load balancing method and connect directly to the pool member. As a result, the traffic load across pool members may be uneven, especially if the persistence profile is configured with a high timeout value. -- Causes of Uneven Traffic Distribution Across BIG-IP Pool Members So far so good. The problem with round robin- – and reason I’m picking on Round Robin specifically - is that round robin is pretty, well, dumb in its decision making. It doesn’t factor anything into its decision regarding which instance gets the next request. It’s as simple as “next in line", period. Depending on the number of users and at what point a session is created, this can lead to scenarios in which the majority of sessions are created on just a few instances. The result is a couple of overwhelmed instances (with performance degradations commensurate with the reduction in available resources) and a bunch of barely touched instances. The smaller the pool of instances, the more likely it is that a small number of servers will be disproportionately burdened. Again, lots of little (virtual) web servers scales out more evenly and efficiently than a few big (virtual) web servers. Assuming a pool of similarly-capable instances (RAM and CPU about equal on all) there are other load balancing algorithms that should be considered more appropriate for use in conjunction with persistence-based load balancing configurations. Least connections should provide better distribution, although the assumption that an active connection is equivalent to the number of sessions currently in memory on the application server could prove to be incorrect at some point, leading to the same situation as would be the case with the choice of round robin. It is still a better option, but not an infallible one. Fastest response time is likely a better indicator of capacity as we know that responses times increase along with resource consumption, thus a faster responding instance is likely (but not guaranteed) to have more capacity available. Again, this algorithm in conjunction with persistence is not a panacea. Better options for a load balancing algorithm include those that are application aware; that is, algorithms that can factor into the decision making process the current load on the application instance and thus direct requests toward less burdened instances, resulting in a more even distribution of load across available instances. NON-ALGORITHMIC SOLUTIONS There are also non-algorithmic, i.e. architectural, solutions that can address this issue. DIVIDE and CONQUER In cloud computing environments, where it is less likely to find available algorithms other than industry standard (none of which are application-aware), it may be necessary to approach the problem with a divide and conquer strategy, i.e. lots of little servers. Rather than choosing one or two “large” instances, choose to scale out with four or five “small” instances, thus providing a better (but not guaranteed) statistical chance of load being distributed more evenly across instances. FLANKING STRATEGY If the option is available, an architectural “flanking” strategy that leverages layer 7 load balancing, a.k.a. content/application switching, will also provide better consumptive rates as well as more consistent performance. An architectural strategy of this sort is in line with sharding practices at the data layer in that it separates out by some attribute different kinds of content and serves that content from separate pools. Thus, image or other static content may come from one pool of resources while session-oriented, process intensive dynamic content may come from another pool. This allows different strategies – and algorithms – to be used simultaneously without sacrificing the notion of a single point of entry through which all users interact on the client-side. Regardless of how you choose to address the potential impact on capacity, it is important to recognize the intimate relationship between infrastructure services and applications. A more integrated architectural approach to application delivery can result in a much more efficient and better performing application. Understanding the relationship between delivery services and application performance and capacity can also help improve on operational costs, especially in cloud computing environments that constrain the choices of load balancing algorithms. As always, test early and test often and test under high load if you want to be assured that the load balancing algorithm is suitable to meet your operational and business requirements. WILS: Why Does Load Balancing Improve Application Performance? Load Balancing in a Cloud Infrastructure Scalability Pattern: Sharding Sessions Infrastructure Scalability Pattern: Partition by Function or Type It’s 2am: Do You Know What Algorithm Your Load Balancer is Using? Lots of Little Virtual Web Applications Scale Out Better than Scaling Up Sessions, Sessions Everywhere Choosing a Load Balancing Algorithm Requires DevOps Fu Amazon Makes the Cloud Sticky To Boldly Go Where No Production Application Has Gone Before Cloud Testing: The Next Generation2.3KViews0likes1CommentXFF Universal Persistence iRule
Problem this snippet solves: Simple iRule to read the XFF header on an incoming HTTP Request and use a Universal Persistence ID. Orginal iRule found to have an issue with multiple IP addresses in the XFF header for changed to only pass the first XFF IP. I have updated the iRule line to account for systems where multiple 'X-Forwarded-For' headers have been added. persist uie [lindex [ split [HTTP::header X-Forwarded-For] "," ] 0] to persist uie [lindex [ split [lindex [HTTP::header values X-Forwarded-For] 0] "," ] 0] thanks to the advice from Yann Desmarest. This could also be done with the 'getfield' command see Yann's comments below. How to use this snippet: Create iRule using following code (mine is named 'persist_xff_uie') Create Universal Persistence Profile with iRule set to 'persist_xff_uie' (or what ever name you assign to the iRule) Assign Universal Persistence Profile to Virtual Server (ensure virtual server has HTTP profile assigned) Code : # Name: persist_xff_uie # # To be used with UIE Persistence Profile # # Checks HTTP Request for 'X-Forwarded-For' header and if exists takes the first 'X-Forwarded-For' IP address as sets as # Persist identifier. # If the 'X-Forwarded-For' header does not exist then the client IP address is set as Persist identifier. when HTTP_REQUEST { if {[HTTP::header X-Forwarded-For] != ""} then { persist uie [lindex [ split [lindex [HTTP::header values X-Forwarded-For] 0] "," ] 0] } else { persist uie [IP::client_addr] } } Tested this on version: 11.52.3KViews1like7CommentsSessions and Cookies and Persistence, oh my!
At some point (you hope!) it becomes necessary to implement load-balancing for your applications. So you went out and got one, either from a hardware vendor or maybe downloaded a solution, and put it into place. Now you're ready to go, right? Maybe not just yet. Do your applications require persistence? Yes? You did remember to validate that your solution is capable of performing persistence-based load-balancing, didn't you? If you're shaking your head wondering why this application thing is important to load balancing, read on. Persistence is one of the best examples of why it's so very important to understand how the applications you will be load-balancing work, because if an application needs persistence, you may break it without a persistent capable load-balancing solution. The Relationship between Sessions and Cookies Sessions are not cookies, but they can (and do) work together to create the illusion of persistence in an otherwise stateless protocol. Sometimes persistence is referred to as "stickiness", or "sticky connections." That's because what persistence does is ensure that a client connects to the "real" server on which his/her current session is active. When a user connects the first time to a site, a session is created on the server to which the user was directed. If the site is load balanced and the user is directed to a second server on the next request, a new session is created. Obviously this is not an optimal situation. What we need is some mechanism to ensure that a user is reconnected to the same server for the duration of a session. Persistence is the process of ensuring that a user is connected to the same server every time they make a request within the boundaries of a single session. Even though users could be persisted based on their IP address, this is rarely done due to the sharing of IP addresses. Persistence is most often implemented using a cookie containing the server session id because it is the most accurate method of determining where a user's session is currently stored. If you're a web developer or administrator, you might think "that sounds a lot like server affinity". You'd be right, of course, server affinity and persistence are two different terms that mean the same thing. Sessions are stored on the server, and are not reliant on cookies being enable in the client's browser. Sessions are where web developers store bits of application relevant data that they may wish to use across requests. Shopping carts are the most ubiquitous example of session data, but there are other uses for it, especially in complex web applications like CRM (customer relationship management) or SFA (sales force automation) applications. Cookies store bits of data on the client (the browser) and are passed to the server via the HTTP header Cookie. Without persistence, users would be unknowingly creating sessions willy-nilly across multiple web servers in a load-balanced environment. That's a waste of resources, as sessions will remain in memory on the web server until they time out according to the web server's configuration. Additionally, a lack of persistence breaks web applications, because the data stored in the session on previous requests is no longer accessible on Server 2 because it's still sitting over on Server 1. This is why it's so important that a load-balancing or application delivery solution is capable of handling persistence-based load distribution. If your load-balancing solution works based on an industry standard algorithm like round-robin, least-connections, or a weighted version of either, then you're likely to break those applications which require persistence because the load balancing algorithms aren't taking session persistence needs into consideration. Persisting Connections The most common data used to persist connections is SSL session id. SSL connections without persistence is like crust without the bread. Yeah, it's that bad. Basically, load balancing SSL without persistence doesn't work. The second most common data used to persist connections is application or server session id, like JSESSIONID or PHPSESSIONID. These IDs are automatically generated by applications and web servers, and are generally passed to the client as a cookie on the first response, and then used by the load balancer to determine to which server it should direct subsequent requests. An example of HTTP headers storing a JSESSIONID in a cookie: Cookie: JSESSIONID=9597856473431 Cache-Control: no-cache Host: 127.0.0.2:8080 Connection: Keep-Alive Your chosen load-balancing or application delivery solution needs to be able to take application session data into consideration when making routing decisions. It must be able to look at HTTP headers and extract the data the web application stored to determine which server it should direct the request to, or you risk breaking your web applications and wasting resources on your servers. Imbibing: Coffee ADDITIONAL RESOURCES Colin has a great entry in his 20LOL series that implements JSessionID based persistence. The iRule should be easily modified to support other types of application ID based persistence, as long as the value is stored in the HTTP Headers somewhere. And Joe has a short article on enabling session persistence on BIG-IP. Wikipedia has a great discussion on Web server session management here.2.2KViews0likes6Comments