Commit 0b0c12a1 authored by Cecilie Fritzvold's avatar Cecilie Fritzvold

Added support for load balancing among backends in varnish. It is still experimental

and very basic, but it should be ready for testing. Two strategies for load balancing
are implemented: a simple round robin, and a simple weighted random. The following
is an example configuration in vcl. The weight parameter for random is optional. Default
is equal weight.

backend foo {
	set backend.host = "foo.bar.com";
	set backend.port = "http";
}

backend_round_robin rr {
	set backend.set = {
		{ "foo1.bar.com", "http" }
		{ "foo2.bar.com", "http" }
		{ "foo3.bar.com", "http" }
	};
}

backend_random rrr {
	set backend.set = {
		{ "foo1.bar.com", "http", 0.3 }
		{ "foo2.bar.com", "http", 0.6 }
		{ "foo3.bar.com", "http", 0.1 }
	};
}

sub vcl_recv {
	if {req.http.host ~ "foo"} {
		req.backend = foo;
	} elseif {req.http.host ~ "bar"} {
		req.backend = rr;
	} else {
		req.backend = rrr;
	}
}




git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@1931 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 86a6b9bc
......@@ -12,6 +12,8 @@ varnishd_SOURCES = \
cache_acceptor_poll.c \
cache_acceptor_kqueue.c \
cache_backend.c \
cache_backend_random.c \
cache_backend_round_robin.c \
cache_backend_simple.c \
cache_ban.c \
cache_center.c \
......
......@@ -326,6 +326,7 @@ struct vbe_conn {
TAILQ_ENTRY(vbe_conn) list;
struct backend *backend;
int fd;
void *priv;
};
......
This diff is collapsed.
This diff is collapsed.
......@@ -46,6 +46,26 @@ struct vrt_simple_backend {
const char *host;
};
struct vrt_backend_entry {
const char *port;
const char *host;
double weight;
struct vrt_backend_entry *next;
};
struct vrt_round_robin_backend {
const char *name;
struct vrt_backend_entry *bentry;
};
struct vrt_random_backend {
const char *name;
unsigned weighted;
unsigned count;
struct vrt_backend_entry *bentry;
};
struct vrt_ref {
unsigned source;
unsigned offset;
......@@ -94,6 +114,8 @@ int VRT_strcmp(const char *s1, const char *s2);
/* Backend related */
void VRT_init_simple_backend(struct backend **, struct vrt_simple_backend *);
void VRT_init_round_robin_backend(struct backend **, struct vrt_round_robin_backend *);
void VRT_init_random_backend(struct backend **, struct vrt_random_backend *);
void VRT_fini_backend(struct backend *);
char *VRT_IP_string(struct sess *sp, struct sockaddr *sa);
......
......@@ -9,6 +9,7 @@
void VRT_l_backend_host(struct backend *, const char *);
void VRT_l_backend_port(struct backend *, const char *);
void VRT_l_backend_dnsttl(struct backend *, double);
void VRT_l_backend_set(struct backend *, struct vrt_backend_entry *);
struct sockaddr * VRT_r_client_ip(struct sess *);
struct sockaddr * VRT_r_server_ip(struct sess *);
const char * VRT_r_req_request(struct sess *);
......
......@@ -59,7 +59,7 @@ CheckHostPort(const char *host, const char *port)
}
void
vcc_ParseBackend(struct tokenlist *tl)
vcc_ParseSimpleBackend(struct tokenlist *tl)
{
struct var *vp;
struct token *t_be = NULL;
......@@ -85,6 +85,7 @@ vcc_ParseBackend(struct tokenlist *tl)
vcc_NextToken(tl);
ExpectErr(tl, '{');
vcc_NextToken(tl);
while (1) {
if (tl->t->tok == '}')
break;
......@@ -160,3 +161,177 @@ vcc_ParseBackend(struct tokenlist *tl)
Ff(tl, 0, "\tVRT_fini_backend(VGC_backend_%.*s);\n", PF(t_be));
tl->nbackend++;
}
void
vcc_ParseBalancedBackend(struct tokenlist *tl)
{
struct var *vp;
struct token *t_be = NULL;
struct token *t_host = NULL;
struct token *t_port = NULL;
double t_weight = 0;
const char *ep;
int cnt = 0;
int weighted = 0;
double weight = 0;
unsigned backend_type = tl->t->tok;
vcc_NextToken(tl);
ExpectErr(tl, ID);
t_be = tl->t;
vcc_AddDef(tl, tl->t, R_BACKEND);
/*
* The first backend is always referenced because that is the default
* at the beginning of vcl_recv
*/
if (tl->nbackend == 0)
vcc_AddRef(tl, tl->t, R_BACKEND);
/* In the compiled vcl we use these macros to refer to backends */
Fh(tl, 1, "#define VGC_backend_%.*s (VCL_conf.backend[%d])\n",
PF(tl->t), tl->nbackend);
vcc_NextToken(tl);
ExpectErr(tl, '{');
vcc_NextToken(tl);
while (1) {
if (tl->t->tok == '}')
break;
ExpectErr(tl, ID);
if (!vcc_IdIs(tl->t, "set")) {
vsb_printf(tl->sb,
"Expected 'set', found ");
vcc_ErrToken(tl, tl->t);
vsb_printf(tl->sb, " at\n");
vcc_ErrWhere(tl, tl->t);
return;
}
vcc_NextToken(tl);
ExpectErr(tl, VAR);
vp = vcc_FindVar(tl, tl->t, vcc_be_vars);
ERRCHK(tl);
assert(vp != NULL);
vcc_NextToken(tl);
ExpectErr(tl, '=');
vcc_NextToken(tl);
if (vp->fmt != SET) {
vsb_printf(tl->sb,
"Assignments not possible for '%s'\n", vp->name);
vcc_ErrWhere(tl, tl->t);
return;
}
ExpectErr(tl, '{');
vcc_NextToken(tl);
while (1) {
if (tl->t->tok == '}')
break;
ExpectErr(tl, '{');
vcc_NextToken(tl);
// Host
ExpectErr(tl, CSTR);
t_host = tl->t;
vcc_NextToken(tl);
ep = CheckHostPort(t_host->dec, "80");
if (ep != NULL) {
vsb_printf(tl->sb, "Backend '%.*s': %s\n", PF(t_be), ep);
vcc_ErrWhere(tl, t_host);
return;
}
if (tl->t->tok == ',') {
vcc_NextToken(tl);
// Port
ExpectErr(tl, CSTR);
t_port = tl->t;
vcc_NextToken(tl);
ep = CheckHostPort(t_host->dec, t_port->dec);
if (ep != NULL) {
vsb_printf(tl->sb,
"Backend '%.*s': %s\n", PF(t_be), ep);
vcc_ErrWhere(tl, t_port);
return;
}
if (tl->t->tok == ',') {
vcc_NextToken(tl);
// Weight
t_weight = vcc_DoubleVal(tl);
weighted = 1;
weight += t_weight;
}
}
ExpectErr(tl, '}');
vcc_NextToken(tl);
Fc(tl, 0, "\nstatic struct vrt_backend_entry bentry_%.*s_%d = {\n",
PF(t_be), cnt);
Fc(tl, 0, "\t.port = %.*s,\n", PF(t_port));
Fc(tl, 0, "\t.host = %.*s,\n", PF(t_host));
Fc(tl, 0, "\t.weight = %f,\n", t_weight);
if (cnt > 0) {
Fc(tl, 0, "\t.next = &bentry_%.*s_%d\n", PF(t_be), cnt-1);
} /*else {
Fc(tl, 0, "\t.next = NULL\n");
}*/
Fc(tl, 0, "};\n");
t_weight = 0;
cnt++;
}
ExpectErr(tl, '}');
vcc_NextToken(tl);
ExpectErr(tl, ';');
vcc_NextToken(tl);
if (t_host == NULL) {
vsb_printf(tl->sb, "Backend '%.*s' has no hostname\n",
PF(t_be));
vcc_ErrWhere(tl, tl->t);
return;
}
if (weighted && (int)weight != 1) {
vsb_printf(tl->sb, "Total weight must be 1\n");
vcc_ErrWhere(tl, tl->t);
return;
}
if (backend_type == T_BACKEND_ROUND_ROBIN) {
Fc(tl, 0, "\nstatic struct vrt_round_robin_backend sbe_%.*s = {\n",
PF(t_be));
Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(t_be));
Fc(tl, 0, "\t.bentry = &bentry_%.*s_%d\n", PF(t_be), cnt-1);
Fc(tl, 0, "};\n");
Fi(tl, 0, "\tVRT_init_round_robin_backend(&VGC_backend_%.*s , &sbe_%.*s);\n",
PF(t_be), PF(t_be));
} else if (backend_type == T_BACKEND_RANDOM) {
Fc(tl, 0, "\nstatic struct vrt_random_backend sbe_%.*s = {\n",
PF(t_be));
Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(t_be));
Fc(tl, 0, "\t.weighted = %d,\n", weighted);
Fc(tl, 0, "\t.count = %d,\n", cnt);
Fc(tl, 0, "\t.bentry = &bentry_%.*s_%d\n", PF(t_be), cnt-1);
Fc(tl, 0, "};\n");
Fi(tl, 0, "\tVRT_init_random_backend(&VGC_backend_%.*s , &sbe_%.*s);\n",
PF(t_be), PF(t_be));
}
Ff(tl, 0, "\tVRT_fini_backend(VGC_backend_%.*s);\n", PF(t_be));
}
ExpectErr(tl, '}');
vcc_NextToken(tl);
tl->nbackend++;
}
......@@ -99,7 +99,8 @@ enum var_type {
HOSTNAME,
PORTNAME,
HASH,
HEADER
HEADER,
SET
};
enum ref_type {
......@@ -144,7 +145,8 @@ void vcc_Cond_Ip(const struct var *vp, struct tokenlist *tl);
void vcc_ParseAction(struct tokenlist *tl);
/* vcc_backend.c */
void vcc_ParseBackend(struct tokenlist *tl);
void vcc_ParseSimpleBackend(struct tokenlist *tl);
void vcc_ParseBalancedBackend(struct tokenlist *tl);
/* vcc_compile.c */
extern struct method method_tab[];
......
......@@ -166,6 +166,24 @@ vcl_fixed_token(const char *p, const char **q)
}
return (0);
case 'b':
if (p[0] == 'b' && p[1] == 'a' && p[2] == 'c' &&
p[3] == 'k' && p[4] == 'e' && p[5] == 'n' &&
p[6] == 'd' && p[7] == '_' && p[8] == 'r' &&
p[9] == 'o' && p[10] == 'u' && p[11] == 'n' &&
p[12] == 'd' && p[13] == '_' && p[14] == 'r' &&
p[15] == 'o' && p[16] == 'b' && p[17] == 'i' &&
p[18] == 'n' && !isvar(p[19])) {
*q = p + 19;
return (T_BACKEND_ROUND_ROBIN);
}
if (p[0] == 'b' && p[1] == 'a' && p[2] == 'c' &&
p[3] == 'k' && p[4] == 'e' && p[5] == 'n' &&
p[6] == 'd' && p[7] == '_' && p[8] == 'r' &&
p[9] == 'a' && p[10] == 'n' && p[11] == 'd' &&
p[12] == 'o' && p[13] == 'm' && !isvar(p[14])) {
*q = p + 14;
return (T_BACKEND_RANDOM);
}
if (p[0] == 'b' && p[1] == 'a' && p[2] == 'c' &&
p[3] == 'k' && p[4] == 'e' && p[5] == 'n' &&
p[6] == 'd' && !isvar(p[7])) {
......@@ -274,6 +292,8 @@ vcl_init_tnames(void)
vcl_tnames[ID] = "ID";
vcl_tnames[T_ACL] = "acl";
vcl_tnames[T_BACKEND] = "backend";
vcl_tnames[T_BACKEND_RANDOM] = "backend_random";
vcl_tnames[T_BACKEND_ROUND_ROBIN] = "backend_round_robin";
vcl_tnames[T_CAND] = "&&";
vcl_tnames[T_COR] = "||";
vcl_tnames[T_DEC] = "--";
......@@ -333,7 +353,7 @@ vcl_output_lang_h(struct vsb *sb)
vsb_cat(sb, " struct vrt_ref *ref;\n");
vsb_cat(sb, " unsigned nref;\n");
vsb_cat(sb, " unsigned busy;\n");
vsb_cat(sb, "\n");
vsb_cat(sb, " \n");
vsb_cat(sb, " unsigned nsrc;\n");
vsb_cat(sb, " const char **srcname;\n");
vsb_cat(sb, " const char **srcbody;\n");
......@@ -404,6 +424,26 @@ vcl_output_lang_h(struct vsb *sb)
vsb_cat(sb, " const char *host;\n");
vsb_cat(sb, "};\n");
vsb_cat(sb, "\n");
vsb_cat(sb, "struct vrt_backend_entry {\n");
vsb_cat(sb, " const char *port;\n");
vsb_cat(sb, " const char *host;\n");
vsb_cat(sb, " double weight;\n");
vsb_cat(sb, " struct vrt_backend_entry *next;\n");
vsb_cat(sb, "};\n");
vsb_cat(sb, "\n");
vsb_cat(sb, "struct vrt_round_robin_backend {\n");
vsb_cat(sb, " const char *name;\n");
vsb_cat(sb, " struct vrt_backend_entry *bentry;\n");
vsb_cat(sb, "};\n");
vsb_cat(sb, "\n");
vsb_cat(sb, "struct vrt_random_backend {\n");
vsb_cat(sb, " const char *name;\n");
vsb_cat(sb, " unsigned weighted;\n");
vsb_cat(sb, " unsigned count;\n");
vsb_cat(sb, " struct vrt_backend_entry *bentry;\n");
vsb_cat(sb, "};\n");
vsb_cat(sb, "\n");
vsb_cat(sb, "\n");
vsb_cat(sb, "struct vrt_ref {\n");
vsb_cat(sb, " unsigned source;\n");
vsb_cat(sb, " unsigned offset;\n");
......@@ -452,6 +492,8 @@ vcl_output_lang_h(struct vsb *sb)
vsb_cat(sb, "\n");
vsb_cat(sb, "/* Backend related */\n");
vsb_cat(sb, "void VRT_init_simple_backend(struct backend **, struct vrt_simple_backend *);\n");
vsb_cat(sb, "void VRT_init_round_robin_backend(struct backend **, struct vrt_round_robin_backend *);\n");
vsb_cat(sb, "void VRT_init_random_backend(struct backend **, struct vrt_random_backend *);\n");
vsb_cat(sb, "void VRT_fini_backend(struct backend *);\n");
vsb_cat(sb, "\n");
vsb_cat(sb, "char *VRT_IP_string(struct sess *sp, struct sockaddr *sa);\n");
......@@ -473,6 +515,7 @@ vcl_output_lang_h(struct vsb *sb)
vsb_cat(sb, "void VRT_l_backend_host(struct backend *, const char *);\n");
vsb_cat(sb, "void VRT_l_backend_port(struct backend *, const char *);\n");
vsb_cat(sb, "void VRT_l_backend_dnsttl(struct backend *, double);\n");
vsb_cat(sb, "void VRT_l_backend_set(struct backend *, struct vrt_backend_entry *);\n");
vsb_cat(sb, "struct sockaddr * VRT_r_client_ip(struct sess *);\n");
vsb_cat(sb, "struct sockaddr * VRT_r_server_ip(struct sess *);\n");
vsb_cat(sb, "const char * VRT_r_req_request(struct sess *);\n");
......
......@@ -73,6 +73,10 @@ set keywords {
acl
backend
backend_round_robin
backend_random
}
# Non-word tokens
......@@ -137,7 +141,7 @@ puts $fo { unsigned magic;
struct vrt_ref *ref;
unsigned nref;
unsigned busy;
unsigned nsrc;
const char **srcname;
const char **srcbody;
......
......@@ -35,6 +35,7 @@ set beobj {
{ backend.host WO HOSTNAME {} }
{ backend.port WO PORTNAME {} }
{ backend.dnsttl WO TIME {} }
{ backend.set WO SET {} }
}
# Variables available in sessions
......@@ -180,6 +181,7 @@ set tt(HEADER) "const char *"
set tt(HOSTNAME) "const char *"
set tt(PORTNAME) "const char *"
set tt(HASH) "const char *"
set tt(SET) "struct vrt_backend_entry *"
#----------------------------------------------------------------------
# Boilerplate warning for all generated files.
......
......@@ -31,6 +31,13 @@ struct var vcc_be_vars[] = {
0,
0
},
{ "backend.set", SET, 11,
NULL,
"VRT_l_backend_set(backend, ",
V_WO,
0,
0
},
{ NULL }
};
......
......@@ -545,7 +545,11 @@ vcc_Parse(struct tokenlist *tl)
Function(tl);
break;
case T_BACKEND:
vcc_ParseBackend(tl);
vcc_ParseSimpleBackend(tl);
break;
case T_BACKEND_RANDOM:
case T_BACKEND_ROUND_ROBIN:
vcc_ParseBalancedBackend(tl);
break;
case EOI:
break;
......
......@@ -15,23 +15,25 @@
#define T_SUB 133
#define T_ACL 134
#define T_BACKEND 135
#define T_INC 136
#define T_DEC 137
#define T_CAND 138
#define T_COR 139
#define T_LEQ 140
#define T_EQ 141
#define T_NEQ 142
#define T_GEQ 143
#define T_SHR 144
#define T_SHL 145
#define T_INCR 146
#define T_DECR 147
#define T_MUL 148
#define T_DIV 149
#define ID 150
#define VAR 151
#define CNUM 152
#define CSTR 153
#define EOI 154
#define CSRC 155
#define T_BACKEND_ROUND_ROBIN 136
#define T_BACKEND_RANDOM 137
#define T_INC 138
#define T_DEC 139
#define T_CAND 140
#define T_COR 141
#define T_LEQ 142
#define T_EQ 143
#define T_NEQ 144
#define T_GEQ 145
#define T_SHR 146
#define T_SHL 147
#define T_INCR 148
#define T_DECR 149
#define T_MUL 150
#define T_DIV 151
#define ID 152
#define VAR 153
#define CNUM 154
#define CSTR 155
#define EOI 156
#define CSRC 157
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