Jim Jagielski wrote:
If we state that the evaluation takes place in the occurence of
stickysession attributes
and suggest
stickysession=Cookie:JSESSIONID stickysession=Path:;jsessionid to
the user
it will perform faster in average.
As I promised to write the patch, I would do it the way Jim suggested
it for backward
compatibility, however I would also add a table "sticky_type"
containing the "Cookie vs.
Path vs. Env"-prefix (in a parsed way) to the balancer struct.
Is this OK for everybody?
Patch for trunk...
Hi,
attached is the patch for trunk with documentation & Co.
Could anybody review it & commit?
Cheers,
Georg
Index: docs/manual/mod/mod_proxy.xml
===================================================================
--- docs/manual/mod/mod_proxy.xml (revision 526077)
+++ docs/manual/mod/mod_proxy.xml (working copy)
@@ -707,9 +707,19 @@
</td></tr>
<tr><td>stickysession</td>
<td>-</td>
- <td>Balancer sticky session name. The value is usually set to something
- like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>,
- and it depends on the backend application server that support sessions.
+ <td>Balancer sticky session name. Multiple definitions are possible and
+ are interpreted in the sequence of their definition. The
+ value can be preceeded with a specification <code>Cookie</code>,
+ <code>Path</code> or <code>Env</code> to restrict the search.
+ Without this specification, the identifier is searched in the URL path
+ and then in the cookie. The value is usually set to something
+ like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>, e.g.
+ <br/><indent>
+ <code>stickysession=Cookie:JSESSIONID
+ stickysession=Path:;jsessionid</code>
+ </indent>
+ for Java Servlets and depends on the backend application server
+ that support sessions.
</td></tr>
<tr><td>timeout</td>
<td>0</td>
@@ -721,7 +731,7 @@
<p>A sample balancer setup</p>
<example>
ProxyPass /special-area http://special.example.com/ smax=5 max=10<br />
- ProxyPass / balancer://mycluster/ stickysession=jsessionid
nofailover=On<br />
+ ProxyPass / balancer://mycluster/ stickysession=Cookie:JSESSIONID
stickysession=Path:;jsessionid nofailover=On<br />
<Proxy balancer://mycluster><br />
<indent>
BalancerMember http://1.2.3.4:8009<br />
Index: docs/manual/mod/mod_proxy.html.en
===================================================================
--- docs/manual/mod/mod_proxy.html.en (revision 526077)
+++ docs/manual/mod/mod_proxy.html.en (working copy)
@@ -892,9 +892,19 @@
</td></tr>
<tr><td>stickysession</td>
<td>-</td>
- <td>Balancer sticky session name. The value is usually set to something
- like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>,
- and it depends on the backend application server that support sessions.
+ <td>Balancer sticky session name. Multiple definitions are possible and
+ are interpreted in the sequence of their definition. The
+ value can be preceeded with a specification <code>Cookie</code>,
+ <code>Path</code> or <code>Env</code> to restrict the search.
+ Without this specification, the identifier is searched in the URL path
+ and then in the cookie. The value is usually set to something
+ like <code>JSESSIONID</code> or <code>PHPSESSIONID</code>, e.g.
+ <br /><span class="indent">
+ <code>stickysession=Cookie:JSESSIONID
+ stickysession=Path:;jsessionid</code>
+ </span>
+ for Java Servlets and depends on the backend application server
+ that support sessions.
</td></tr>
<tr><td>timeout</td>
<td>0</td>
@@ -906,7 +916,7 @@
<p>A sample balancer setup</p>
<div class="example"><p><code>
ProxyPass /special-area http://special.example.com/ smax=5 max=10<br />
- ProxyPass / balancer://mycluster/ stickysession=jsessionid
nofailover=On<br />
+ ProxyPass / balancer://mycluster/ stickysession=Cookie:JSESSIONID
stickysession=Path:;jsessionid nofailover=On<br />
<Proxy balancer://mycluster><br />
<span class="indent">
BalancerMember http://1.2.3.4:8009<br />
Index: docs/manual/new_features_2_4.xml
===================================================================
--- docs/manual/new_features_2_4.xml (revision 526077)
+++ docs/manual/new_features_2_4.xml (working copy)
@@ -39,8 +39,12 @@
<section id="module">
<title>Module Enhancements</title>
- <!-- <dl>
- </dl> -->
+ <dl>
+ <dt><module>mod_proxy</module></dt>
+ <dd>Extension of the stickysession attribute in ProxyPass to allow
+ multiple definitions and to specify a prefix to the value like
+ 'Path', 'Cookie' or 'Env' to limit the search.</dd>
+ </dl>
</section>
<section id="programs">
Index: docs/manual/new_features_2_4.html.en
===================================================================
--- docs/manual/new_features_2_4.html.en (revision 526077)
+++ docs/manual/new_features_2_4.html.en (working copy)
@@ -40,7 +40,12 @@
<div class="section">
<h2><a name="module" id="module">Module Enhancements</a></h2>
-
+ <dl>
+ <dt><code class="module"><a
href="./mod/mod_proxy.html">mod_proxy</a></code></dt>
+ <dd>Extension of the stickysession attribute in ProxyPass to allow
+ multiple definitions and to specify a prefix to the value like
+ 'Path', 'Cookie' or 'Env' to limit the search.</dd>
+ </dl>
</div><div class="top"><a href="#page-header"><img alt="top"
src="./images/up.gif" /></a></div>
<div class="section">
<h2><a name="programs" id="programs">Program Enhancements</a></h2>
Index: CHANGES
===================================================================
--- CHANGES (revision 526077)
+++ CHANGES (working copy)
@@ -2,6 +2,10 @@
Changes with Apache 2.3.0
[Remove entries to the current 2.0 and 2.2 section below, when backported]
+ *) mod_proxy: Extension of the stickysession attribute in ProxyPass to allow
+ multiple definitions and to specify a prefix to the value like
+ 'Path', 'Cookie' or 'Env' to limit the search. [Georg v. Zezschwitz]
+
*) mod_ssl: initialize thread locks before initializing the hardware
acceleration library, so the latter can make use of the former.
PR 20951. [adunn at ncipher.com, reviewed by Sander Temme]
Index: modules/proxy/mod_proxy_balancer.c
===================================================================
--- modules/proxy/mod_proxy_balancer.c (revision 526077)
+++ modules/proxy/mod_proxy_balancer.c (working copy)
@@ -241,19 +241,42 @@
static proxy_worker *find_session_route(proxy_balancer *balancer,
request_rec *r,
char **route,
- char **url)
+ char **url,
+ struct proxy_sticky_entry
**sticky_entry)
{
proxy_worker *worker = NULL;
+ struct proxy_sticky_entry *entry;
+ int i;
if (!balancer->sticky)
return NULL;
- /* Try to find the sticky route inside url */
- *route = get_path_param(r->pool, *url, balancer->sticky);
- if (!*route)
- *route = get_cookie_param(r, balancer->sticky);
- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+
+ entry = (struct proxy_sticky_entry*) balancer->sticky->elts;
+ *route = NULL;
+ for (i = balancer->sticky->nelts; i>0; i--, entry++) {
+ if (entry->method & PROXY_STICKY_PATH) {
+ /* Try to find the sticky route inside url */
+ *route = get_path_param(r->pool, *url, entry->name);
+ }
+ if (!*route && (entry->method & PROXY_STICKY_COOKIE)) {
+ /* Try to find the sticky route by cookie */
+ *route = get_cookie_param(r, entry->name);
+ }
+ if (!*route && (entry->method & PROXY_STICKY_ENV)) {
+ /* Try to find the sticky route by environment */
+ const char *troute = apr_table_get(r->subprocess_env, entry->name);
+ if (troute)
+ *route = apr_pstrdup(r->pool, troute);
+ }
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
"proxy: BALANCER: Found value %s for "
- "stickysession %s", *route, balancer->sticky);
+ "stickysession %s", *route, entry->name);
+ if (*route) {
+ *sticky_entry = entry;
+ break;
+ }
+ }
+
/*
* If we found a value for sticksession, find the first '.' within.
* Everything after '.' (if present) is our route.
@@ -404,6 +427,7 @@
proxy_worker *runtime;
char *route = NULL;
apr_status_t rv;
+ struct proxy_sticky_entry *sticky_entry = NULL;
*worker = NULL;
/* Step 1: check if the url is for us
@@ -429,7 +453,7 @@
force_recovery(*balancer, r->server);
/* Step 4: find the session route */
- runtime = find_session_route(*balancer, r, &route, url);
+ runtime = find_session_route(*balancer, r, &route, url, &sticky_entry);
if (runtime) {
int i, total_factor = 0;
proxy_worker *workers;
@@ -513,12 +537,12 @@
access_status = rewrite_url(r, *worker, url);
/* Add the session route to request notes if present */
if (route) {
- apr_table_setn(r->notes, "session-sticky", (*balancer)->sticky);
+ apr_table_setn(r->notes, "session-sticky", sticky_entry->name);
apr_table_setn(r->notes, "session-route", route);
/* Add session info to env. */
apr_table_setn(r->subprocess_env,
- "BALANCER_SESSION_STICKY", (*balancer)->sticky);
+ "BALANCER_SESSION_STICKY", sticky_entry->name);
apr_table_setn(r->subprocess_env,
"BALANCER_SESSION_ROUTE", route);
}
@@ -581,6 +605,30 @@
}
}
+static void print_balancer_sticky(request_rec *r,
+ apr_array_header_t *sticky)
+{
+ int i;
+ struct proxy_sticky_entry *entries;
+
+ if (sticky==NULL)
+ return;
+ entries = (struct proxy_sticky_entry *) sticky->elts;
+ for (i = 0; i < sticky->nelts; i++) {
+ if (i>0)
+ ap_rputs (" ", r);
+ switch (entries[i].method) {
+ case PROXY_STICKY_COOKIE:
+ ap_rputs ("Cookie:", r); break;
+ case PROXY_STICKY_PATH:
+ ap_rputs ("Path:", r); break;
+ case PROXY_STICKY_ENV:
+ ap_rputs ("Env:", r); break;
+ }
+ ap_rputs (entries[i].name, r);
+ }
+}
+
/* Manages the loadfactors and member status
*/
static int balancer_handler(request_rec *r)
@@ -645,10 +693,30 @@
if (bsel) {
const char *val;
if ((val = apr_table_get(params, "ss"))) {
- if (strlen(val))
- bsel->sticky = apr_pstrdup(conf->pool, val);
- else
- bsel->sticky = NULL;
+ char *tok_cntx;
+ bsel->sticky = apr_array_make(conf->pool, 1,
+ sizeof (struct proxy_sticky_entry));
+ char *v = apr_strtok(apr_pstrdup(r->pool, val), "+", &tok_cntx);
+ while (v!=NULL) {
+ struct proxy_sticky_entry * entry =
+ (struct proxy_sticky_entry *) apr_array_push(bsel->sticky);
+ const char *colon_pos = strchr (v, ':');
+ entry->method = PROXY_STICKY_COOKIE | PROXY_STICKY_PATH;
+ if (colon_pos!=NULL) {
+ while (*++colon_pos==' ')
+ continue;
+ entry->name = apr_pstrdup(conf->pool, colon_pos);
+ if (!strncasecmp ("cookie:", v, 7))
+ entry->method = PROXY_STICKY_COOKIE;
+ else if (!strncasecmp ("path:", v, 5))
+ entry->method = PROXY_STICKY_PATH;
+ else if (!strncasecmp ("env:", v, 4))
+ entry->method = PROXY_STICKY_ENV;
+ } else {
+ entry->name = apr_pstrdup(conf->pool, v);
+ }
+ v = apr_strtok(NULL, "+", &tok_cntx);
+ }
}
if ((val = apr_table_get(params, "tm"))) {
int ival = atoi(val);
@@ -753,9 +821,9 @@
"\">", NULL);
ap_rvputs(r, balancer->name, "</a></h3>\n\n", NULL);
ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>"
-
"<th>StickySession</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>"
- "</tr>\n<tr>", r);
- ap_rvputs(r, "<td>", balancer->sticky, NULL);
+ "<th>StickySession</th><th>Timeout</th><th>FailoverAttempts"
+ "</th><th>Method</th></tr>\n<tr><td>", r);
+ print_balancer_sticky (r, balancer->sticky);
ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
apr_time_sec(balancer->timeout));
ap_rprintf(r, "<td>%d</td>\n", balancer->max_attempts);
@@ -841,10 +909,10 @@
ap_rvputs(r, bsel->name, "</h3>\n", NULL);
ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
ap_rvputs(r, r->uri, "\">\n<dl>", NULL);
- ap_rputs("<table><tr><td>StickySession Identifier:</td><td><input
name=\"ss\" type=text ", r);
- if (bsel->sticky)
- ap_rvputs(r, "value=\"", bsel->sticky, "\"", NULL);
- ap_rputs("></td><tr>\n<tr><td>Timeout:</td><td><input name=\"tm\"
type=text ", r);
+ ap_rputs("<table>", r);
+ ap_rputs("<tr><td>StickySession:</td><td><input name=\"ss\"
type=text value=\"", r);
+ print_balancer_sticky (r, bsel->sticky);
+ ap_rputs("\"></td></tr><tr><td>Timeout:</td><td><input name=\"tm\"
type=text ", r);
ap_rprintf(r, "value=\"%" APR_TIME_T_FMT "\"></td></tr>\n",
apr_time_sec(bsel->timeout));
ap_rputs("<tr><td>Failover Attempts:</td><td><input name=\"fa\"
type=text ", r);
Index: modules/proxy/mod_proxy.c
===================================================================
--- modules/proxy/mod_proxy.c (revision 526077)
+++ modules/proxy/mod_proxy.c (working copy)
@@ -272,14 +272,36 @@
const char *key,
const char *val)
{
-
int ival;
if (!strcasecmp(key, "stickysession")) {
- /* Balancer sticky session name.
- * Set to something like JSESSIONID or
- * PHPSESSIONID, etc..,
- */
- balancer->sticky = apr_pstrdup(p, val);
+ /* Balancer sticky session name.
+ * Set to something like JSESSIONID or
+ * PHPSESSIONID, etc..,
+ */
+ struct proxy_sticky_entry *entry;
+
+ if (balancer->sticky==NULL)
+ balancer->sticky =
+ apr_array_make(p, 1, sizeof (struct proxy_sticky_entry));
+ entry = (struct proxy_sticky_entry *) apr_array_push(balancer->sticky);
+ const char *colon_pos = strchr (val, ':');
+ if (colon_pos!=NULL) {
+ while (*++colon_pos==' ')
+ continue;
+ entry->name = apr_pstrdup(p, colon_pos);
+ if (!strncasecmp ("cookie:", val, 7))
+ entry->method = PROXY_STICKY_COOKIE;
+ else if (!strncasecmp ("path:", val, 5))
+ entry->method = PROXY_STICKY_PATH;
+ else if (!strncasecmp ("env:", val, 4))
+ entry->method = PROXY_STICKY_ENV;
+ else
+ return "stickysession must be preceeded by Cookie|Path|Env "
+ "followed by a colon";
+ } else {
+ entry->method = PROXY_STICKY_COOKIE | PROXY_STICKY_PATH;
+ entry->name = apr_pstrdup(p, val);
+ }
}
else if (!strcasecmp(key, "nofailover")) {
/* If set to 'on' the session will break
@@ -324,7 +346,7 @@
return "unknown lbmethod";
}
else {
- return "unknown Balancer parameter";
+ return apr_pstrcat (p, "unknown Balancer parameter ", key);
}
return NULL;
}
@@ -1121,9 +1143,8 @@
char *f = cmd->path;
char *r = NULL;
char *word;
- apr_table_t *params = apr_table_make(cmd->pool, 5);
- const apr_array_header_t *arr;
- const apr_table_entry_t *elts;
+ apr_array_header_t *params = apr_array_make(cmd->pool, 10, sizeof(char*));
+ const char **elts;
int i;
while (*arg) {
@@ -1152,7 +1173,8 @@
}
else
*val++ = '\0';
- apr_table_setn(params, word, val);
+ * (const char**) (apr_array_push (params)) = word;
+ * (const char**) (apr_array_push (params)) = val;
}
};
@@ -1165,8 +1187,7 @@
if (r[0] == '!' && r[1] == '\0')
return NULL;
- arr = apr_table_elts(params);
- elts = (const apr_table_entry_t *)arr->elts;
+ elts = (const char **) params->elts;
/* Distinguish the balancer from worker */
if (strncasecmp(r, "balancer:", 9) == 0) {
proxy_balancer *balancer = ap_proxy_get_balancer(cmd->pool, conf, r);
@@ -1177,9 +1198,20 @@
if (err)
return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
}
- for (i = 0; i < arr->nelts; i++) {
- const char *err = set_balancer_param(conf, cmd->pool, balancer,
elts[i].key,
- elts[i].val);
+ else {
+ if (params->nelts > 0 &&
+ (balancer->sticky || balancer->sticky_force ||
+ balancer->max_attempts_set || balancer->timeout)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ "repeated parameter definition for balancer %s "
+ "overwrites previous definitions",
+ balancer->name);
+ balancer->sticky = NULL;
+ }
+ }
+ for (i = 0; i < params->nelts; i+=2) {
+ const char *err = set_balancer_param(conf, cmd->pool, balancer,
+ elts[i], elts[i+1]);
if (err)
return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
}
@@ -1196,9 +1228,9 @@
}
PROXY_COPY_CONF_PARAMS(worker, conf);
- for (i = 0; i < arr->nelts; i++) {
- const char *err = set_worker_param(cmd->pool, worker, elts[i].key,
- elts[i].val);
+ for (i = 0; i < params->nelts; i+=2) {
+ const char *err = set_worker_param(cmd->pool, worker,
+ elts[i], elts[i+1]);
if (err)
return apr_pstrcat(cmd->temp_pool, "ProxyPass ", err, NULL);
}
@@ -1975,7 +2007,7 @@
*/
static int proxy_status_hook(request_rec *r, int flags)
{
- int i, n;
+ int i, j, n;
void *sconf = r->server->module_config;
proxy_server_conf *conf = (proxy_server_conf *)
ap_get_module_config(sconf, &proxy_module);
@@ -1993,7 +2025,22 @@
ap_rputs("\n\n<table border=\"0\"><tr>"
"<th>SSes</th><th>Timeout</th><th>Method</th>"
"</tr>\n<tr>", r);
- ap_rvputs(r, "<td>", balancer->sticky, NULL);
+ ap_rputs("<td>", r);
+ if (balancer->sticky!=NULL) {
+ struct proxy_sticky_entry *sticky =
+ (struct proxy_sticky_entry*) balancer->sticky->elts;
+ for (j = 0; j < balancer->sticky->nelts; j++) {
+ switch (sticky[j].method) {
+ case PROXY_STICKY_COOKIE:
+ ap_rputs ("Cookie:", r); break;
+ case PROXY_STICKY_PATH:
+ ap_rputs ("Path:", r); break;
+ case PROXY_STICKY_ENV:
+ ap_rputs ("Env:", r); break;
+ }
+ ap_rvputs (r, sticky[j].name, " ", NULL);
+ }
+ }
ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
apr_time_sec(balancer->timeout));
ap_rprintf(r, "<td>%s</td>\n",
Index: modules/proxy/mod_proxy.h
===================================================================
--- modules/proxy/mod_proxy.h (revision 526077)
+++ modules/proxy/mod_proxy.h (working copy)
@@ -348,7 +348,7 @@
struct proxy_balancer {
apr_array_header_t *workers; /* array of proxy_workers */
const char *name; /* name of the load balancer */
- const char *sticky; /* sticky session identifier */
+ apr_array_header_t *sticky; /* array of sticky session identifiers */
int sticky_force; /* Disable failover for sticky sessions */
apr_interval_time_t timeout; /* Timeout for waiting on free connection */
int max_attempts; /* Number of attempts before failing */
@@ -373,6 +373,15 @@
void *context; /* general purpose storage */
};
+struct proxy_sticky_entry {
+ const char *name; /* key to look for, e.g. JSESSIONID */
+ int method; /* where to search, e.g. PROXY_STICKY_ENV */
+};
+
+#define PROXY_STICKY_COOKIE 1 /* Search for stickiness by Cookie */
+#define PROXY_STICKY_PATH 2 /* Search for stickiness by URL / Path */
+#define PROXY_STICKY_ENV 4 /* Search for stickiness by Environment */
+
#if APR_HAS_THREADS
#define PROXY_THREAD_LOCK(x) apr_thread_mutex_lock((x)->mutex)
#define PROXY_THREAD_UNLOCK(x) apr_thread_mutex_unlock((x)->mutex)