Commit a4bc345a authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Add a "fallback" director as a variant of round-robin.

This one always picks the first healty backend, in the order
they are specified in the VCL.

Submitted by:	DocWilco
parent 7dd921d6
......@@ -156,4 +156,5 @@ dir_init_f VRT_init_dir_dns;
dir_init_f VRT_init_dir_hash;
dir_init_f VRT_init_dir_random;
dir_init_f VRT_init_dir_round_robin;
dir_init_f VRT_init_dir_fallback;
dir_init_f VRT_init_dir_client;
......@@ -256,6 +256,8 @@ VRT_init_dir(struct cli *cli, struct director **dir, const char *name,
VRT_init_dir_dns(cli, dir, idx, priv);
else if (!strcmp(name, "round-robin"))
VRT_init_dir_round_robin(cli, dir, idx, priv);
else if (!strcmp(name, "fallback"))
VRT_init_dir_fallback(cli, dir, idx, priv);
else if (!strcmp(name, "client"))
VRT_init_dir_client(cli, dir, idx, priv);
else
......
......@@ -47,10 +47,13 @@ struct vdi_round_robin_host {
struct director *backend;
};
enum mode_e { m_round_robin, m_fallback };
struct vdi_round_robin {
unsigned magic;
#define VDI_ROUND_ROBIN_MAGIC 0x2114a178
struct director dir;
enum mode_e mode;
struct vdi_round_robin_host *hosts;
unsigned nhosts;
unsigned next_host;
......@@ -68,9 +71,17 @@ vdi_round_robin_getfd(const struct director *d, struct sess *sp)
CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC);
CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC);
/*
* In fallback mode we ignore the next_host and always grab the
* first healthy backend we can find.
*/
for (i = 0; i < vs->nhosts; i++) {
backend = vs->hosts[vs->next_host].backend;
vs->next_host = (vs->next_host + 1) % vs->nhosts;
if (vs->mode == m_round_robin) {
backend = vs->hosts[vs->next_host].backend;
vs->next_host = (vs->next_host + 1) % vs->nhosts;
} else /* m_fallback */ {
backend = vs->hosts[i].backend;
}
if (!VDI_Healthy(backend, sp))
continue;
vbe = VDI_GetFd(backend, sp);
......@@ -114,9 +125,9 @@ vdi_round_robin_fini(const struct director *d)
FREE_OBJ(vs);
}
void
VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
const void *priv)
static void
vrt_init_dir(struct cli *cli, struct director **bp, int idx,
const void *priv, enum mode_e mode)
{
const struct vrt_dir_round_robin *t;
struct vdi_round_robin *vs;
......@@ -141,6 +152,7 @@ VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
vs->dir.fini = vdi_round_robin_fini;
vs->dir.healthy = vdi_round_robin_healthy;
vs->mode = mode;
vh = vs->hosts;
te = t->members;
for (i = 0; i < t->nmember; i++, vh++, te++) {
......@@ -152,3 +164,18 @@ VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
bp[idx] = &vs->dir;
}
void
VRT_init_dir_round_robin(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init_dir(cli, bp, idx, priv, m_round_robin);
}
void
VRT_init_dir_fallback(struct cli *cli, struct director **bp, int idx,
const void *priv)
{
vrt_init_dir(cli, bp, idx, priv, m_fallback);
}
varnishtest "Test fallback director"
server s1 {
rxreq
expect req.url == "/"
txresp -body "slash"
} -start
server s2 {
rxreq
expect req.url == "/"
txresp -body "slash"
} -start
server s3 {
rxreq
expect req.url == "/foo"
txresp -hdr "Foo: 3" -body "foobar"
} -start
varnish v1 -vcl {
probe p1 {
.url = "/";
.timeout = 1s;
.interval = 1s;
.window = 4;
.threshold = 3;
.initial = 0;
}
probe p2 {
.url = "/";
.timeout = 1s;
.interval = 1s;
.window = 3;
.threshold = 2;
.initial = 0;
}
backend b1 {
.host = "${s1_addr}";
.port = "${s1_port}";
.max_connections = 1;
.probe = p1;
}
backend b2 {
.host = "${s2_addr}";
.port = "${s2_port}";
.max_connections = 1;
.probe = p2;
}
backend b3 {
.host = "${s3_addr}";
.port = "${s3_port}";
}
director f1 fallback {
{ .backend = b1; }
{ .backend = b2; }
{ .backend = b3; }
}
sub vcl_recv {
set req.backend = f1;
return(pass);
}
} -start
# s1 & s2 have both had 1 probe, so both are unhealthy
client c1 {
txreq -url "/foo"
rxresp
expect resp.http.foo == "3"
} -run
# setup for probe #2
server s1 {
rxreq
expect req.url == "/"
txresp -body "slash"
} -start
server s2 {
rxreq
expect req.url == "/"
txresp -body "slash"
} -start
# if we muck with a running server, the test will wait until it's done,
# which will be after probe #2 completes. b2 will then be healthy.
server s2 {
rxreq
expect req.url == "/foo"
txresp -hdr "Foo: 2" -body "foobar"
} -start
client c1 {
txreq -url "/foo"
rxresp
expect resp.http.foo == "2"
} -run
# setup for probe #3
server s1 {
rxreq
expect req.url == "/"
txresp -body "slash"
} -start
server s2 {
rxreq
expect req.url == "/"
txresp -body "slash"
} -start
# after probe #3 b1 should be healthy.
server s1 {
rxreq
expect req.url == "/foo"
txresp -hdr "Foo: 1" -body "foobar"
} -start
client c1 {
txreq -url "/foo"
rxresp
expect resp.http.foo == "1"
} -run
......@@ -221,6 +221,22 @@ The above example will append "internal.example.net" to the incoming Host
header supplied by the client, before looking it up. All settings are
optional.
The fallback director
~~~~~~~~~~~~~~~~~~~~~
The fallback director will pick the first backend that is healthy. It
considers them in the order in which they are listed in its definition.
The fallback director does not take any options.
An example of a fallback director::
director b3 fallback {
{ .backend = www1; }
{ .backend = www2; } // will only be used if www1 is unhealthy.
{ .backend = www3; } // will only be used if both www1 and www2
// are unhealthy.
}
Backend probes
--------------
......
......@@ -695,6 +695,7 @@ static const struct dirlist {
{ "random", vcc_ParseRandomDirector },
{ "client", vcc_ParseRandomDirector },
{ "round-robin", vcc_ParseRoundRobinDirector },
{ "fallback", vcc_ParseRoundRobinDirector },
{ "dns", vcc_ParseDnsDirector },
{ NULL, NULL }
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment