De momento, en el LVS existen 10 algoritmos de balanceo de carga, de los cuales ya comenté brevemente. Voy a volver a listarlos, agregando el archivo en el cual se encuentran definidos:
- Planificación por Round-Robin (Round-Robin Scheduling). Definido en: ip_vs_rr.c
- Planificación por Round-Robin ponderado (Weighted Round-Robin Scheduling). Definido en: ip_vs_wrr.c
- Planificación por menor número de conexiones (Least-Connection Scheduling). Definido en: ip_vs_lc.c
- Planificación por menor número de conexiones ponderado (Weighted Least-Connection Scheduling). Definido en: ip_vs_wlc.c
- Planificación por menor número de conexiones local (Locality-Based Least-Connection Scheduling). Definido en: ip_vs_lblc.c
- Planificación por menor número de conexiones local con réplicas (Locality-Based Least-Connection with Replication Scheduling). Definido en: ip_vs_lblcr.c
- Planificación por hashing de destino (Destination Hashing Scheduling). Definido en: ip_vs_dh.c
- Planificación por hashing de origen (Source Hashing Scheduling). Definido en: ip_vs_sh.c
- Planificación por menor retardo esperado (Shortest Expected Delay Scheduling). Definido en: ip_vs_sed.c
- Planificación por servidores sin peticiones en espera (Never Queue Scheduling). Definido en: ip_vs_nq.c
Cualquiera de estos algoritmos se puede escoger para realizar el balanceo en un sistema basado en LVS, a través de ipvsadm, la herramienta de administración para cónsola del LVS. Probablemente los más comunes sean el algoritmo de Round-Robin o el de Round-Robin ponderado (aunque no necesariamente sean los mejores para todas las situaciones).
Si queremos realizar nuestro propio algoritmo de balanceo, o queremos modificar alguno de los ya existentes, debemos empezar por estudiar la estructura que tienen o deben tener.
Recordemos que estos programas están diseñados para ser utilizados como módulos del kernel de Linux, por ello, todos deben tener al menos dos funciones:
static int __init_ip_vs_XXX_init(void);
Es la función que se ejecuta cuando se carga el módulo. Entre otras cosas, acá se registran los módulos.
static int __exit_ip_vs_XXX_cleanup(void);
Es la función que se ejecuta cuando se descarga el módulo.
Sobra decir que en las definiciones anteriores, XXX se sustituye por el nombre del módulo (rr, wrr, wlc, etc). Adicionalmente, en todo algoritmo de balanceo, se define un struct de tipo ip_vs_scheduler, en el cual se asignan las propiedades (atributos y funciones) que definen el comportamiento del módulo. La estructura de estos, es la siguiente:
struct ip_vs_scheduler { struct list_head n_list; /* d-linked list head */ char *name; /* scheduler name */ atomic_t refcnt; /* reference counter */ struct module *module; /* THIS_MODULE/NULL */ /* scheduler initializing service */ int (*init_service)(struct ip_vs_service *svc); /* scheduling service finish */ int (*done_service)(struct ip_vs_service *svc); /* scheduler updating service */ int (*update_service)(struct ip_vs_service *svc); /* selecting a server from the given service */ struct ip_vs_dest* (*schedule)(struct ip_vs_service *svc, const struct sk_buff *skb); };
Las funciones que definen el comportamiento del algoritmo son:
- init_service: se ejecuta en el momento en que se asocia un servicio con el algoritmo de planificación. Es opcional, no todos los algoritmos requieren realizar alguna tarea especial al iniciar el servicio. Ejemplo de uso: el algoritmo de round-robin, utiliza la función init_service para inicializar la data que utilizará para el balanceo (sched_data), con la lista de servidores reales actuales (destinations).
- update_service: se ejecuta al actualizar o eliminar alguno de los destinos. Es opcional. Ejemplo de uso: el algoritmo de round-robin utiliza esta función para actualizar la data que utiliza para el balanceo (sched_data), con la lista de servidores reales actuales (destinations).
- done_service: se ejecuta al finalizar el servicio. Es opcional. Ejemplo de uso: el algoritmo de round-robin ponderado utiliza la función done_service para liberar la memoria ocupada por la data utilizada para el balanceo (sched_data).
- schedule: esta es la función en donde se define el algoritmo de balanceo per se. Es la parte más importante del módulo, y por supuesto no es opcional. Devuelve un apuntador a un objeto de tipo ip_vs_dest, que indica cuál es el servidor que el algoritmo escogió para responder la petición realizada por el cliente.
Y bien, estos son todos los elementos que necesitamos conocer para escribir un algoritmo de balanceo o modificar alguno de los existentes. Recomiendo comenzar estudiando los algoritmos de balanceo por menor número de conexiones y por round-robin, pues son los más cortos (no pasa de 120 líneas ninguno de los dos) y sencillos de entender.