En esta sección explicaremos qué compone y cómo cambiar la configuración estándar proporcionada por Ribbon.

Configuración avanzada de Ribbon

La configuración por defecto de Ribbon (integrado con Eureka) se pueden ver en la clase RibbonClientConfiguration. Esta clase define los siguientes beans (entre paréntesis se incluye la interfaz que implementan):

En nuestro caso queremos cambiar dicha configuración para poder probar diferentes reglas de balanceo, para ello deberemos crear nuestra propia clase de configuración para sobreescribir los beans deseados. A continuación se muestra un ejemplo de configuración en la que se indica que la regla de balanceo esté compuesta por RetryRule y WeightedResponseTimeRule que serán explicadas en apartados posteriores:

MicroS4 5

Esta clase de configuración se puede utilizar para sobreescribir los beans que queramos de los previamente listados.

Para poder utilizar esta configuración deberemos indicarlo en nuestra clase main por medio de la anotación @RibbonClient como se muestra en la siguiente imagen:

MicroS4 6

Reglas de balanceo

MicroS4 7 390

Como se puede ver existen dos zonas: defaultZone en la que se encuentran eureka, customer-backend y una instancia de security, e ireland compuesto por dos instancias de security. Así el cluster de instancias de security cuenta con una instancia en defaultZone y dos en ireland.

Como se ha mencionado en un post anterior sobre Eureka, la zona de un microservicio se define con la propiedad eureka.instance.metadataMap.zone.

A continuación se explican las diferentes reglas disponibles y su funcionamiento:

La lógica de esta regla se basa en descartar zonas según la disponibilidad de las instancias de microservicio en dicha zona. Los dos criterios de descarte implementados son el porcentaje de instancias no disponibles y el número de peticiones activas por servidor.

En esta situación dicha zona será descartada y las instancias disponibles serán las que existan en las restantes zonas. Para escoger cual de esas instancias será la invocada se utilizará el algoritmo RoundRobin.

Así por ejemplo, en una situación como la de la anterior imagen suponiendo que la instancia de defaultRegion sea descartada, se aplicará un RoundRobin sobre las dos instancias de la zona ireland.

En una situación en la que tuviésemos una zona adicional japan y ésta fuese descartada por el motivo que fuera, el algoritmo RoundRobin se aplicaría sobre las tres instancias disponibles en las zonas defaultZone e ireland.

% block:blockquote
% items:
% text:IMPORTANTE: hay un detalle a tener en cuenta a la hora de utilizar este filtro. Si partimos de una configuración por defecto no podremos hacerlo funcionar debido a que la lógica inicialmente diseñada por Netflix es ligeramente diferente a la utilizada por el OSS (sistemas de soporte a las operaciones) Spring Cloud-Netflix. Esto se explica posteriormente en el apartado de ZonePreferenceServerListFilter de la sección ‘Balanceador y filtros de balanceado’.
% endblock

Esta regla es la más sencilla de todas. Aplicará el conocido algoritmo RoundRobin que alternará las peticiones entre las diferentes instancias disponibles.

Esta regla está basada en el tiempo de respuesta medio de la instancia. A cada instancia se le asignará un peso en función de su tiempo medio de respuesta. Cuanto mayor sea el tiempo, menor peso tendrá la instancia. La instancia se elige de forma aleatoria entre las posibles con sus valores ajustados de forma acorde al peso.

El ajuste de los pesos se realiza cada 30 segundos en base a las estadísticas de peticiones, por lo que este proceso no se realiza para cada petición.

El proceso de cálculo de pesos consiste en sumar los tiempos medios de todas las instancias y a partir de ese valor el peso se calcula como:

% block:blockquote
% items:
% text:(suma del tiempo medio de todas las instancias) - (tiempo medio de la instancia)
% endblock

Por lo que a tiempo medio más bajo, se tendrá más peso. Una vez asignados los pesos se sumará el valor de los mismos. A cada instancia se le asignará un rango entre cero y la suma de los pesos igual al valor de su peso. Como paso final se generará un número aleatorio y según en qué rango se sitúe, la instancia que corresponda será la elegida.

Durante las primeras peticiones al no haber estadísticas de tiempos medios de respuesta y por tanto no haber pesos disponibles se utiliza el algoritmo RoundRobin.

