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
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
Arrancamos la aplicación
Atacamos al controlador /ping/rest/ribbon
Resultado
Alive!