You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
5.0 KiB

/*
* mod_balancer - balance between different backends
*
* Description:
* mod_balancer balances between different backends;
*
* Setups:
* none
* Options:
* none
* Actions:
* balance.rr <actions> - balance between actions (list or single action)
*
* Example config:
* balance.rr { fastcgi "127.0.0.1:9090"; };
*
* Todo:
* - add some select function (current: always take first)
* - support backlogs
*
* Author:
* Copyright (c) 2009 Stefan Bühler
*/
#include <lighttpd/base.h>
LI_API gboolean mod_balancer_init(liModules *mods, liModule *mod);
LI_API gboolean mod_balancer_free(liModules *mods, liModule *mod);
typedef enum {
BE_ALIVE,
BE_OVERLOADED,
BE_DOWN,
BE_DOWN_RETRY
} backend_state;
typedef enum {
BAL_ALIVE,
BAL_OVERLOADED,
BAL_DOWN
} balancer_state;
struct backend {
liAction *act;
guint load;
backend_state state;
};
typedef struct backend backend;
struct balancer {
GArray *backends;
balancer_state state;
};
typedef struct balancer balancer;
static balancer* balancer_new() {
balancer *b = g_slice_new(balancer);
b->backends = g_array_new(FALSE, TRUE, sizeof(backend));
return b;
}
static void balancer_free(liServer *srv, balancer *b) {
guint i;
if (!b) return;
for (i = 0; i < b->backends->len; i++) {
backend *be = &g_array_index(b->backends, backend, i);
li_action_release(srv, be->act);
}
g_array_free(b->backends, TRUE);
g_slice_free(balancer, b);
}
static gboolean balancer_fill_backends(balancer *b, liServer *srv, liValue *val) {
if (val->type == LI_VALUE_ACTION) {
backend be = { val->data.val_action.action, 0, BE_ALIVE };
assert(srv == val->data.val_action.srv);
li_action_acquire(be.act);
g_array_append_val(b->backends, be);
return TRUE;
} else if (val->type == LI_VALUE_LIST) {
guint i;
if (val->data.list->len == 0) {
ERROR(srv, "%s", "expected non-empty list");
return FALSE;
}
for (i = 0; i < val->data.list->len; i++) {
liValue *oa = g_array_index(val->data.list, liValue*, i);
if (oa->type != LI_VALUE_ACTION) {
ERROR(srv, "expected action at entry %u of list, got %s", i, li_value_type_string(oa->type));
return FALSE;
}
assert(srv == oa->data.val_action.srv);
{
backend be = { oa->data.val_action.action, 0, BE_ALIVE };
li_action_acquire(be.act);
g_array_append_val(b->backends, be);
}
}
return TRUE;
} else {
ERROR(srv, "expected list, got %s", li_value_type_string(val->type));
return FALSE;
}
}
static liHandlerResult balancer_act_select(liVRequest *vr, gboolean backlog_provided, gpointer param, gpointer *context) {
balancer *b = (balancer*) param;
gint be_ndx = 0;
backend *be = &g_array_index(b->backends, backend, be_ndx);
UNUSED(backlog_provided);
/* TODO implement some selection algorithms */
be->load++;
li_action_enter(vr, be->act);
*context = GINT_TO_POINTER(be_ndx);
return LI_HANDLER_GO_ON;
}
static liHandlerResult balancer_act_fallback(liVRequest *vr, gboolean backlog_provided, gpointer param, gpointer *context, liBackendError error) {
balancer *b = (balancer*) param;
gint be_ndx = GPOINTER_TO_INT(*context);
backend *be = &g_array_index(b->backends, backend, be_ndx);
UNUSED(backlog_provided);
UNUSED(error);
if (be_ndx < 0) return LI_HANDLER_GO_ON;
/* TODO implement fallback/backlog */
be->load--;
*context = GINT_TO_POINTER(-1);
li_vrequest_backend_error(vr, error);
return LI_HANDLER_GO_ON;
}
static liHandlerResult balancer_act_finished(liVRequest *vr, gpointer param, gpointer context) {
balancer *b = (balancer*) param;
gint be_ndx = GPOINTER_TO_INT(context);
backend *be = &g_array_index(b->backends, backend, be_ndx);
UNUSED(vr);
if (be_ndx < 0) return LI_HANDLER_GO_ON;
/* TODO implement backlog */
be->load--;
return LI_HANDLER_GO_ON;
}
static void balancer_act_free(liServer *srv, gpointer param) {
balancer_free(srv, (balancer*) param);
}
static liAction* balancer_rr(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) {
balancer *b;
liAction *a;
UNUSED(p); UNUSED(userdata);
if (!val) {
ERROR(srv, "%s", "need parameter");
return NULL;
}
b = balancer_new();
if (!balancer_fill_backends(b, srv, val)) {
balancer_free(srv, b);
return NULL;
}
a = li_action_new_balancer(balancer_act_select, balancer_act_fallback, balancer_act_finished, balancer_act_free, b, TRUE);
return a;
}
static const liPluginOption options[] = {
{ NULL, 0, NULL, NULL, NULL }
};
static const liPluginAction actions[] = {
{ "balancer.rr", balancer_rr, NULL },
{ NULL, NULL, NULL }
};
static const liPluginSetup setups[] = {
{ NULL, NULL, NULL }
};
static void plugin_init(liServer *srv, liPlugin *p, gpointer userdata) {
UNUSED(srv); UNUSED(userdata);
p->options = options;
p->actions = actions;
p->setups = setups;
}
gboolean mod_balancer_init(liModules *mods, liModule *mod) {
MODULE_VERSION_CHECK(mods);
mod->config = li_plugin_register(mods->main, "mod_balancer", plugin_init, NULL);
return mod->config != NULL;
}
gboolean mod_balancer_free(liModules *mods, liModule *mod) {
if (mod->config)
li_plugin_free(mods->main, mod->config);
return TRUE;
}