From 098ae9ee5d0795995a086e3487c6e502dc73f8df Mon Sep 17 00:00:00 2001 From: petter Date: Mon, 14 Jul 2008 12:59:10 +0000 Subject: [PATCH] Added a round robin director. The round robin director can be created like this: director batman round-robin { { .backend = b1; } { .backend = b2; } { .backend = b3; } } sub vcl_recv { set req.backend = batman; } The backend will then be chosen in a round robin fashion. git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@2943 d4fa192b-c00b-0410-8231-f00ffab90ce4 --- varnish-cache/bin/varnishd/Makefile.am | 1 + .../bin/varnishd/cache_dir_round_robin.c | 127 ++++++++++++++++++ .../bin/varnishtest/tests/v00009.vtc | 67 +++++++++ varnish-cache/include/vrt.h | 16 +++ varnish-cache/lib/libvcl/Makefile.am | 1 + varnish-cache/lib/libvcl/vcc_backend.c | 1 + varnish-cache/lib/libvcl/vcc_compile.h | 3 + .../lib/libvcl/vcc_dir_round_robin.c | 108 +++++++++++++++ varnish-cache/lib/libvcl/vcc_fixed_token.c | 16 +++ 9 files changed, 340 insertions(+) create mode 100644 varnish-cache/bin/varnishd/cache_dir_round_robin.c create mode 100644 varnish-cache/bin/varnishtest/tests/v00009.vtc create mode 100644 varnish-cache/lib/libvcl/vcc_dir_round_robin.c diff --git a/varnish-cache/bin/varnishd/Makefile.am b/varnish-cache/bin/varnishd/Makefile.am index 770cf98e..93524f4f 100644 --- a/varnish-cache/bin/varnishd/Makefile.am +++ b/varnish-cache/bin/varnishd/Makefile.am @@ -18,6 +18,7 @@ varnishd_SOURCES = \ cache_center.c \ cache_cli.c \ cache_dir_random.c \ + cache_dir_round_robin.c \ cache_dir_simple.c \ cache_expire.c \ cache_fetch.c \ diff --git a/varnish-cache/bin/varnishd/cache_dir_round_robin.c b/varnish-cache/bin/varnishd/cache_dir_round_robin.c new file mode 100644 index 00000000..0a44f8d9 --- /dev/null +++ b/varnish-cache/bin/varnishd/cache_dir_round_robin.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006-2008 Linpro AS + * All rights reserved. + * + * Author: Petter Knudsen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: cache_dir_round_robin.c 2906 2008-07-08 10:29:07Z phk $ + * + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include + +#include "shmlog.h" +#include "cache.h" +#include "vrt.h" + +/*--------------------------------------------------------------------*/ + +struct vdi_round_robin_host { + struct backend *backend; +}; + +struct vdi_round_robin { + unsigned magic; +#define VDI_ROUND_ROBIN_MAGIC 0x2114a178 + struct director dir; + struct backend *backend; + struct vdi_round_robin_host *hosts; + unsigned nhosts; + unsigned next_host; +}; + + +static struct backend * +vdi_round_robin_choose(struct sess *sp) +{ + struct vdi_round_robin *vs; + struct backend *backend; + + CHECK_OBJ_NOTNULL(sp->director, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(vs, sp->director->priv, VDI_ROUND_ROBIN_MAGIC); + + backend = vs->hosts[ vs->next_host ].backend; + vs->next_host = (vs->next_host + 1) % vs->nhosts; + + return (backend); +} + +static void +vdi_round_robin_fini(struct director *d) +{ + int i; + struct vdi_round_robin *vs; + struct vdi_round_robin_host *vh; + + CHECK_OBJ_NOTNULL(d, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(vs, d->priv, VDI_ROUND_ROBIN_MAGIC); + + vh = vs->hosts; + for (i = 0; i < vs->nhosts; i++, vh++) + VBE_DropRef(vh->backend); + free(vs->hosts); + vs->dir.magic = 0; + vs->next_host = 0; + FREE_OBJ(vs); +} + +void +VRT_init_dir_round_robin(struct cli *cli, struct director **bp, const struct vrt_dir_round_robin *t) +{ + struct vdi_round_robin *vs; + const struct vrt_dir_round_robin_entry *te; + struct vdi_round_robin_host *vh; + int i; + + (void)cli; + + ALLOC_OBJ(vs, VDI_ROUND_ROBIN_MAGIC); + XXXAN(vs); + vs->hosts = calloc(sizeof *vh, t->nmember); + XXXAN(vs->hosts); + + vs->dir.magic = DIRECTOR_MAGIC; + vs->dir.priv = vs; + vs->dir.name = "round_robin"; + vs->dir.choose = vdi_round_robin_choose; + vs->dir.fini = vdi_round_robin_fini; + + vh = vs->hosts; + te = t->members; + for (i = 0; i < t->nmember; i++, vh++, te++) + vh->backend = VBE_AddBackend(cli, te->host); + vs->nhosts = t->nmember; + vs->next_host = 0; + + *bp = &vs->dir; +} diff --git a/varnish-cache/bin/varnishtest/tests/v00009.vtc b/varnish-cache/bin/varnishtest/tests/v00009.vtc new file mode 100644 index 00000000..515ca122 --- /dev/null +++ b/varnish-cache/bin/varnishtest/tests/v00009.vtc @@ -0,0 +1,67 @@ +# $Id$ + +test "Test round robin director" + +server s1 -listen 127.0.0.1:2000 { + rxreq + txresp -body "1" +} -start + +server s2 -listen 127.0.0.1:3000 { + rxreq + txresp -body "22" +} -start + + +server s3 -listen 127.0.0.1:4000 { + rxreq + txresp -body "333" +} -start + +server s4 -listen 127.0.0.1:5000 { + rxreq + txresp -body "4444" +} -start + + +varnish v1 -vcl+backend { + director batman round-robin { + { .backend = s1; } + { .backend = s2; } + { .backend = s3; } + { .backend = s4; } + } + + sub vcl_recv { + set req.backend = batman; + } +} -start + +client c1 { + timeout 3 + txreq -url "/foo1" + rxresp + expect resp.http.content-length == 1 + txreq -url "/foo2" + rxresp + expect resp.http.content-length == 2 + txreq -url "/foo3" + rxresp + expect resp.http.content-length == 3 + txreq -url "/foo4" + rxresp + expect resp.http.content-length == 4 +} -run + +server s1 -start +server s2 -start + +client c2 { + timeout 3 + txreq -url "/foo11" + rxresp + expect resp.http.content-length == 1 + txreq -url "/foo22" + rxresp + expect resp.http.content-length == 2 +} -run diff --git a/varnish-cache/include/vrt.h b/varnish-cache/include/vrt.h index 7ef1a4b7..013c0778 100644 --- a/varnish-cache/include/vrt.h +++ b/varnish-cache/include/vrt.h @@ -91,6 +91,21 @@ struct vrt_dir_random { const struct vrt_dir_random_entry *members; }; +/* + * A director with round robin selection + */ + +struct vrt_dir_round_robin_entry { + const struct vrt_backend *host; +}; + +struct vrt_dir_round_robin { + const char *name; + unsigned nmember; + const struct vrt_dir_round_robin_entry *members; +}; + + /* * other stuff. * XXX: document when bored @@ -147,6 +162,7 @@ void VRT_Rollback(struct sess *sp); /* Backend related */ void VRT_init_dir_simple(struct cli *, struct director **, const struct vrt_dir_simple *); void VRT_init_dir_random(struct cli *, struct director **, const struct vrt_dir_random *); +void VRT_init_dir_round_robin(struct cli *, struct director **, const struct vrt_dir_round_robin *); void VRT_fini_dir(struct cli *, struct director *); char *VRT_IP_string(const struct sess *sp, const struct sockaddr *sa); diff --git a/varnish-cache/lib/libvcl/Makefile.am b/varnish-cache/lib/libvcl/Makefile.am index 388ac77e..8910e8b1 100644 --- a/varnish-cache/lib/libvcl/Makefile.am +++ b/varnish-cache/lib/libvcl/Makefile.am @@ -14,6 +14,7 @@ libvcl_la_SOURCES = \ vcc_backend.c \ vcc_compile.c \ vcc_dir_random.c \ + vcc_dir_round_robin.c \ vcc_parse.c \ vcc_fixed_token.c \ vcc_obj.c \ diff --git a/varnish-cache/lib/libvcl/vcc_backend.c b/varnish-cache/lib/libvcl/vcc_backend.c index 0f43b548..ad7eb262 100644 --- a/varnish-cache/lib/libvcl/vcc_backend.c +++ b/varnish-cache/lib/libvcl/vcc_backend.c @@ -656,6 +656,7 @@ static const struct dirlist { parsedirector_f *func; } dirlist[] = { { "random", vcc_ParseRandomDirector }, + { "round-robin", vcc_ParseRoundRobinDirector }, { NULL, NULL } }; diff --git a/varnish-cache/lib/libvcl/vcc_compile.h b/varnish-cache/lib/libvcl/vcc_compile.h index 911e511f..05a83c47 100644 --- a/varnish-cache/lib/libvcl/vcc_compile.h +++ b/varnish-cache/lib/libvcl/vcc_compile.h @@ -178,6 +178,9 @@ void *TlAlloc(struct tokenlist *tl, unsigned len); /* vcc_dir_random.c */ parsedirector_f vcc_ParseRandomDirector; +/* vcc_dir_round_robin.c */ +parsedirector_f vcc_ParseRoundRobinDirector; + /* vcc_obj.c */ extern struct var vcc_vars[]; diff --git a/varnish-cache/lib/libvcl/vcc_dir_round_robin.c b/varnish-cache/lib/libvcl/vcc_dir_round_robin.c new file mode 100644 index 00000000..eda2acb5 --- /dev/null +++ b/varnish-cache/lib/libvcl/vcc_dir_round_robin.c @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 2006 Verdens Gang AS + * Copyright (c) 2006-2008 Linpro AS + * All rights reserved. + * + * Author: Petter Knudsen + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: vcc_dir_random.c 2900 2008-07-08 07:30:42Z phk $ + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include + +#include "vsb.h" + +#include "vcc_priv.h" +#include "vcc_compile.h" +#include "libvarnish.h" + +/*-------------------------------------------------------------------- + * Parse directors + */ + +void +vcc_ParseRoundRobinDirector(struct tokenlist *tl, const struct token *t_policy, const struct token *t_dir) +{ + struct token *t_field, *t_be; + int nbh, nelem; + struct fld_spec *fs; + + fs = vcc_FldSpec(tl, "!backend", NULL); + + Fc(tl, 0, + "\nstatic const struct vrt_dir_round_robin_entry vdrre_%.*s[] = {\n", + PF(t_dir)); + + for (nelem = 0; tl->t->tok != '}'; nelem++) { /* List of members */ + t_be = tl->t; + vcc_ResetFldSpec(fs); + nbh = -1; + + ExpectErr(tl, '{'); + vcc_NextToken(tl); + Fc(tl, 0, "\t{"); + + while (tl->t->tok != '}') { /* Member fields */ + vcc_IsField(tl, &t_field, fs); + ERRCHK(tl); + if (vcc_IdIs(t_field, "backend")) { + vcc_ParseBackendHost(tl, &nbh, + t_dir, t_policy, nelem); + Fc(tl, 0, " .host = &bh_%d,", nbh); + ERRCHK(tl); + } else { + ErrInternal(tl); + } + } + vcc_FieldsOk(tl, fs); + if (tl->err) { + vsb_printf(tl->sb, + "\nIn member host specfication starting at:\n"); + vcc_ErrWhere(tl, t_be); + return; + } + Fc(tl, 0, " },\n"); + vcc_NextToken(tl); + } + Fc(tl, 0, "};\n"); + Fc(tl, 0, + "\nstatic const struct vrt_dir_round_robin vdrr_%.*s = {\n", + PF(t_dir)); + Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(t_dir)); + Fc(tl, 0, "\t.nmember = %d,\n", nelem); + Fc(tl, 0, "\t.members = vdrre_%.*s,\n", PF(t_dir)); + Fc(tl, 0, "};\n"); + Fi(tl, 0, + "\tVRT_init_dir_round_robin(cli, &VGC_backend_%.*s , &vdrr_%.*s);\n", + PF(t_dir), PF(t_dir)); + Ff(tl, 0, "\tVRT_fini_dir(cli, VGC_backend_%.*s);\n", PF(t_dir)); +} diff --git a/varnish-cache/lib/libvcl/vcc_fixed_token.c b/varnish-cache/lib/libvcl/vcc_fixed_token.c index b0d87486..72746795 100644 --- a/varnish-cache/lib/libvcl/vcc_fixed_token.c +++ b/varnish-cache/lib/libvcl/vcc_fixed_token.c @@ -430,6 +430,21 @@ vcl_output_lang_h(struct vsb *sb) vsb_cat(sb, "};\n"); vsb_cat(sb, "\n"); vsb_cat(sb, "/*\n"); + vsb_cat(sb, " * A director with round robin selection\n"); + vsb_cat(sb, " */\n"); + vsb_cat(sb, "\n"); + vsb_cat(sb, "struct vrt_dir_round_robin_entry {\n"); + vsb_cat(sb, " const struct vrt_backend *host;\n"); + vsb_cat(sb, "};\n"); + vsb_cat(sb, "\n"); + vsb_cat(sb, "struct vrt_dir_round_robin {\n"); + vsb_cat(sb, " const char *name;\n"); + vsb_cat(sb, " unsigned nmember;\n"); + vsb_cat(sb, " const struct vrt_dir_round_robin_entry *members;\n"); + vsb_cat(sb, "};\n"); + vsb_cat(sb, "\n"); + vsb_cat(sb, "\n"); + vsb_cat(sb, "/*\n"); vsb_cat(sb, " * other stuff.\n"); vsb_cat(sb, " * XXX: document when bored\n"); vsb_cat(sb, " */\n"); @@ -485,6 +500,7 @@ vcl_output_lang_h(struct vsb *sb) vsb_cat(sb, "/* Backend related */\n"); vsb_cat(sb, "void VRT_init_dir_simple(struct cli *, struct director **, const struct vrt_dir_simple *);\n"); vsb_cat(sb, "void VRT_init_dir_random(struct cli *, struct director **, const struct vrt_dir_random *);\n"); + vsb_cat(sb, "void VRT_init_dir_round_robin(struct cli *, struct director **, const struct vrt_dir_round_robin *);\n"); vsb_cat(sb, "void VRT_fini_dir(struct cli *, struct director *);\n"); vsb_cat(sb, "\n"); vsb_cat(sb, "char *VRT_IP_string(const struct sess *sp, const struct sockaddr *sa);\n"); -- 2.39.5