source: lib/libvcl/vcc_backend.c @ 0b0c12

Revision 0b0c12, 8.4 KB checked in by Cecilie Fritzvold <cecilihf@…>, 7 years ago (diff)

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

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2007 Linpro AS
4 * All rights reserved.
5 *
6 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Id$
30 */
31
32#include <sys/types.h>
33#include <sys/socket.h>
34
35#include <netdb.h>
36#include <stdio.h>
37#include <string.h>
38
39#include "vsb.h"
40
41#include "vcc_priv.h"
42#include "vcc_compile.h"
43#include "libvarnish.h"
44
45static const char *
46CheckHostPort(const char *host, const char *port)
47{
48        struct addrinfo *res, hint;
49        int error;
50
51        memset(&hint, 0, sizeof hint);
52        hint.ai_family = PF_UNSPEC;
53        hint.ai_socktype = SOCK_STREAM;
54        error = getaddrinfo(host, port, &hint, &res);
55        if (error)
56                return (gai_strerror(error));
57        freeaddrinfo(res);
58        return (NULL);
59}
60
61void
62vcc_ParseSimpleBackend(struct tokenlist *tl)
63{
64        struct var *vp;
65        struct token *t_be = NULL;
66        struct token *t_host = NULL;
67        struct token *t_port = NULL;
68        const char *ep;
69
70        vcc_NextToken(tl);
71        ExpectErr(tl, ID);
72        t_be = tl->t;
73        vcc_AddDef(tl, tl->t, R_BACKEND);
74        /*
75         * The first backend is always referenced because that is the default
76         * at the beginning of vcl_recv
77         */
78        if (tl->nbackend == 0)
79                vcc_AddRef(tl, tl->t, R_BACKEND);
80
81        /* In the compiled vcl we use these macros to refer to backends */
82        Fh(tl, 1, "#define VGC_backend_%.*s (VCL_conf.backend[%d])\n",
83            PF(tl->t), tl->nbackend);
84
85        vcc_NextToken(tl);
86        ExpectErr(tl, '{');
87        vcc_NextToken(tl);
88       
89        while (1) {
90                if (tl->t->tok == '}')
91                        break;
92                ExpectErr(tl, ID);
93                if (!vcc_IdIs(tl->t, "set")) {
94                        vsb_printf(tl->sb,
95                            "Expected 'set', found ");
96                        vcc_ErrToken(tl, tl->t);
97                        vsb_printf(tl->sb, " at\n");
98                        vcc_ErrWhere(tl, tl->t);
99                        return;
100                }
101                vcc_NextToken(tl);
102                ExpectErr(tl, VAR);
103                vp = vcc_FindVar(tl, tl->t, vcc_be_vars);
104                ERRCHK(tl);
105                assert(vp != NULL);
106                vcc_NextToken(tl);
107                ExpectErr(tl, '=');
108                vcc_NextToken(tl);
109                switch (vp->fmt) {
110                case HOSTNAME:
111                        ExpectErr(tl, CSTR);
112                        t_host = tl->t;
113                        vcc_NextToken(tl);
114                        break;
115                case PORTNAME:
116                        ExpectErr(tl, CSTR);
117                        t_port = tl->t;
118                        vcc_NextToken(tl);
119                        break;
120                default:
121                        vsb_printf(tl->sb,
122                            "Assignments not possible for '%s'\n", vp->name);
123                        vcc_ErrWhere(tl, tl->t);
124                        return;
125                }
126                ExpectErr(tl, ';');
127                vcc_NextToken(tl);
128        }
129        ExpectErr(tl, '}');
130        if (t_host == NULL) {
131                vsb_printf(tl->sb, "Backend '%.*s' has no hostname\n",
132                    PF(t_be));
133                vcc_ErrWhere(tl, tl->t);
134                return;
135        }
136        ep = CheckHostPort(t_host->dec, "80");
137        if (ep != NULL) {
138                vsb_printf(tl->sb, "Backend '%.*s': %s\n", PF(t_be), ep);
139                vcc_ErrWhere(tl, t_host);
140                return;
141        }
142        if (t_port != NULL) {
143                ep = CheckHostPort(t_host->dec, t_port->dec);
144                if (ep != NULL) {
145                        vsb_printf(tl->sb,
146                            "Backend '%.*s': %s\n", PF(t_be), ep);
147                        vcc_ErrWhere(tl, t_port);
148                        return;
149                }
150        }
151
152        vcc_NextToken(tl);
153        Fc(tl, 0, "\nstatic struct vrt_simple_backend sbe_%.*s = {\n",
154            PF(t_be));
155        Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(t_be));
156        Fc(tl, 0, "\t.port = %.*s,\n", PF(t_port));
157        Fc(tl, 0, "\t.host = %.*s,\n", PF(t_host));
158        Fc(tl, 0, "};\n");
159        Fi(tl, 0, "\tVRT_init_simple_backend(&VGC_backend_%.*s , &sbe_%.*s);\n",
160            PF(t_be), PF(t_be));
161        Ff(tl, 0, "\tVRT_fini_backend(VGC_backend_%.*s);\n", PF(t_be));
162        tl->nbackend++;
163}
164
165void
166vcc_ParseBalancedBackend(struct tokenlist *tl)
167{
168        struct var *vp;
169        struct token *t_be = NULL;
170        struct token *t_host = NULL;
171        struct token *t_port = NULL;
172        double t_weight = 0;
173        const char *ep;
174        int cnt = 0;
175        int weighted = 0;
176        double weight = 0;
177        unsigned backend_type = tl->t->tok;
178
179        vcc_NextToken(tl);
180        ExpectErr(tl, ID);
181        t_be = tl->t;
182        vcc_AddDef(tl, tl->t, R_BACKEND);
183        /*
184         * The first backend is always referenced because that is the default
185         * at the beginning of vcl_recv
186         */
187        if (tl->nbackend == 0)
188                vcc_AddRef(tl, tl->t, R_BACKEND);
189
190        /* In the compiled vcl we use these macros to refer to backends */
191        Fh(tl, 1, "#define VGC_backend_%.*s (VCL_conf.backend[%d])\n",
192            PF(tl->t), tl->nbackend);
193
194        vcc_NextToken(tl);
195        ExpectErr(tl, '{');
196        vcc_NextToken(tl);
197       
198        while (1) {
199                if (tl->t->tok == '}')
200                        break;
201                ExpectErr(tl, ID);
202                if (!vcc_IdIs(tl->t, "set")) {
203                        vsb_printf(tl->sb,
204                            "Expected 'set', found ");
205                        vcc_ErrToken(tl, tl->t);
206                        vsb_printf(tl->sb, " at\n");
207                        vcc_ErrWhere(tl, tl->t);
208                        return;
209                }
210                vcc_NextToken(tl);
211                ExpectErr(tl, VAR);
212                vp = vcc_FindVar(tl, tl->t, vcc_be_vars);
213                ERRCHK(tl);
214                assert(vp != NULL);
215                vcc_NextToken(tl);
216                ExpectErr(tl, '=');
217                vcc_NextToken(tl);
218                if (vp->fmt != SET) {
219                        vsb_printf(tl->sb,
220                            "Assignments not possible for '%s'\n", vp->name);
221                        vcc_ErrWhere(tl, tl->t);
222                        return;
223                }
224               
225                ExpectErr(tl, '{');
226                vcc_NextToken(tl);
227               
228                while (1) {
229                        if (tl->t->tok == '}')
230                                break;
231                               
232                        ExpectErr(tl, '{');
233                        vcc_NextToken(tl);
234                       
235                        // Host
236                        ExpectErr(tl, CSTR);
237                        t_host = tl->t;
238                        vcc_NextToken(tl);
239               
240                        ep = CheckHostPort(t_host->dec, "80");
241                        if (ep != NULL) {
242                                vsb_printf(tl->sb, "Backend '%.*s': %s\n", PF(t_be), ep);
243                                vcc_ErrWhere(tl, t_host);
244                                return;
245                        }
246                       
247                        if (tl->t->tok == ',') {
248                                vcc_NextToken(tl);
249                               
250                                // Port
251                               
252                                ExpectErr(tl, CSTR);
253                                t_port = tl->t;
254                                vcc_NextToken(tl);
255                               
256                                ep = CheckHostPort(t_host->dec, t_port->dec);
257                                if (ep != NULL) {
258                                        vsb_printf(tl->sb,
259                                            "Backend '%.*s': %s\n", PF(t_be), ep);
260                                        vcc_ErrWhere(tl, t_port);
261                                        return;
262                                }
263                               
264                                if (tl->t->tok == ',') {
265                               
266                                        vcc_NextToken(tl);
267                                       
268                                        // Weight
269                                        t_weight = vcc_DoubleVal(tl);
270                                        weighted = 1;
271                                        weight += t_weight;
272                                }
273                        }
274                                               
275                        ExpectErr(tl, '}');
276                        vcc_NextToken(tl);
277               
278                        Fc(tl, 0, "\nstatic struct vrt_backend_entry bentry_%.*s_%d = {\n",
279                                PF(t_be), cnt);
280                        Fc(tl, 0, "\t.port = %.*s,\n", PF(t_port));
281                        Fc(tl, 0, "\t.host = %.*s,\n", PF(t_host));
282                        Fc(tl, 0, "\t.weight = %f,\n", t_weight);
283                        if (cnt > 0) {
284                                Fc(tl, 0, "\t.next = &bentry_%.*s_%d\n", PF(t_be), cnt-1);
285                        } /*else {
286                                Fc(tl, 0, "\t.next = NULL\n");
287                        }*/
288                        Fc(tl, 0, "};\n");
289                        t_weight = 0;
290                        cnt++;
291                }
292                ExpectErr(tl, '}');
293                vcc_NextToken(tl);
294                ExpectErr(tl, ';');
295                vcc_NextToken(tl);
296               
297                if (t_host == NULL) {
298                        vsb_printf(tl->sb, "Backend '%.*s' has no hostname\n",
299                        PF(t_be));
300                        vcc_ErrWhere(tl, tl->t);
301                        return;
302                }
303               
304                if (weighted && (int)weight != 1) {
305                        vsb_printf(tl->sb, "Total weight must be 1\n");
306                        vcc_ErrWhere(tl, tl->t);
307                        return;
308                }
309               
310                if (backend_type == T_BACKEND_ROUND_ROBIN) {
311                        Fc(tl, 0, "\nstatic struct vrt_round_robin_backend sbe_%.*s = {\n",
312                            PF(t_be));
313                        Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(t_be));
314                        Fc(tl, 0, "\t.bentry = &bentry_%.*s_%d\n", PF(t_be), cnt-1);
315                        Fc(tl, 0, "};\n");
316                        Fi(tl, 0, "\tVRT_init_round_robin_backend(&VGC_backend_%.*s , &sbe_%.*s);\n",
317                            PF(t_be), PF(t_be));
318                } else if (backend_type == T_BACKEND_RANDOM) {
319                        Fc(tl, 0, "\nstatic struct vrt_random_backend sbe_%.*s = {\n",
320                            PF(t_be));
321                        Fc(tl, 0, "\t.name = \"%.*s\",\n", PF(t_be));
322                        Fc(tl, 0, "\t.weighted = %d,\n", weighted);
323                        Fc(tl, 0, "\t.count = %d,\n", cnt);
324                        Fc(tl, 0, "\t.bentry = &bentry_%.*s_%d\n", PF(t_be), cnt-1);
325                        Fc(tl, 0, "};\n");
326                        Fi(tl, 0, "\tVRT_init_random_backend(&VGC_backend_%.*s , &sbe_%.*s);\n",
327                            PF(t_be), PF(t_be));
328                }
329                Ff(tl, 0, "\tVRT_fini_backend(VGC_backend_%.*s);\n", PF(t_be));
330
331        }
332        ExpectErr(tl, '}');
333
334        vcc_NextToken(tl);
335        tl->nbackend++;
336}
337
Note: See TracBrowser for help on using the repository browser.