1. Introducción

En el siguiente post veremos como utilizar Ribbon sin Eureka. En la entrada Spring Cloud – Eureka + Ribbon + Feign vimos como realizar el balanceo de la carga haciendo uso de Ribbon. Éste se apoyaba en Eureka para hacer el descubrimiento de instancias.

Sin embargo Spring Cloud permite que el descubrimiento pueda realizarse sin utilizar Eureka. Para ello debemos:

  • Utilizar la anotación @RibbonClient para definir un nuevo cliente que nos permita realizar el balanceo
  • Establecer las máquinas-puerto asociadas a dicho cliente

Os podéis descargar el código de ejemplo de mi GitHub aquí.

Tecnologías empleadas:

  • Java 8
  • Gradle 3.1
  • SpringBoot 1.5.2.RELEASE
  • Spring 4.3.7.RELEASE

2. Ficheros

Debemos incluir las siguientes dependencias en el build.gradle

  • spring-boot-starter-web
  • spring-cloud-starter-ribbon
group 'com.jorgehernandezramirez.spring.springboot'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'spring-boot'
apply plugin: 'io.spring.dependency-management'

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR6'
    }
}

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springcloud.zuul.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-ribbon')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

Main que arranca SpringBoot

package com.jorgehernandezramirez.spring.springboot.ribbon;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public Application(){
        //For Spring
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Hacemos uso de la anotación @RibbonClient para configurar el cliente Ribbon. Por otro lado creamos en el contexto un objeto de la clase RestTemplate. Utilizamos la anotación @LoadBalanced para que utilice Ribbon con el fin de llevar a cabo el descubrimiento de instancias.

package com.jorgehernandezramirez.spring.springboot.ribbon.configuration;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
@RibbonClient(name = "springcloudwebtest")
public class RibbonConfiguration {

    @LoadBalanced
    @Bean
    public RestTemplate buildRestTemplate(){
        return new RestTemplate();
    }
}

En el fichero application.yml indicamos que no vamos a utilizar Ribbon para el cliente springcloudwebtest así como la lista de instancias asociadas.

server:
  port: 8080

springcloudwebtest:
  ribbon:
    eureka:
      enabled: false
    listOfServers: localhost:8080
    ServerListRefreshInterval: 15000

Creamos un controlador de test para realizar las peticiones.

package com.jorgehernandezramirez.spring.springboot.ribbon.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping("/ping")
public class TestController {

    @Autowired
    private RestTemplate restTemplate;

    public TestController(){
        //For Spring
    }

    @RequestMapping
    public String doAlive() {
        return "Alive!";
    }

    @RequestMapping("/rest")
    public String doRestAlive() {
        return new RestTemplate().getForObject("http://localhost:8080/ping", String.class);
    }

    @RequestMapping("/rest/ribbon")
    public String doRestAliveUsingEurekaAndRibbon() {
        return restTemplate.getForObject("http://springcloudwebtest/ping", String.class);
    }
}

3. Testeando la aplicación. SpringBootTest

Vamos a utilizar SpringBootTest para testear nuestra aplicación.

Incluimos la dependencia spring-boot-starter-test

...
dependencies {
    ...
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

Utilizamos SpringBootTest para testear nuestra aplicación. Para ello deberemos

  • Utilizar la anotación @SpringBootTest para arrancar SpringBoot en el test.
  • Inyectar el objeto TestRestTemplate para hacer invocaciones a nuestros controladores
package com.jorgehernandezramirez.spring.springboot.ribbon;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import static org.junit.Assert.assertEquals;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT)
public class RibbonWithoutEurekaTest {

    @Autowired
    private TestRestTemplate testRestTemplate;

    @Test
    public void shouldReturnAliveFromPingUrl() throws Exception {
        final String pingResult = testRestTemplate.getForEntity("/ping", String.class).getBody();
        assertEquals("Alive!", pingResult);
    }

    @Test
    public void shouldReturnAliveFromPingRestUrl() throws Exception {
        final String pingRestResult = testRestTemplate.getForEntity("/ping/rest", String.class).getBody();
        assertEquals("Alive!", pingRestResult);
    }

    @Test
    public void shouldReturnAliveFromPingRestRibbonUrl() throws Exception {
        final String pingRestResult = testRestTemplate.getForEntity("/ping/rest/ribbon", String.class).getBody();
        assertEquals("Alive!", pingRestResult);
    }
}

4. Testando la aplicación. Levantando la aplicación

A continuación compilaremos nuestros fuentes y arrancaremos SpringBoot para testear nuestros controladores

Compilamos

gradle clean build

Arrancamos la aplicación

java -jar [JAR_EN_BUILD_LIBS]

Atacamos al controlador /ping/rest/ribbon

curl http://localhost:8080/ping/rest/ribbon

Resultado

Alive!