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

Separate the generic backend handling from the backend "method".

For now all we have is a "simple" method, but we want more complex
methods later on, round-robin, least-busy and so on.

For now, fall into the other ditch and move everything to 
cache_backend_simple.c, we will move bits back as the gain generality.



git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@1892 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 0bde32eb
......@@ -12,6 +12,7 @@ varnishd_SOURCES = \
cache_acceptor_poll.c \
cache_acceptor_kqueue.c \
cache_backend.c \
cache_backend_simple.c \
cache_ban.c \
cache_center.c \
cache_cli.c \
......
......@@ -178,7 +178,7 @@ struct workreq {
#include "hash_slinger.h"
/* Backend Connection ------------------------------------------------*/
/* Backend Request ---------------------------------------------------*/
struct bereq {
unsigned magic;
......@@ -189,14 +189,6 @@ struct bereq {
struct http http[1];
};
struct vbe_conn {
unsigned magic;
#define VBE_CONN_MAGIC 0x0c5e6592
TAILQ_ENTRY(vbe_conn) list;
struct backend *backend;
int fd;
};
/* Storage -----------------------------------------------------------*/
struct storage {
......@@ -325,10 +317,40 @@ struct sess {
const char **hashptr;
};
/* -------------------------------------------------------------------*/
/* Backend connection */
struct vbe_conn {
unsigned magic;
#define VBE_CONN_MAGIC 0x0c5e6592
TAILQ_ENTRY(vbe_conn) list;
struct backend *backend;
int fd;
};
/* Backend method */
typedef struct vbe_conn *vbe_getfd_f(struct sess *sp);
typedef void vbe_close_f(struct worker *w, struct vbe_conn *vc);
typedef void vbe_recycle_f(struct worker *w, struct vbe_conn *vc);
typedef void vbe_init_f(void);
struct backend_method {
const char *name;
vbe_getfd_f *getfd;
vbe_close_f *close;
vbe_recycle_f *recycle;
vbe_init_f *init;
};
/* Backend indstance */
struct backend {
unsigned magic;
#define BACKEND_MAGIC 0x64c4c7c6
const char *vcl_name;
struct backend_method *method;
const char *hostname;
const char *portname;
......@@ -364,6 +386,7 @@ void VCA_Init(void);
extern int vca_pipes[2];
/* cache_backend.c */
void VBE_Init(void);
struct vbe_conn *VBE_GetFd(struct sess *sp);
void VBE_ClosedFd(struct worker *w, struct vbe_conn *vc);
......@@ -371,6 +394,9 @@ void VBE_RecycleFd(struct worker *w, struct vbe_conn *vc);
struct bereq * VBE_new_bereq(void);
void VBE_free_bereq(struct bereq *bereq);
/* cache_backend_simple.c */
extern struct backend_method backend_method_simple;
/* cache_ban.c */
void AddBan(const char *, int hash);
void BAN_Init(void);
......
......@@ -28,44 +28,24 @@
*
* $Id$
*
* NB: This file is in transition:
* In the future it will contain the central part of the backend handling,
* refcounting, functions for DNS lookup and connection revalidation etc.
* The actual policies will be put in separate files, cache_backend_simple.c,
* cache_backend_round_robin.c etc etc.
* Manage backend connections and requests.
*
* Manage backend connections.
*
* XXX: When we switch VCL we can have vbe_conn's dangling from
* XXX: the backends no longer used. When the VCL's refcount
* XXX: drops to zero we should zap them.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include "heritage.h"
#include "shmlog.h"
#include "cache.h"
/* A backend IP */
static TAILQ_HEAD(,vbe_conn) vbe_head = TAILQ_HEAD_INITIALIZER(vbe_head);
static TAILQ_HEAD(,bereq) bereq_head = TAILQ_HEAD_INITIALIZER(bereq_head);
static MTX vbemtx;
/*--------------------------------------------------------------------*/
/*--------------------------------------------------------------------
* Get a http structure for talking to the backend.
*/
struct bereq *
VBE_new_bereq(void)
......@@ -108,245 +88,11 @@ VBE_free_bereq(struct bereq *bereq)
/*--------------------------------------------------------------------*/
static struct vbe_conn *
vbe_new_conn(void)
{
struct vbe_conn *vbc;
vbc = calloc(sizeof *vbc, 1);
if (vbc == NULL)
return (NULL);
VSL_stats->n_vbe_conn++;
vbc->magic = VBE_CONN_MAGIC;
vbc->fd = -1;
return (vbc);
}
/*--------------------------------------------------------------------
* XXX: There is a race here, we need to lock the replacement of the
* XXX: resolved addresses, or some other thread might try to access
* XXX: them while/during/after we changed them.
* XXX: preferably, we should make a copy to the vbe while we hold a
* XXX: lock anyway.
*/
static void
vbe_lookup(struct backend *bp)
{
struct addrinfo *res, hint, *old;
int error;
memset(&hint, 0, sizeof hint);
hint.ai_family = PF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
res = NULL;
error = getaddrinfo(bp->hostname,
bp->portname == NULL ? "http" : bp->portname,
&hint, &res);
bp->dnstime = TIM_mono();
if (error) {
if (res != NULL)
freeaddrinfo(res);
printf("getaddrinfo: %s\n", gai_strerror(error)); /* XXX */
return;
}
old = bp->addr;
bp->last_addr = res;
bp->addr = res;
if (old != NULL)
freeaddrinfo(old);
}
/*--------------------------------------------------------------------*/
static int
vbe_sock_conn(const struct addrinfo *ai)
{
int s;
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s >= 0 && connect(s, ai->ai_addr, ai->ai_addrlen)) {
AZ(close(s));
s = -1;
}
return (s);
}
/*--------------------------------------------------------------------*/
static int
vbe_conn_try(struct backend *bp, struct addrinfo **pai)
{
struct addrinfo *ai;
int s;
/* First try the cached good address, and any following it */
for (ai = bp->last_addr; ai != NULL; ai = ai->ai_next) {
s = vbe_sock_conn(ai);
if (s >= 0) {
bp->last_addr = ai;
*pai = ai;
return (s);
}
}
/* Then try the list until the cached last good address */
for (ai = bp->addr; ai != bp->last_addr; ai = ai->ai_next) {
s = vbe_sock_conn(ai);
if (s >= 0) {
bp->last_addr = ai;
*pai = ai;
return (s);
}
}
if (bp->dnstime + bp->dnsttl >= TIM_mono())
return (-1);
/* Then do another lookup to catch DNS changes */
vbe_lookup(bp);
/* And try the entire list */
for (ai = bp->addr; ai != NULL; ai = ai->ai_next) {
s = vbe_sock_conn(ai);
if (s >= 0) {
bp->last_addr = ai;
*pai = ai;
return (s);
}
}
return (-1);
}
static int
vbe_connect(struct sess *sp, struct backend *bp)
{
int s;
char abuf1[TCP_ADDRBUFSIZE], abuf2[TCP_ADDRBUFSIZE];
char pbuf1[TCP_PORTBUFSIZE], pbuf2[TCP_PORTBUFSIZE];
struct addrinfo *ai;
CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
AN(bp->hostname);
s = vbe_conn_try(bp, &ai);
if (s < 0)
return (s);
TCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
TCP_name(ai->ai_addr, ai->ai_addrlen,
abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
WSL(sp->wrk, SLT_BackendOpen, s, "%s %s %s %s %s",
bp->vcl_name, abuf1, pbuf1, abuf2, pbuf2);
return (s);
}
/* Get a backend connection ------------------------------------------
*
* Try all cached backend connections for this backend, and use the
* first one that is looks like it is still connected.
* If that fails to get us a connection, create a new one, reusing a
* connection from the freelist, if possible.
*
* This function is slightly complicated by optimizations on vbemtx.
*/
static struct vbe_conn *
vbe_nextfd(struct sess *sp)
{
struct vbe_conn *vc, *vc2;
struct pollfd pfd;
struct backend *bp;
int reuse = 0;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
bp = sp->backend;
CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
vc2 = NULL;
while (1) {
LOCK(&vbemtx);
vc = TAILQ_FIRST(&bp->connlist);
if (vc != NULL) {
assert(vc->backend == bp);
assert(vc->fd >= 0);
TAILQ_REMOVE(&bp->connlist, vc, list);
} else {
vc2 = TAILQ_FIRST(&vbe_head);
if (vc2 != NULL) {
VSL_stats->backend_unused--;
TAILQ_REMOVE(&vbe_head, vc2, list);
}
}
UNLOCK(&vbemtx);
if (vc == NULL)
break;
/* Test the connection for remote close before we use it */
pfd.fd = vc->fd;
pfd.events = POLLIN;
pfd.revents = 0;
if (!poll(&pfd, 1, 0)) {
reuse = 1;
break;
}
VBE_ClosedFd(sp->wrk, vc);
}
if (vc == NULL) {
if (vc2 == NULL)
vc = vbe_new_conn();
else
vc = vc2;
if (vc != NULL) {
assert(vc->fd == -1);
AZ(vc->backend);
vc->fd = vbe_connect(sp, bp);
if (vc->fd < 0) {
LOCK(&vbemtx);
TAILQ_INSERT_HEAD(&vbe_head, vc, list);
VSL_stats->backend_unused++;
UNLOCK(&vbemtx);
vc = NULL;
} else {
vc->backend = bp;
}
}
}
LOCK(&vbemtx);
if (vc != NULL ) {
VSL_stats->backend_reuse += reuse;
VSL_stats->backend_conn++;
} else {
VSL_stats->backend_fail++;
}
UNLOCK(&vbemtx);
if (vc != NULL ) {
WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid);
assert(vc->fd >= 0);
assert(vc->backend == bp);
}
return (vc);
}
/*--------------------------------------------------------------------*/
struct vbe_conn *
VBE_GetFd(struct sess *sp)
{
struct vbe_conn *vc;
unsigned n;
for (n = 1; n < 5; n++) {
vc = vbe_nextfd(sp);
if (vc != NULL) {
WSL(sp->wrk, SLT_Backend, sp->fd, "%d %s", vc->fd,
sp->backend->vcl_name);
return (vc);
}
usleep(100000 * n);
}
return (NULL);
return(sp->backend->method->getfd(sp));
}
/* Close a connection ------------------------------------------------*/
......@@ -355,17 +101,7 @@ void
VBE_ClosedFd(struct worker *w, struct vbe_conn *vc)
{
CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
assert(vc->fd >= 0);
AN(vc->backend);
WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name);
AZ(close(vc->fd));
vc->fd = -1;
vc->backend = NULL;
LOCK(&vbemtx);
TAILQ_INSERT_HEAD(&vbe_head, vc, list);
VSL_stats->backend_unused++;
UNLOCK(&vbemtx);
vc->backend->method->close(w, vc);
}
/* Recycle a connection ----------------------------------------------*/
......@@ -374,14 +110,7 @@ void
VBE_RecycleFd(struct worker *w, struct vbe_conn *vc)
{
CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
assert(vc->fd >= 0);
AN(vc->backend);
WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name);
LOCK(&vbemtx);
VSL_stats->backend_recycle++;
TAILQ_INSERT_HEAD(&vc->backend->connlist, vc, list);
UNLOCK(&vbemtx);
vc->backend->method->recycle(w, vc);
}
/*--------------------------------------------------------------------*/
......@@ -391,4 +120,5 @@ VBE_Init(void)
{
MTX_INIT(&vbemtx);
backend_method_simple.init();
}
/*-
* Copyright (c) 2006 Verdens Gang AS
* Copyright (c) 2006-2007 Linpro AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
*
* 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$
*
*
* XXX: When we switch VCL we can have vbe_conn's dangling from
* XXX: the backends no longer used. When the VCL's refcount
* XXX: drops to zero we should zap them.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include "shmlog.h"
#include "cache.h"
static TAILQ_HEAD(,vbe_conn) vbe_head = TAILQ_HEAD_INITIALIZER(vbe_head);
static MTX vbemtx;
/*--------------------------------------------------------------------*/
static struct vbe_conn *
vbe_new_conn(void)
{
struct vbe_conn *vbc;
vbc = calloc(sizeof *vbc, 1);
if (vbc == NULL)
return (NULL);
VSL_stats->n_vbe_conn++;
vbc->magic = VBE_CONN_MAGIC;
vbc->fd = -1;
return (vbc);
}
/*--------------------------------------------------------------------
* XXX: There is a race here, we need to lock the replacement of the
* XXX: resolved addresses, or some other thread might try to access
* XXX: them while/during/after we changed them.
* XXX: preferably, we should make a copy to the vbe while we hold a
* XXX: lock anyway.
*/
static void
vbe_lookup(struct backend *bp)
{
struct addrinfo *res, hint, *old;
int error;
memset(&hint, 0, sizeof hint);
hint.ai_family = PF_UNSPEC;
hint.ai_socktype = SOCK_STREAM;
res = NULL;
error = getaddrinfo(bp->hostname,
bp->portname == NULL ? "http" : bp->portname,
&hint, &res);
bp->dnstime = TIM_mono();
if (error) {
if (res != NULL)
freeaddrinfo(res);
printf("getaddrinfo: %s\n", gai_strerror(error)); /* XXX */
return;
}
old = bp->addr;
bp->last_addr = res;
bp->addr = res;
if (old != NULL)
freeaddrinfo(old);
}
/*--------------------------------------------------------------------*/
static int
vbe_sock_conn(const struct addrinfo *ai)
{
int s;
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s >= 0 && connect(s, ai->ai_addr, ai->ai_addrlen)) {
AZ(close(s));
s = -1;
}
return (s);
}
/*--------------------------------------------------------------------*/
static int
vbe_conn_try(struct backend *bp, struct addrinfo **pai)
{
struct addrinfo *ai;
int s;
/* First try the cached good address, and any following it */
for (ai = bp->last_addr; ai != NULL; ai = ai->ai_next) {
s = vbe_sock_conn(ai);
if (s >= 0) {
bp->last_addr = ai;
*pai = ai;
return (s);
}
}
/* Then try the list until the cached last good address */
for (ai = bp->addr; ai != bp->last_addr; ai = ai->ai_next) {
s = vbe_sock_conn(ai);
if (s >= 0) {
bp->last_addr = ai;
*pai = ai;
return (s);
}
}
if (bp->dnstime + bp->dnsttl >= TIM_mono())
return (-1);
/* Then do another lookup to catch DNS changes */
vbe_lookup(bp);
/* And try the entire list */
for (ai = bp->addr; ai != NULL; ai = ai->ai_next) {
s = vbe_sock_conn(ai);
if (s >= 0) {
bp->last_addr = ai;
*pai = ai;
return (s);
}
}
return (-1);
}
static int
vbe_connect(struct sess *sp, struct backend *bp)
{
int s;
char abuf1[TCP_ADDRBUFSIZE], abuf2[TCP_ADDRBUFSIZE];
char pbuf1[TCP_PORTBUFSIZE], pbuf2[TCP_PORTBUFSIZE];
struct addrinfo *ai;
CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
AN(bp->hostname);
s = vbe_conn_try(bp, &ai);
if (s < 0)
return (s);
TCP_myname(s, abuf1, sizeof abuf1, pbuf1, sizeof pbuf1);
TCP_name(ai->ai_addr, ai->ai_addrlen,
abuf2, sizeof abuf2, pbuf2, sizeof pbuf2);
WSL(sp->wrk, SLT_BackendOpen, s, "%s %s %s %s %s",
bp->vcl_name, abuf1, pbuf1, abuf2, pbuf2);
return (s);
}
/* Get a backend connection ------------------------------------------
*
* Try all cached backend connections for this backend, and use the
* first one that is looks like it is still connected.
* If that fails to get us a connection, create a new one, reusing a
* connection from the freelist, if possible.
*
* This function is slightly complicated by optimizations on vbemtx.
*/
static struct vbe_conn *
vbe_nextfd(struct sess *sp)
{
struct vbe_conn *vc, *vc2;
struct pollfd pfd;
struct backend *bp;
int reuse = 0;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
bp = sp->backend;
CHECK_OBJ_NOTNULL(bp, BACKEND_MAGIC);
vc2 = NULL;
while (1) {
LOCK(&vbemtx);
vc = TAILQ_FIRST(&bp->connlist);
if (vc != NULL) {
assert(vc->backend == bp);
assert(vc->fd >= 0);
TAILQ_REMOVE(&bp->connlist, vc, list);
} else {
vc2 = TAILQ_FIRST(&vbe_head);
if (vc2 != NULL) {
VSL_stats->backend_unused--;
TAILQ_REMOVE(&vbe_head, vc2, list);
}
}
UNLOCK(&vbemtx);
if (vc == NULL)
break;
/* Test the connection for remote close before we use it */
pfd.fd = vc->fd;
pfd.events = POLLIN;
pfd.revents = 0;
if (!poll(&pfd, 1, 0)) {
reuse = 1;
break;
}
VBE_ClosedFd(sp->wrk, vc);
}
if (vc == NULL) {
if (vc2 == NULL)
vc = vbe_new_conn();
else
vc = vc2;
if (vc != NULL) {
assert(vc->fd == -1);
AZ(vc->backend);
vc->fd = vbe_connect(sp, bp);
if (vc->fd < 0) {
LOCK(&vbemtx);
TAILQ_INSERT_HEAD(&vbe_head, vc, list);
VSL_stats->backend_unused++;
UNLOCK(&vbemtx);
vc = NULL;
} else {
vc->backend = bp;
}
}
}
LOCK(&vbemtx);
if (vc != NULL ) {
VSL_stats->backend_reuse += reuse;
VSL_stats->backend_conn++;
} else {
VSL_stats->backend_fail++;
}
UNLOCK(&vbemtx);
if (vc != NULL ) {
WSL(sp->wrk, SLT_BackendXID, vc->fd, "%u", sp->xid);
assert(vc->fd >= 0);
assert(vc->backend == bp);
}
return (vc);
}
/*--------------------------------------------------------------------*/
static struct vbe_conn *
bes_GetFd(struct sess *sp)
{
struct vbe_conn *vc;
unsigned n;
for (n = 1; n < 5; n++) {
vc = vbe_nextfd(sp);
if (vc != NULL) {
WSL(sp->wrk, SLT_Backend, sp->fd, "%d %s", vc->fd,
sp->backend->vcl_name);
return (vc);
}
usleep(100000 * n);
}
return (NULL);
}
/* Close a connection ------------------------------------------------*/
static void
bes_ClosedFd(struct worker *w, struct vbe_conn *vc)
{
CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
assert(vc->fd >= 0);
AN(vc->backend);
WSL(w, SLT_BackendClose, vc->fd, "%s", vc->backend->vcl_name);
AZ(close(vc->fd));
vc->fd = -1;
vc->backend = NULL;
LOCK(&vbemtx);
TAILQ_INSERT_HEAD(&vbe_head, vc, list);
VSL_stats->backend_unused++;
UNLOCK(&vbemtx);
}
/* Recycle a connection ----------------------------------------------*/
static void
bes_RecycleFd(struct worker *w, struct vbe_conn *vc)
{
CHECK_OBJ_NOTNULL(vc, VBE_CONN_MAGIC);
assert(vc->fd >= 0);
AN(vc->backend);
WSL(w, SLT_BackendReuse, vc->fd, "%s", vc->backend->vcl_name);
LOCK(&vbemtx);
VSL_stats->backend_recycle++;
TAILQ_INSERT_HEAD(&vc->backend->connlist, vc, list);
UNLOCK(&vbemtx);
}
/*--------------------------------------------------------------------*/
static void
bes_Init(void)
{
MTX_INIT(&vbemtx);
}
/*--------------------------------------------------------------------*/
struct backend_method backend_method_simple = {
.name = "simple",
.getfd = bes_GetFd,
.close = bes_ClosedFd,
.recycle = bes_RecycleFd,
.init = bes_Init
};
......@@ -540,6 +540,8 @@ VRT_init_simple_backend(struct backend **bp, struct vrt_simple_backend *t)
b->hostname = strdup(t->host);
XXXAN(b->hostname);
b->method = &backend_method_simple;
*bp = b;
}
......
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