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
@RibbonClientpara 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-webspring-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
Utilizamos SpringBootTest para testear nuestra aplicación. Para ello deberemos
- Utilizar la anotación
@SpringBootTestpara arrancar SpringBoot en el test. - Inyectar el objeto
TestRestTemplatepara 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
Arrancamos la aplicación
Atacamos al controlador /ping/rest/ribbon
Resultado
Alive!