crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs

tips.yo (48430B)


      1 The following sections elaborate on the directives as described in
      2 section ref(config) to illustrate how crossroads works and to help you
      3 achieve the "optimal" balancing configuration.
      4 
      5 subsect(How back ends are selected in load balancing)label(howselected)
      6 
      7 In order to tune your load balancing, you'll need to understand how
      8 crossroads computes usage, how weighing works, and so on. In this
      9 section we'll focus on the dispatching modes tt(bysize), tt(byduration)
     10 and tt(byconnections) only. The other dispatching types are
     11 self-explanatory. 
     12 
     13 
     14 subsubsect(Bysize, byduration or byconnections?)
     15 
     16 As stated before, crossroads doesn't know 'what a service does' and
     17 how to judge whether a given back end is very busy or not. You
     18 must therefore give the right hints:
     19 
     20 itemization(
     21         it() In general, a service which is CPU bound, will be more
     22         busy when it takes longer to process a request. The dispatch
     23         mode tt(byduration) is appropriate here.
     24 
     25         it() In contrast, a service which is filesystem bound, will be
     26         more busy when more data are transferred. The dispatch mode
     27         tt(bysize) is apppropriate.
     28 
     29         it() The dispatch mode tt(byduration) can also be used when
     30         network latency is an issue. E.g., if your balancer has back
     31         ends that are geograpically distributed, then tt(byduration)
     32         would be a good way to select best available back ends.
     33 
     34         it() Furthermore it is noteworthy that tt(dispatchmode
     35         byduration) is not usable for interactive processes such as
     36         SSH logins. Idle time of a
     37         login adds to the duration, while causing (almost) no
     38         load. Mode tt(byduration) should only be used for automated
     39         processes that don't wait for user interaction (e.g., SOAP
     40         calls and other HTTP requests).
     41 
     42         it() As a last remark, the dispatching mode tt(byconnections) can
     43         be used if you don't have other clues for load
     44         estimations.
     45 
     46         E.g., consider a database connection. What's
     47         heavier on the back end, time-consuming connections, or connections
     48         where loads of bytes are transferred? Well, that depends. A
     49         tough tt(select) query that joins multiple tables can be very
     50         heavy on the back end, though the response set can be quite
     51         small - and hence the number of
     52         transferred bytes. That would suggest
     53         dispatching by duration. However, tt(byduration)
     54         balancing doesn't respresent the true world, when interactive
     55         connections can occur where users have an idle TCP connection to
     56         the database:
     57         this consumes time, but no bytes (see the SSH login example
     58         above). In this case, the dispatch mode tt(byconnections) may be
     59         your best bet.
     60 
     61         ) 
     62 
     63 
     64 subsubsect(Averaging size and duration)
     65 
     66 The configuration statement tt(dispatchmode bysize) or tt(byduration)
     67 allows an optional modifier tt(over) em(number), where the stated
     68 number represents a connection count. When this modifier is present, then
     69 crossroads will use a moving average over the last em(n) connections to
     70 compute duration and size figures.
     71 
     72 In the real world you'll always want this modifier. E.g., consider two
     73 back ends that are running for years now, and one of them is suddenly
     74 overloaded and very busy (it experiences a 'spike' in activity).
     75 When the tt(over) modifier is absent, then
     76 the sudden load will hardly show up in the usage figures -- it will
     77 flatten out due to the large usage figures already stored in the years
     78 of service.
     79 
     80 In contrast, when e.g. tt(over 3) is in effect, then a sudden load
     81 does show up -- because it highly contributes to the average of three
     82 connections.
     83 
     84 
     85 subsubsect(Specifying decays)
     86 
     87 Decays are also only relevant when crossroads computes the 'next best
     88 back end' by size (bytes) or duration (seconds). E.g., imagine two
     89 back ends A and B, both averaged over say 3 connections.
     90 
     91 Now when back end A is suddenly hit by a spike,
     92 its average would go up accordingly. But the back end would never
     93 again be used, unless B also received a similar spike, because A's
     94 'usage data' over its last three connections would forever be larger than
     95 B's data. 
     96 
     97 For that reason, you should in real situations probably always
     98 specify a decay, so that the backend selection algorithm recovers from
     99 spikes. Note that the usage data of the back end where a decay is
    100 specified, decay when bf(other) back ends are hit. The decay parameter
    101 is like specifying how fast your body regenerates when someone else
    102 does the work.
    103 
    104 The below configuration illustrates this:
    105 
    106 verb(\
    107 /* Definition of the service */
    108 service soap {
    109     /* Local TCP port */
    110     port 8080;
    111 
    112     /* We'll select back ends by the processing
    113      * duration
    114      */
    115     dispatchmode byduration over 3;
    116 
    117     /* First back end: */
    118     backend A {
    119         /* Back end IP address and port */
    120         server 10.1.1.1:8080;
    121 
    122         /* When this back end is NOT hit because
    123          * the other one was less busy, then the
    124          * usage parameters decay 10% per connection
    125          */
    126         decay 10;
    127     }
    128 
    129     /* Second back end: */
    130     backend B {
    131         server 10.1.1.2:8080;
    132         decay 10;
    133     }
    134 })
    135 
    136 subsubsect(Adjusting the weights)
    137 
    138 The back end modifier tt(weight) is useful in situations where your
    139 back ends differ in respect to performance. E.g,. your back ends may
    140 be geographically distributed, and you know that a given back end is
    141 difficult to reach and often experiences network lag.
    142 
    143 Or you may have
    144 one primary back end, a system with a fast CPU and enough memory, and a
    145 small fall-back back end, with a slow CPU and short on memory. In that
    146 case you know in advance that the second back end should be used only
    147 rarely. Most requests should go to the big server, up to a certain load.
    148 
    149 In such cases you will know in advance that the best performing back ends
    150 should be selected the most often. Here's where the tt(weight)
    151 statement comes in: you can simply increase the weight of the back
    152 ends with the least performance, so that they are selected less
    153 frequently.
    154 
    155 E.g., consider the following configuration:
    156 
    157 verb(\
    158 service soap {
    159     port 8080;
    160     dispatchmode byduration over 3;
    161     backend A {
    162         server 10.1.1.1:8080;
    163         decay 20;
    164     }
    165     backend B {
    166         server 10.1.1.2:8080;
    167         weight 2;
    168         decay 10;
    169     }
    170     backend C {
    171         server 10.1.1.3:8080;
    172         weight 4;
    173         decay 5;
    174     }
    175 })
    176 
    177 This will cause crossroads to select back ends by the processing time,
    178 averaging over the last three connections. However, backend B will kick
    179 in only when its usage is half of the usage of A (back end B is
    180 probably only half as fast as A). Backend C will kick in only when its
    181 usage is a quarter of the usage of A, which is half of the usage of B
    182 (back end C is probably very weak, and just a fall-back system incase
    183 both A and B crash). Note also that A's usage data decay much faster
    184 than B's and C's: we're assuming that this big server recovers quicker
    185 than its smaller siblings.
    186 
    187 
    188 subsubsect(Throttling the number of concurrent connections)
    189 
    190 If you suspect that your service may occasionally receive 'spikes' of
    191 activity+footnote(which you should always assume), then it might be a
    192 good idea to protect your service by specifying a maximum number of
    193 concurrent connections. This protection can be specified on two levels:
    194 
    195 description(
    196         dit(On the service level) a statement like tt(maxconnections
    197             100;) states that the service as a whole will never
    198             service more than 100 concurrent connections. This means that
    199             all your back ends and the crossroads balancer itself
    200             will be protected from being overloaded.
    201         dit(On the back end level) a statement like tt(maxconnections 10;)
    202             states that this particular back end will never have more
    203             than 10 concurrent connections; regardless of the overall
    204             setting on the service level. This means that this
    205             particular back end will be protected from being
    206             overloaded (regardless of what other back ends may
    207             experience).)
    208 
    209 The tt(maxconnections) statement, combined with a back end selection
    210 algorithm, allows very fine granularity. The tt(maxconnections) statement
    211 on the back end level is like a hand brake: even when you specify a
    212 back end algorithm that would protect a given back end from being used
    213 too much, a situation may occur where that back end is about to be
    214 hit. A tt(maxconnections) statement on the level of that back may then
    215 protect it.
    216 
    217 
    218 subsect(Using an external program to dispatch)
    219 label(externalhandler)
    220 
    221 As mentioned before, Crossroads supports several built-in dispatch
    222 modes. However, you are always free to hook-in your own dispatch mode
    223 that determines the next back end using your own specific
    224 algorithm. This section explains how to do it.
    225 
    226 subsubsect(Configuring the external handler)
    227 
    228 First, the tt(dispatchmode) statement needs to inform Crossroads that
    229 an external program will do the job. The syntax is: tt(dispatchmode
    230 externalhandler) em(program arguments). The em(program) must point to
    231 an executable program that will be started by Crossroads. The
    232 specifier em(arguments) can be anything you want; those will be the
    233 arguments to Crossroads. You can however use the following special
    234 format specifiers:
    235 
    236 INCLUDEFILE(formattable)
    237 
    238 Note that the format specifiers such as tt(%b) don't make sense in the
    239 phase in which an external handler is called, since there is no
    240 current back end yet (the job of the handler is to supply one).
    241 
    242 subsubsect(Writing the external handler)
    243 
    244 The external handler is activated using the arguments that are
    245 specified in tt(/etc/crossroads.conf). The external handler can do
    246 whatever it wants, but ultimately, it must write a back end name on
    247 its em(stdout). Crossroads reads this, and if the back end is
    248 available, uses that back end for the connection.
    249 
    250 subsubsect(Examples of external handlers)
    251 
    252 This section shows some examples of Crossroads configurations
    253 vs. external handlers. The sample handlers that are shown here, are
    254 also included in the Crossroads distribution, under the directory
    255 tt(etc/). Also note that the examples shown here are just
    256 quick-and-dirty Perl scripts, meant to illustrate only. Your
    257 applications may need other external handlers, but you can use the
    258 shown scripts as a starting point.
    259 
    260 subsubsubsect(Round-robin dispatching)
    261 
    262 This example is trivial in the sense that round-robin dispatching is
    263 already built into Crossroads, so
    264 that using an external handler for this purpose only slows down
    265 Crossroads. However, it's a good starting example.
    266 
    267 The Crossroads configuration is shown below:
    268 
    269 verb(\
    270 service test {
    271     port 8001;
    272     verbosity on;
    273     revivinginterval 5;
    274     
    275     dispatchmode externalhandler
    276         /usr/local/src/crossroads/etc/dispatcher-roundrobin
    277             %1b %1a %2b %2a;
    278 
    279     backend testone {
    280         server localhost:3128;
    281         verbosity on;
    282     }
    283     backend testtwo {
    284         server locallhost:3128;
    285         verbosity on;
    286     }
    287 })
    288 
    289 The relevant tt(dispatchmode) statement invokes the external program
    290 tt(dispatcher-roundrobin) with four arguments: the name of the first
    291 back end (tt(testone)), its availability (0 or 1), the name of the
    292 second back end (tt(testtwo)) and its availability (0 or 1).
    293 
    294 The external handler, which is also included in the Crossroads
    295 distribution, is shown below. It is a Perl script.
    296 
    297 verb(\
    298 #!/usr/bin/perl
    299 
    300 use strict;
    301 
    302 # Example of a round-robin external dispatcher. This is totally
    303 # superfluous, Crossroads has this on-board; if you use the external
    304 # program for determining round-robin dispatching, then you'll only
    305 # slow things down. This script is just meant as an example.
    306 
    307 # Globals / configuration
    308 # -----------------------
    309 my $log = '/tmp/exthandler.log';    # Debug log, set to /dev/null to suppress
    310 my $statefile = '/tmp/rr.last';	    # Where we keep the last used
    311 
    312 # Logging
    313 # -------
    314 sub msg {
    315     return if ($log eq '/dev/null' or $log eq '');
    316     open (my $of, ">>$log") or return;
    317     print $of (scalar(localtime()), ' ', @_);
    318 }
    319 
    320 # Read the last used back end
    321 # ---------------------------
    322 sub readlast() {
    323     my $ret;
    324     
    325     if (open (my $if, $statefile)) {
    326         $ret = <$if>;
    327         chomp ($ret);
    328         close ($if);
    329         msg ("Last used back end: $ret\n");
    330         return ($ret);
    331     }
    332     msg ("No last-used back end (yet)\n");
    333     return (undef);    
    334 }
    335 
    336 # Write back the last used back end, reply to Crossroads and stop
    337 # ---------------------------------------------------------------
    338 sub reply ($) {
    339     my $last = shift;
    340 
    341     if (open (my $of, ">$statefile")) {
    342         print $of ("$last\n");
    343     }
    344     print ("$last\n");
    345     exit (0);
    346 }
    347 
    348 # Main starts here
    349 # ----------------
    350 
    351 # Collect the cmdline arguments. We expect pairs of backend-name /
    352 # backend-availablility, and we'll store only the available ones.
    353 msg ("Dispatch request received\n");
    354 my @backend;
    355 for (my $i = 0; $i <= $#ARGV; $i += 2) {
    356     push (@backend,  $ARGV[$i]) if ($ARGV[$i + 1]);
    357 }
    358 msg ("Available back ends: @backend\n");
    359 
    360 # Let's see what the last one is. If none found, then we return the
    361 # first available back end. Otherwise we need to go thru the list of
    362 # back ends, and return the next one in line.
    363 my $last = readlast();
    364 if ($last eq '') {
    365     msg ("Returning first available back end $backend[0]\n");
    366     reply ($backend[0]);
    367 }
    368 
    369 # There **was** a last back end.  Try to match it in the list,
    370 # then return the next-in-line.
    371 for (my $i = 0; $i < $#backend; $i++) {
    372     if ($last eq $backend[$i]) {
    373         msg ("Returning next back end ", $backend[$i + 1], "\n");
    374         reply ($backend[$i + 1]);
    375     }
    376 }
    377 
    378 # No luck.. run back to the first one.
    379 msg ("Returning first back end $backend[0]\n");
    380 reply ($backend[0]);)
    381 
    382 The working of the script is basically as follows:
    383 
    384 itemization(
    385         it() The argument list is scanned. Back ends that are
    386         available are collected in an array tt(@backend).
    387 
    388         it() The script queries a state file tt(/tmp/rr.last). If a
    389         back end name occurs there, then the next back end is looked
    390         up in tt(@backend) and returned to Crossroads. If no last back
    391         is unknown or can't be matched, then the first available back
    392         end (first element of tt(@backend)) is returned to Crossroads.
    393 
    394         it() Informing Crossroads is done via the subroutine
    395         tt(reply()). This code writes the selected back end to file
    396         tt(/tmp/rr.last) (for future usage) and prints the back end
    397         name to em(stdout).
    398 
    399         it() The script logs its actions to a file
    400         tt(/tmp/exthandler.log). This log file can be inspected for
    401         the script's actions.)
    402 
    403         
    404 subsubsubsect(Dispatching by the client IP address)
    405 
    406 The following example shows a useful real-life situation. The
    407 situation is as follows:
    408 
    409 itemization(
    410         it() Crossroads is used as a single-address point to forward
    411         Remote Desktop requests to a farm of Windows systems, where
    412         users can work via remote access;
    413 
    414         it() However, users may stop their session, and when they
    415         re-connect, they expect to be sent to the Windows system that
    416         they had worked on previously;
    417 
    418         it() Client PC's have their distinct IP addresses, which
    419         distinguishes them.
    420 
    421         it() Of four windows systems, two are large servers, and two
    422         are small ones. We'll want to assign large servers to clients
    423         when we have a choice.)
    424 
    425 The requirements resemble session stickiness in HTTP, except that the remote
    426 desktop protocol doesn't support stickiness. This situation is a
    427 perfect example of how an external handler can help:
    428 
    429 itemization(
    430         it() A suitable dispatch mode isn't yet available in
    431         Crossroads, but can be easily coded in an external handler;
    432 
    433         it() The potential delay due to the calling of an external
    434         handler won't even be noticed. This is a network service where
    435         the connection time isn't critical; we'd expect only a few
    436         (albeit lengthy) TCP connections.)
    437 
    438 The approach to the solution of this problem uses several external
    439 program hooks:
    440 
    441 itemization(
    442         it() An external dispatcher handler will be responsible for
    443         suggesting a back end, given a client IP and given the current
    444         timestamp. This handler will consult an internal
    445         administration to see whether the stated IP address should
    446         re-use a back end, or to determine which back end is free for usage.
    447         it() An external hook tt(onstart) will be responsible for
    448         updating the internal administration; i.e., to flag a back end
    449         as 'occupied'.
    450         it() The external hooks tt(onfailure) and tt(onend) will be
    451         responsible for flagging a back end as 'free' again; i.e., for
    452         erasing any previous information that states that the back end
    453         was occupied.)
    454 
    455 The Crossroads configuration is shown below. Only four Windows back
    456 ends are shown. Each back end is configured on a
    457 given IP address, port 3389, and is limited to one concurrent connection
    458 (otherwise a new user might 'steal' a running desktop session).
    459 
    460 verb(\
    461 service rdp {
    462     port 3389;
    463     revivinginterval 5;
    464 
    465     /* rdp-helper dispatch IP STAMP ...  will suggest a back end to use,
    466      * arguments are for all back ends: name, availability, weight */
    467     dispatchmode externalhandler
    468         /usr/local/src/crossroads/etc/rdp-helper dispatch %r %e
    469             %1b %1a %1w
    470             %2b %2a %2w
    471             %3b %3a %3w
    472             %4b %4a %4w;
    473             
    474     backend win1 {
    475         server 10.1.1.1:3389;
    476         maxconnections 1;
    477         /* rdp-helper start IP STAMP BACKEND will log the actual start
    478          * of a connection;
    479          * rdp-helper end IP will log the ending of a connection */
    480         onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
    481         onend   /usr/local/src/crossroads/etc/rdp-helper end %r;
    482         onfail  /usr/local/src/crossroads/etc/rdp-helper end %r;
    483     }
    484     backend win2 {
    485         server 10.1.1.2:3389;
    486         maxconnections 1;
    487         onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
    488         onend   /usr/local/src/crossroads/etc/rdp-helper end %r;
    489         onfail  /usr/local/src/crossroads/etc/rdp-helper end %r;
    490     }
    491     backend win3 {
    492         server 10.1.1.3:3389;
    493         maxconnections 1;
    494         weight 2;
    495         onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
    496         onend   /usr/local/src/crossroads/etc/rdp-helper end %r;
    497         onfail  /usr/local/src/crossroads/etc/rdp-helper end %r;
    498     }
    499     backend win4 {
    500         server 10.1.1.4:3389;
    501         maxconnections 1;
    502         weight 3;
    503         onstart /usr/local/src/crossroads/etc/rdp-helper start %r %e %b;
    504         onend   /usr/local/src/crossroads/etc/rdp-helper end %r;
    505         onfail  /usr/local/src/crossroads/etc/rdp-helper end %r;
    506     }
    507 })
    508 
    509 Depending on the dispatcher stage, the exernal handler tt(rdp-helper)
    510 is invoked in different ways:
    511 
    512 description(
    513         dit(During dispatching) the helper is called to suggest a back
    514         end. The arguments are an action indicator tt(dispatch), the
    515         client's IP address, the timestamp, and four triplets that
    516         represent back ends: per back end its name, its availability,
    517         and its weight. The purpose of the helper is to tell
    518         Crossroads which back end to use.
    519 
    520         dit(During connection start) the helper will be invoked to
    521         inform it of the start of a connection, given a client IP
    522         address.
    523 
    524         dit(When a connection terminates) the helper will be invoked
    525         to inform it that the connection has ended.)
    526 
    527 Here's the external handler as Perl script. It uses the module
    528 tt(GDBM_File) which most likely will not be part of standard Perl
    529 distributions, but can be added using CPAN. (Alternatively, any other
    530 database module can be used.)
    531 
    532 verb(\
    533 #!/usr/bin/perl
    534 
    535 use strict;
    536 use GDBM_File;
    537 
    538 # Global variables and configuration
    539 # ----------------------------------
    540 my $log = '/tmp/exthandler.log';    # Debug log, set to /dev/null to suppress
    541 my $cdb = '/tmp/client.db';         # GDBM database of clients
    542 my %db;                             # .. and memory representation of it
    543 my $timeout = 24*60*60;             # Timeout of a connection in secs
    544 
    545 # Logging
    546 # -------
    547 sub msg {
    548     return if ($log eq '/dev/null' or $log eq '');
    549     open (my $of, ">>$log") or return;
    550     print $of (scalar(localtime()), ' ', @_);
    551     close ($of);
    552 }
    553 
    554 # Reply a back end to the caller and stop processing.
    555 # ---------------------------------------------------
    556 sub reply ($) {
    557     my $b = shift;
    558     msg ("Suggesting $b to Crossroads.\n");
    559     print ("$b\n");
    560     exit (0);
    561 }
    562 
    563 # Is a value in an array
    564 # ----------------------
    565 sub inarray {
    566     my $val = shift;
    567     for my $other (@_) {
    568         return (1) if ($other eq $val);
    569     }
    570     return (0);
    571 }
    572 
    573 # A connection is starting
    574 # ------------------------
    575 sub start {
    576     my ($ip, $stamp, $backend) = @_;
    577     msg ("Logging START of connection for IP $ip on stamp $stamp, ",
    578          "back end $backend\n");
    579     $db{$ip} = "$backend:$stamp";
    580 }
    581 
    582 # A connection has ended
    583 # ----------------------
    584 sub end {
    585     my $ip = shift;
    586     msg ("Logging END of connection for IP $ip\n");
    587     $db{$ip} = undef;
    588 }
    589 
    590 # Request to determine a back end
    591 # -------------------------------
    592 sub dispatch {
    593     my $ip = shift;
    594     my $stamp = shift;
    595 
    596     msg ("Request to dispatch IP $ip on stamp $stamp\n");
    597     
    598     # Read the next arguments. They are triplets of
    599     # backend-name / availability / weight. Store if the back end is
    600     # available.
    601     my (@backends, @weights);
    602     for (my $i = 0; $i < $#_; $i += 3) {
    603         if ($_[$i + 1] != 0) {
    604             push (@backends, $_[$i]);
    605             push (@weights,  $_[$i + 2]);
    606             msg ("Candidate back end: $_[$i] with weight ", $_[$i + 2], "\n");
    607         }
    608     }
    609 
    610     # See if this is a reconnect by a previously seen client IP. We'll
    611     # treat this as a reconnect if the timeout wasn't yet exceeded.
    612     if ($db{$ip} ne '') {
    613         my ($last_backend, $last_stamp) = split (/:/, $db{$ip});
    614         msg ("IP $ip had last connected on $last_stamp to $last_backend\n");
    615         if ($stamp < $last_stamp + $timeout) {
    616             msg ("Timeout not yet exceeded, this may be a reconnect\n");
    617             # We'll allow a reconnect only if the stated last_backend is
    618             # free (sanity check).
    619             if (inarray ($last_backend, @backends)) {
    620                 msg ("Last back end $last_backend is available, ",
    621                      "letting through\n");
    622                 reply ($last_backend);
    623             } else {
    624                 msg ("Last used back end isn't free, suggesting a new one\n");
    625             }
    626         } else {
    627             msg ("Timeout exceeded, suggesting a new back end\n");
    628         }
    629     } else {
    630         msg ("Np preveious connection data, suggesting a new back end\n");
    631     }
    632 
    633     my $bestweight = -1;
    634     my $bestbackend;
    635     for (my $i = 0; $i <= $#weights; $i++) {
    636         if ($bestweight == -1 or $bestweight > $weights[$i]) {
    637             $bestweight  = $weights[$i];
    638             $bestbackend = $backends[$i];
    639         }
    640     }
    641 
    642     msg ("Best back end: $bestbackend (given weight $bestweight)\n");
    643     reply ($bestbackend);
    644 }
    645 
    646 # Main starts here
    647 # ----------------
    648 msg ("Start of run, attaching GDBM database '$cdb'\n");
    649 tie (%db, 'GDBM_File', $cdb, &GDBM_WRCREAT, 0600);
    650 
    651 # The first argument must be an action 'dispatch', 'start' or 'end'.
    652 # Depending on the action, we do stuff.
    653 my $action = shift (@ARGV);
    654 if ($action eq 'dispatch') {
    655     dispatch (@ARGV);
    656 } elsif ($action eq 'start') {
    657     start (@ARGV);
    658 } elsif ($action eq 'end') {
    659     end (@ARGV);
    660 } else {
    661     print STDERR ("Usage: rdp-helper {dispatch|start|end} args\n");
    662     exit (1);
    663 })
    664 
    665 
    666 subsect(HTTP Session Stickiness)
    667 
    668 This section focuses on HTTP session stickiness. This term refers to
    669 the ability of a balancer to route a conversation between browser and
    670 a backend farm always to the same back end. In other words: once a
    671 back end is selected by the balancer, it will remain the back end of
    672 choice, even for subsequent connections.
    673 
    674 subsubsect(Don't use stickiness!)
    675 
    676 The rule of thumb as far as the balancer is concerned, is: bf(Do not
    677 use HTTP session stickiness unless you really have to.) Enabling
    678 session stickiness hampers failover, balancing and performance:
    679 
    680 itemization(
    681     it() Failover is hampered because during the session,
    682          the balancer has to assign new connections to the same back
    683          end that was selected at the start of a session. If the back
    684          end suddenly goes 'down', then the session will most likely
    685          crash. (Actually, when a back end becomes unreachable in the
    686          middle of a session, Crossroads will assign a new back end to
    687          that session. This will most likely result in a malfunction
    688          of the underlying application.)
    689     it() Balancing is hampered because at the start of the session,
    690          the balancer has selected the next-best back end. But during
    691          the session, that back end may well become overloaded. The
    692          balancer however must continue to send the requests there.
    693     it() Performance is hampered because crossroads needs to 'unpack'
    694          messages as they are passed to and fro. That's because
    695          crossroads needs to check the HTTP headers in the messages
    696          for persistence cookies.)
    697 
    698 There is a number of measures that you can take to avoid using session
    699 stickiness. E.g., session data can be 'shared' between web back
    700 ends. PHP offers functionality to store session data in a database, so
    701 that all PHP applications have access to these data. Application
    702 servers such as Websphere can be configured to replicate session data
    703 between nodes.
    704 
    705 subsubsect(But if you must..)
    706 
    707 However, if you bf(must) use session stickiness, then proceed as
    708 follows:
    709 
    710 itemization(
    711     it() At the level of a tt(service) description, set the type to
    712          tt(http). 
    713     it() At the level of each back end description, configure the
    714          tt(stickycookie) and a tt(addclientheader) directives.)
    715 
    716 Once crossroads sees that, it will examine each HTTP message that it
    717 shuttles between client and back end:
    718 
    719 itemization(
    720     it() If there is no persistence cookie in the HTTP headers of a
    721          client's request, then the message must be the first one and
    722          a new session should be established. 
    723          Crossroads selects an appropriate back
    724          end, sends the message to that back end, catches the reply,
    725          and inserts a tt(Set-Cookie) directive.
    726     it() If there is a persistence cookie in the HTTP headers of a
    727          client's request, then the request is part of an already
    728          established session. Crossroads analyzes the cookie and
    729          forwards the request to the appropriate back end.)
    730 
    731 Below is a short example of a configuration.
    732 
    733 verb(\
    734 service www {
    735     port 80;
    736     type http;
    737     revivinginterval 15;
    738     dispatchmode byconnections;
    739 
    740     backend one {
    741         server 10.1.1.100:80;
    742         stickycookie XRID=100;
    743         addclientheader "Set-Cookie: XRID=100; Path=/";
    744     }
    745 
    746     backend two {
    747         server 10.1.1.101:80;
    748         stickycookie XRID=101;
    749         addclientheader "Set-Cookie: XRID=101; Path=/";
    750     }
    751 })
    752 
    753 Note how the cookie names and values in the directives
    754 tt(stickycookie) and tt(addclientheader) match. That is obviously a
    755 prerequisite for stickiness.
    756 
    757 
    758 subsect(Passing the client's IP address)
    759 
    760 Since Crossroads just shuttles bytes to and fro, meta-information of
    761 network connections is lost. As far as the back ends are concerned,
    762 their connections originate at the Crossroads junction.
    763 For example, standard Apache access logs will show the IP address of
    764 Crossroads. 
    765 
    766 In order to compensate for this, Crossroads can insert a special
    767 header in HTTP connections, to inform the back end of the original
    768 client's IP address. In order to enable this, the Crossroads
    769 configuration must state the following:
    770 
    771 itemization(
    772     it() The service type must be tt(http), and not tt(any);
    773     it() In the back end definition, the following statement must
    774          occur: nl()
    775          tt(addserverheader "X-Real-IP: %r";) nl()
    776          You are of course free to choose the header name; the here
    777          used tt(X-Real-IP) is a common name for this purpose.)
    778 
    779 After this, HTTP traffic that arrives at the back ends has a new
    780 header: tt(X-Real-IP), holding the client's IP address.
    781 bf(Note that) once the type is set to tt(http), Crossroads'
    782 performance will be hampered -- all passing messages will have to be
    783 unpacked and analyzed.
    784 
    785 subsubsect(Sample Crossroads configuration)
    786 
    787 The below sample configuration shows two HTTP back ends that receive
    788 the client's IP address:
    789 
    790 verb(
    791 service www {
    792     port 80;
    793     type http;
    794     revivinginterval 5;
    795     dispatchmode roundrobin;
    796 
    797     backend one {
    798         server 10.1.1.100:80;
    799         addserverheader "X-Real-IP: %r";
    800     }
    801 
    802     backend two {
    803         server 10.1.1.200:80;
    804         addserverheader "X-Real-IP: %r";
    805     }
    806 })
    807 
    808 
    809 subsubsect(Sample Apache configuration)
    810 
    811 The method by which each back end analyzes the header tt(X-Real-IP)
    812 will obviously be different per server implementations. However, a
    813 common method with the Apache webserver is to log the client's IP
    814 address into the access log.
    815 
    816 Often this is accomplished using the log format tt(custom), defined as
    817 follows:
    818 
    819 verb(\
    820 LogFormat "%h %l %u %t %D \"%r\" %>s %b" common
    821 CustomLog logs/access_log common)
    822 
    823 The first line defines the format tt(common), with the remote host
    824 specified by tt(%h). The second line sends access information to a log
    825 file tt(logs/access_log), using the previously defined format
    826 tt(common).
    827 
    828 Furtunately, Apache's tt(LogFormat) allows one to log contents of
    829 headers. By replacing the tt(%h) with tt(%{X-Real-IP}i), the desired
    830 information is sent to the log. Therefore, normally you can simply
    831 redefine the tt(common) format to 
    832 
    833 verb(\
    834 LogFormat "%{X-Real-IP}i %l %u %t %D \"%r\" %>s %b" common)
    835 
    836 
    837 subsect(Debugging network traffic)
    838 
    839     Incase the traffic between
    840     client and backend
    841     must be debugged, the statement tt(trafficlog) em(filename) can
    842     be issued. This causes the traffic to be dumped in hexadecimal
    843     format to the stated filename.
    844 
    845     Traffic sent by the client is prefixed by a bf(C), traffic sent by
    846     the back end is prefixed by a bf(B). Below is a sample traffic
    847     dump of a browser trying to get a HTML page. The server replies
    848     that the page was not modified.
    849 
    850     verb(\
    851 C 0000  47 45 54 20 68 74 74 70 3a 2f 2f 77 77 77 2e 63 GET http://www.c
    852 C 0010  73 2e 68 65 6c 73 69 6e 6b 69 2e 66 69 2f 6c 69 s.helsinki.fi/li
    853 C 0020  6e 75 78 2f 6c 69 6e 75 78 2d 6b 65 72 6e 65 6c nux/linux-kernel
    854 C 0030  2f 32 30 30 31 2d 34 37 2f 30 34 31 37 2e 68 74 /2001-47/0417.ht
    855 C 0040  6d 6c 20 48 54 54 50 2f 31 2e 31 0d 0a 43 6f 6e ml HTTP/1.1..Con
    856 C 0050  6e 65 63 74 69 6f 6e 3a 20 63 6c 6f 73 65 0d 0a nection: close..
    857 .
    858 . etcetera
    859 .
    860 B 0000  48 54 54 50 2f 31 2e 30 20 33 30 34 20 4e 6f 74 HTTP/1.0 304 Not
    861 B 0010  20 4d 6f 64 69 66 69 65 64 0d 0a 44 61 74 65 3a  Modified..Date:
    862 B 0020  20 54 75 65 2c 20 31 32 20 4a 75 6c 20 32 30 30  Tue, 12 Jul 200
    863 B 0030  35 20 30 39 3a 34 39 3a 34 37 20 47 4d 54 0d 0a 5 09:49:47 GMT..
    864 B 0040  43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 74 65 Content-Type: te
    865 B 0050  78 74 2f 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 xt/html; charset
    866 .
    867 . etcetera
    868 .)
    869 
    870     Turning on traffic dumps will em(significantly)
    871     slow down crossroads.
    872 
    873     Besides tt(trafficlog), there is also a directive
    874     tt(throughputlog). This directive also takes one argument, a
    875     filename. The file is appended, and the following information is
    876     logged:
    877 
    878     itemization(
    879         it() The process ID of the crossroads image that serves the
    880         TCP connection;
    881         it() The time of the request, in seconds and microseconds
    882         since start of the run;
    883         it() A bf(C) when the request originated at the client, or
    884         bf(B) when the request originated at the back end;
    885         it() The first 100 bytes of the request.)
    886 
    887     As an example, consider the following (the lines are shortened for
    888     brevity and prefixed by line numbers for clarity):
    889 
    890     verb(
    891 1 0000594 0.000001 C GET http://public.e-tunity.com/index.html...
    892 2 0000594 0.173713 B HTTP/1.0 200 OK..Date: Fri, 18 Nov 2005 0...
    893 3 0000594 0.278125 B  width="100" bgcolor="#e0e0e0" valign="to...
    894 4 0000595 0.000001 C GET http://public.e-tunity.com/css/style/...
    895 5 0000594 0.944339 B /a></td>..  </tr>.</table>.</td><td class...
    896 6 0000594 0.946356 B smallboxdownl">Download</td>..  <td class...
    897 7 0000594 0.961102 B td><td class="smallboxodd" valign="top"><...
    898 8 0000595 0.698215 B HTTP/1.0 304 Not Modified..Date: Fri, 18 ...)
    899 
    900     This tells us that:
    901 
    902     itemization(
    903         it() Line 1:  PID 594 served a request that originated at
    904              the client. The corresponding time is (almost) 0 seconds,
    905              so this is really the start of the run.
    906         it() Line 2: A back end replied 0.17 seconds later, and
    907              0.28 seconds later, it was still replying (this is the
    908              third line, again a bf(B)-type transmission).
    909         it() Line 4: PID 595 served a request that originated
    910              at the client. Again, the corresponding time is (almost)
    911              0 seconds, since this is the first conversation part of
    912              this connection.
    913         it() Lines 5 to 7: This is the continuation of line 2. Line 7
    914              is the last line of the bf(B) series (not visible from
    915              the example, but trust me, it is), so that we may
    916              conclude that it took the back end 0.96 seconds to serve
    917              the file tt(index.html) requested in line 1.
    918         it() Line 8: This is the answer to the client's request of
    919              line 4 (you can tell by the process ID  number).
    920              So the back end took 0.68 seconds to confirm that
    921              the stylesheet requested in line 4 wasn't modified.)
    922 
    923     It is also worth while remembering that the start time of a bf(C)
    924     request is the time that crossroads sees the activity. Any latency
    925     between the true client and crossroads is obviously not
    926     included. This is illustrated by the below simple ASCII art:
    927 
    928     verb(
    929 client ---->---->---->--->*crossroads ====>====>====>
    930                                                      \    
    931                                                   back end
    932                                                      /
    933 client ----<----<----<---< crossroads ====<====<====<
    934 )
    935 
    936     This simple picture shows a typical HTTP request that originates
    937     at a client, travels to crossroads, and is relayed via the back
    938     end. The bf(C) entry in a throughput log is the time when
    939     crossroads sees the request, indicated by an asterisk. The bf(B)
    940     entries are the times that it takes the back end to answer,
    941     indicated by tt(===) style lines. Therefore, the true roundtrip
    942     time will be longer than the number of seconds that are logged in
    943     the throughput log: the latency between client and crossroads
    944     isn't included in that measurement.
    945     
    946     Summarizing, the throughput times of a client-back end connection
    947     can be analyzed using the directive tt(throughputlog). In a
    948     real-world analysis, you'd probably want to write up a script to
    949     analyze the output and to compute round trip times. Such scripts
    950     are not (yet) included in Crossroads.
    951 
    952 subsect(Limiting Access to Crossroads by Client IP Address)
    953 
    954 subsubsect(General Examples)
    955 
    956 The directives tt(allowfrom), tt(denyfrom), tt(allowfile) and
    957 tt(denyfile) can be used to instruct Crossroads to specifically allow
    958 access by using a "whitelist" of IP addresses, or to specifically deny
    959 access by using a "blacklist". E.g., the following configuration
    960 allows access to service tt(webproxy) only to em(localhost):
    961 
    962 verb(\
    963 service webproxy {
    964     port 8000;    
    965     allowfrom 127.0.0.1;
    966     backend one {
    967         .
    968         . Back end definitions occur here
    969         .
    970     }
    971     .
    972     . Other back ends or other service directives
    973     . may occur here
    974     .
    975 })
    976 
    977 In this example there is a "whitelist" having only one entry: IP
    978 address 127.0.0.1, or em(localhost). (Incidentally, the same behaviour
    979 could be accomplished by stating em(bindto 127.0.0.1), in which case
    980 Crossroads would only listen to the local network device.)
    981 
    982 In the same vein, the directive tt(allowfrom 127.0.0.1 192.168.1/24)
    983 would allow access to em(localhost) and to all IP addresses that start
    984 with 192.168.1. The specifier tt(192.168.1/24) states that there are
    985 three network bytes (192, 168 and 1), and 24 bits (or 3 bytes) are
    986 relevant; so that the fourth network byte doesn't matter.
    987 
    988 subsubsect(Using External Files)
    989 
    990 The directives tt(allowfile) and tt(denyfile) allow you to specify IP
    991 addresses in external files. The Crossroads configuration states
    992 e.g. tt(allowfile /tmp/allow.txt), and the IP addresses are then in
    993 tt(/tmp/allow.txt). The format of tt(/tmp/allow.txt) is as follows:
    994 
    995 itemization(
    996         it() The specifications follow again em(p.q.r.s/mask), where
    997         p, q, r and s are network bytes which can be left out on the
    998         right hand side when the mask allows it;
    999 
   1000         it() The specifications must be separated by white space
   1001         (spaces, tabs or newlines).)
   1002 
   1003 E.g., the following is a valid example of an external specification
   1004 file:
   1005 
   1006 verb(\
   1007 127.0.0.1
   1008 192.168.1/24
   1009 10/8)
   1010 
   1011 When external files are in effect, then the signal tt(SIGHUP) (1)
   1012 causes Crossroads to reload the external file. E.g., while Crossroads
   1013 is running, you may edit tt(/tmp/allow.txt), and then issue tt(killall
   1014 -1 crossroads). The new contents of tt(/tmp/allow.txt) will be
   1015 reloaded.
   1016 
   1017 subsubsect(Mixing Directives)
   1018 
   1019 Crossroads allows to mix all directives in one service
   1020 description. However, some mixes are less meaningful than others. It's
   1021 up to you to take this into account.
   1022 
   1023 The following rules apply:
   1024 
   1025 itemization(
   1026         it() Blacklisting and whitelisting can be used together. When
   1027         combined, the blacklist will always be interpreted
   1028         first. E.g., consider the following directives:
   1029 
   1030         verb(\
   1031 allowfrom 192.168.1/24
   1032 denyfrom  192.168.1.100)
   1033 
   1034         Given the fact that the deny list is checked first, client
   1035         192.168.1.100 won't be able to access Crossroads. Then the
   1036         allow list will be checked, stating that all clients whose IP
   1037         address starts with 192.168.1 may connect. The effect will be
   1038         that e.g., client 192.168.1.1 may connect, 192.168.1.2 may
   1039         connect too, 192.168.1.100 will be blocked, and 10.1.1.1 will
   1040         be blocked as well.
   1041 
   1042         Now consider the following directives:
   1043 
   1044         verb(\
   1045 allowfrom 192.168.1.100 127.0.0.1
   1046 denyfrom  192.168.1/24)
   1047 
   1048         This will first of all deny access to all IP addresses that
   1049         start with 192.168.1. So the rule that allows 192.168.1.100
   1050         won't ever be effective. The net result will be that access
   1051         will be granted to 127.0.0.1, and IP addresses that don't
   1052         match 192.168.1/24.
   1053 
   1054         it() Blacklisting or whitelisting can be left out.
   1055         A list is considered empty when no appropriate directives
   1056         occur in tt(/etc/crossroads.conf), or when the directive
   1057         points to an empty or non-existent external file.
   1058         
   1059         it() Using tt(*from) and tt(*file) statements is allowed, but
   1060         doesn't make sense. E.g., the following configuration sample
   1061         is such a case:
   1062 
   1063         verb(\
   1064 allowfrom 127.0.0.1 192.168.1/24
   1065 allowfile /tmp/allow.txt)
   1066 
   1067         There is a technical reason for this. Once Crossroads
   1068         processes the tt(allowfile) directive, then the whole
   1069         whitelist is cleared (thereby removing the entries 127.0.0.1
   1070         and 192.168.1/24), and new entries are reloaded from the
   1071         file. The net result is that the tt(allowfrom) specification
   1072         is overruled.
   1073 
   1074         Crossroads doesn't check for such configurations, which are
   1075         syntactially correct, but make no semantic sense.)
   1076         	
   1077 
   1078 subsect(Configuration examples)
   1079 
   1080 As a general hint, use tt(crossroads sampleconf) to view the most
   1081 up-to-date examples of configurations. The description below shows a
   1082 few examples too.
   1083 
   1084 
   1085 subsubsect(A load balancer for three webserver back ends)
   1086 
   1087 The following configuration example binds crossroads to port 80 of the
   1088 current server, and distributes the load over three back ends. This
   1089 configuration shows most of the possible settings.
   1090 
   1091 verb(\
   1092 service www {
   1093     /* We don't need session stickyness. */
   1094     type any;
   1095     
   1096     /* Port on which we'll listen in this service: required. */
   1097     port 8000;
   1098 
   1099     /* What IP address should this service listen? Default is 'any'.
   1100      * Alternatively you can state an explicit IP address, such as
   1101      * 127.0.0.1; that would bind the service only to 'localhost'. */
   1102     bindto any;
   1103     
   1104     /* Verbose reporting or not. Default is off. */    
   1105     verbosity on;
   1106     
   1107     /* Dispatching mode, or: How to select a back end for an incoming
   1108      * request. Possible values:
   1109      *   roundrobin: just the next back end in line
   1110      *   random: like roundrobin, but at random to make things more
   1111      *          confusing. Probably only good for testing.
   1112      *   bysize: The backend that transferred the least nr of bytes
   1113      *          is the next in line. As a modifier you can say e.g.
   1114      *          bysize over 10, meaning that the 10 last connections will
   1115      *          be used to compute the transfer size, instead of all
   1116      *          transfers.
   1117      *   byduration: The backend that was active for the shortest time
   1118      *          is the next in line. As a modifier you can say e.g.
   1119      *          byduration of 10 to compute over the last 10 connections.
   1120      *   byconnections: The back end with the least active connections
   1121      *          is the next ine line.
   1122      *   byorder: The first available back end is always taken.
   1123      */
   1124     dispatchmode byduration over 5;
   1125 
   1126     /* Interval at which we'll check whether a temporarily unavailable
   1127      * backend has woken up.
   1128      */
   1129     revivinginterval 5;
   1130     
   1131     /* TCP backlog of connections. Default is 0 (no backlog, one
   1132      * connection may be active).
   1133      */
   1134     backlog 5;
   1135     
   1136     /* For status reporting: a shared memory key. Default is the same
   1137      * as the port number, OR-ed by a magic number.
   1138      */
   1139     shmkey 8000;
   1140 
   1141     /* This controls when crossroads should consider a connection as
   1142      * finished even when the TCP sockets weren't closed. This is to
   1143      * avoid hanging connections that don't do anything. NOTE THAT when
   1144      * crossroads cuts off a connection due to timeout exceed, this is
   1145      * not marked as a failure, but as a success. Default is 0: no timeout.
   1146      */
   1147     connectiontimeout 300;
   1148 
   1149     /* The max number of allowed client connections. When present, connections
   1150      * won't be accepted if the max is about to be exceeded. When
   1151      * absent, all connections will be accepted, which might be misused
   1152      * for a DOS attack.
   1153      */
   1154     maxconnections 300;
   1155 
   1156     /* Now let's define a couple of back ends. Number 1: */
   1157     backend www_backend_1 {
   1158         /* The server and its port, the minimum configuration. */
   1159         server httpserver1;
   1160         port 9010;
   1161         /* The 'decay' of usage data of this back end. Only relevant
   1162          * when the whole service has 'dispatchmode bysize' or
   1163          * 'byduration'. The number is a percentage by which the usage
   1164          * parameter is decreased upon each connection of an other back
   1165          * end.
   1166          */
   1167         decay 10;
   1168         
   1169         /* To see what's happening in /var/log/messages: */
   1170         verbosity on;
   1171     }
   1172 
   1173     /* The second one: */
   1174     backend www_backend_2 {
   1175         /* Server and port */
   1176         server httpserver2;
   1177         port 9011;
   1178 
   1179         /* Verbosity of reporting when this back end is active */
   1180         verbosity on;
   1181 
   1182         /* Decay */
   1183         decay 10;
   1184 
   1185         /* This back end is twice as weak as the first one */
   1186         weight 2;
   1187 
   1188         /* Event triggers for system commands upon succesful activation
   1189          * and upon failure.
   1190          */
   1191         onsuccess echo 'success on backend 2' | mail root;
   1192         onfailure echo 'failure on backend 2' | mail root;
   1193     }
   1194 
   1195     /* And yet another one.. this time we will dump the traffic
   1196      * to a trace file. Furthermore we don't want more than 10 concurrent
   1197      * connections here. Note that there's also a total maxconnections for the
   1198      * whole service.
   1199      */
   1200     backend www_backend_3 {
   1201         server httpserver3;
   1202         verbosity on;
   1203         port 9000;
   1204         verbosity on;
   1205         decay 10;
   1206         trafficlog /tmp/backend.3.log;
   1207         maxconnections 10;
   1208     }
   1209 })
   1210 
   1211 subsubsect(An HTTP forwarder when travelling)
   1212 
   1213 As another example, here's my tt(crossroads.conf) that I use on my
   1214 Unix laptop. The problem that I face is that I need many HTTP proxy
   1215 configurations (at home, at customers' sites and so on) but I'm too
   1216 lazy to reconfigure browsers all the time.
   1217 
   1218 Here's how it used to be before crossroads:
   1219 
   1220 itemization(
   1221         it() At home, I would surf through a squid proxy on my local
   1222         machine. The browser proxy setting is then
   1223         tt(http://localhost:3128).
   1224 
   1225         it() Sometimes I start up an SSH tunnel to our offices. The
   1226         tunnel has a local port 3129, and connects to a squid proxy on
   1227         our e-tunity server. Hence, the browser proxy is then
   1228         tt(http://localhost:3129).
   1229 
   1230         it() At a customer's location I need the proxy
   1231         tt(http://10.120.34.113:8080), because they have configured it
   1232         so.
   1233 
   1234         it() And in yet other instances, I use a HTTP diagnostic tool
   1235         url(Charles)(http://www.xk72.com/charles)
   1236         that sits between browser and website and shows me
   1237         what's happening. I run charles on my own machine and it
   1238         listens to port 8888, behaving like a proxy. The browser
   1239         configuration for the proxy is then
   1240         tt(http://localhost:8888).)
   1241 
   1242 Here's how it works with a crossroads configuration:    
   1243 
   1244 itemization(
   1245         it() I have configured my browsers to use
   1246         tt(http://localhost:8080) as the proxy. For all situations.
   1247         
   1248         it() I use the following crossroads configuration, and let
   1249         crossroads figure out which proxy backend works, and which
   1250         doesn't. Note two particularities:
   1251 
   1252         itemization(
   1253                 it() The statement tt(dispatchmode byorder). This
   1254                 makes sure that once crossroads determines which
   1255                 backend works, it will stick to it. This usage of
   1256                 crossroads doesn't need to balance over more than one
   1257                 back end.
   1258 
   1259                 it() The statement tt(bindto 127.0.0.1) makes sure
   1260                 that requests from other interfaces than loopback
   1261                 won't get serviced.)
   1262 
   1263 verb(\
   1264 service HttpProxy {
   1265     port 8080;
   1266     bindto 127.0.0.1;
   1267     verbosity on;
   1268     dispatchmode byorder;
   1269     revivinginterval 15;
   1270 
   1271     backend Charles {
   1272         server localhost:8888;
   1273         verbosity on;
   1274     }
   1275 
   1276     backend CustomerProxy {
   1277         server 10.120.34.113:8080;
   1278         verbosity on;
   1279     }
   1280 
   1281     backend SshTunnel {
   1282         server localhost:3129;
   1283     }
   1284 
   1285     backend LocalSquid {
   1286         server localhost:3128;
   1287     }
   1288 }))
   1289 
   1290 As a final note, the commandline argument tt(tell) can be used to
   1291 influence crossroad's own detection mechanism of back end availability
   1292 detection. E.g., if in the above example the back ends tt(SshTunnel)
   1293 and tt(LocalSquid) are both active, then tt(crossroads tell httpproxy
   1294 sshtunnel down) will 'take down' the back end tt(SshTunnel) -- and
   1295 will automatically cause crossroads to switch to tt(LocalSquid).
   1296 
   1297 
   1298 subsubsect(SSH login with enforced idle logout)
   1299 
   1300 The following example shows how crossroads 'throttles' SSH
   1301 logins. Connections are accepted on port
   1302 22 (the normal SSH port) and forwarded to the actual SSH daemon
   1303 which is running on port 2222.
   1304 
   1305 Note the usage of the
   1306 tt(connectiontimeout) directive. This makes sure that users are logged
   1307 out after 10 minutes of inactivity. Note also the tt(maxconnections)
   1308 setting, this makes sure that no more than 10 concurrent logins occur.
   1309 
   1310 verb(\
   1311 service Ssh {
   1312     port 22;
   1313     backlog 5;
   1314     maxconnections 10;
   1315     connectiontimeout 600;
   1316     backend TrueSshDaemon {
   1317         server localhost:2222;
   1318     }
   1319 })