% block:blockquote
% items:
% text:Ejemplo:
Supongamos que tenemos tres servidores con los siguientes tiempos medios A = 0.25s, B = 0.35s, C = 0.75s
Se calcula la suma total de los tiempos siendo un total de 1.35s
Se adjudican los pesos A = 1.1, B = 1.0, C = 0.6 y la suma de los mismos será 2.7
El cálculo de los rangos adjudicados a cada instancia será: A = [0, 1.1], B = (1.1, 2.1] y C (2.1, 2.7]
% endblock

Como se puede ver en el ejemplo cuanto menor sea el tiempo medio de respuesta de una instancia, mayor será su peso y por tanto su rango. De esta forma la instancia con mejor tiempo medio de respuesta será la que tenga más opciones de ser elegida, pero el factor aleatorio permite que también la instancia más lenta sea la elegida; en la proporción en que se diferencia su tiempo medio del de las demás.

Hay también que resaltar que como consecuencia de este algoritmo dos instancias con un tiempo medio muy similar tendrán unas posibilidades muy similares de ser elegidas, así como el hecho de que si una instancia tiene un tiempo medio muy alto será muy difícil que salga elegida (para el ejemplo anterior si la instancia C tuviese un tiempo medio de 5s nos encontraríamos con unos pesos A = 5.35, B = 5.25, C = 0.6 de forma que cualquiera de las otras dos instancias tiene casi diez veces más posibilidades de ser escogida).

Esta regla añade lógica de reintentos a cualquiera de las otras reglas existentes. Se puede utilizar conjuntamente con cualquiera de las otras reglas disponibles como se muestra en la siguiente configuración:

MicroS4 8

En caso de no indicar ninguna otra regla, RetryRule utiliza por defecto la RoundRobinRule. Otro parámetro que nos permite configurar esta regla es un timeout para reintentos de forma que se intentará encontrar una instancia válida durante el periodo determinado por ese timeout. En caso de no definirse, el valor por defecto es de 500 milisegundos.

Hay que tener en cuenta que esta política de reintento es para localizar una instancia, por lo que aunque nuestra petición retorne un error si el estado de la instancia en Eureka es correcto no se aplicará política de reintento, ya que dicha política es para localizar una instancia válida, no para reintento de peticiones.

Como ejemplo, si quisiéramos invocar al microservicio security y tuviésemos una situación como la de la imagen donde la instancia está registrada pero está caída:

MicroS4 9

La política de reintento intentaría recuperar una instancia con estado UP durante el timeout que hayamos definido. En caso de no encontrarlo lógicamente la petición fallaría.

Esta situación la hemos provocado con la siguiente implementación en nuestro microservicio security y configurando la propiedad eureka.client.healthcheck.enabled=true tal y como se explicó en un post anterior sobre Eureka:

MicroS4 10

Implementar nuestra propia regla

Otra posibilidad que siempre está presente es, en caso de que ninguno de las reglas de balanceo nos satisfaga, implementar la nuestra propia. A modo de ejemplo hemos implementado una regla basada en el concepto de la WeightedResponseTimeRule, solo que en este caso en vez de asignar pesos en base al tiempo medio de respuesta y escoger el servidor aleatoriamente en base a esos pesos, escogeremos directamente el servidor que tenga el mejor tiempo de respuesta. A continuación se puede ver el código fuente de dicha regla:

MicroS4 11

Esta regla se ha implementado con finalidades didácticas pero no sería válida para un entorno productivo real, ya que podría provocar que ciertas instancias no fuesen elegibles en un largo periodo de tiempo.

Supongamos por ejemplo que tenemos una instancia A y estamos arrancando la segunda, B. Si a la instancia B se le deriva una petición y se produce un pequeño retardo de red, o la instancia no ha tenido una petición de warmup y por tanto tarda más en responder o cualquier otro tipo de situación, su tiempo medio de respuesta estaría compuesto únicamente por el tiempo de esa petición, que en este caso sería muy alto.

Esto provocaría que la instancia A, al tener mejor tiempo medio de respuesta, fuese siempre la elegida y se le enviasen todas las peticiones hasta que por sobrecarga de las mismas llegase a tener un tiempo medio de respuesta mayor al de B. En ese momento se empezarían a derivar peticiones a la instancia B.

Obviamente esto nos supone una situación no deseada, ya que en vez de realizar un balanceo lógico entre las dos instancias de forma que la carga de peticiones sea compartida entre las dos, lo que ocurre es que una instancia es ‘descartada’ hasta que la otra tiene un comportamiento tan malo como ésta, momento en el cual se cambian y la nueva empiece a recibir todas las peticiones hasta que tenga un comportamiento peor que la primera.

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.