BLOG

Programación-Tecnología

Zuul, como edge service, proporciona un punto de entrada a nuestro ecosistema de microservicios, proporcionando capacidades de enrutamiento dinámico, seguridad y monitorización de las llamadas que se realicen, lo que le convierte en una buena solución para implementar el patrón API Gateway.

Patrón API Gateway

Chris Richardson ofrece una descripción muy interesante de este patrón que puedes leer en detalle en su artículo. En resumen, se identifica que algunos de los principales problemas a los que nos enfrentamos en arquitecturas orientadas a microservicios son:

  • El número de instancias de servicios y su localización varia dinámicamente
  • La granularidad de las APIs ofrecidas por los microservicios suele ser diferente a la que los clientes realmente requieren
  • Cada cliente necesita datos diferentes
  • El rendimiento de la red varia en función de los tipos de clientes
  • La granularidad de los servicios puede variar en el tiempo, aspecto que debería ser totalmente trasparente para los clientes.

La implementación de un servicio en el lado servidor (API Gateway) responsable de la agregación de datos y enrutamiento de peticiones a los servicios que correspondan, podría ser una buena solución para intentar mitigar los problemas anteriormente descritos

API Gateway pattern

En este artículo veremos como utilizar Spring Cloud y Netflix OSS Zuul para implementar el patrón API Gateway.

Arquitectura de la solución

El siguiente diagrama muestra los servicios que formarán parte del ejercicio práctico que vamos a realizar:

Arquitectura tecnica API Gateway

La infraestructura de solución técnica consta de:

  • Service Discovery (Netflix Eureka): Permite a los servicios registrarse en tiempo de ejecución, facilitando su localización a los consumidores
  • Dynamic Routing y Load Balancer (Netflix Ribbon): Se comunicará con Eureka para obtener información de localización de los servicios. Como balanceador de carga, si Ribbon encontrará más de una instancia disponible, distribuirá las peticiones de la manera más óptima.
  • Edge Service (Netflix Zuul): Actúa como un proxy inverso, proporcionando un punto de entrada a los servicios del sistema. Zuul utiliza Ribbon para localizar dichos servicios y conseguir enrutar una llamada externa a una instancia concreta de un microservicio el ejercicio. ¡Manos a la obra!

Construcción del Service Discovery

Aunque en el post Hello World Eureka ya expliqué de forma detallada cómo implementar un servicio de registro y descubrimiento con Eureka, resumo los pasos fundamentales:

  • Paso 1: Crea un nuevo proyecto maven. Puedes nombrarlo como ServiceDiscovery
  • Paso 2: Configura el fichero pom.xml del proyecto. Aquí tienes un ejemplo
  • Paso 3: Crea la clase principal de la aplicación Spring Boot, añadiendo la anotación @EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class ServiceDiscoveryApp {

	public static void main(String[] args) {

		SpringApplication.run(ServiceDiscoveryApp.class, args);
	}
}
  • Paso 4: Establece las propiedades del proyecto  en el fichero application.yml, entre ellas el puerto donde el servidor escuchará las peticiones.
  • Paso 5: Ejecuta la aplicación y comprueba que accedes al dashboard de Eureka, indicando en la URL el dominio y puerto que estableciste en paso anterior. En mi ejemplo sería https://localhost:1111/

Construcción del Greeting Service

La lógica de ejecución de este microservicio será muy sencilla. Vía REST recibirá un parámetro de entrada con el nombre de una persona y devolverá un saludo.

  • Paso 1: Nuevo proyecto maven que en mi caso  lo nombro como GreetingService
  • Paso 2: Configura el pom.xml del proyecto tal y como se indica en este ejemplo
  • Paso 3: Crea la clase principal de la aplicación y añade la anotación @EnableDiscoveryClient para que el servicio se  registre en Eureka
@EnableAutoConfiguration 
@EnableDiscoveryClient 
@SpringBootApplication
public class GreetingServiceApp {
	
	public static void main(String[] args) {	
	
		SpringApplication.run(GreetingServiceApp.class, args);
	}
} 
  • Paso 4: Crea un REST Controller para atender las peticiones que se reciban a través de la URI /greeting/{name}:
 @RestController
public class GreetingServiceController {
	
	private static final String template = "Hello, %s!";
			
	@RequestMapping("/greeting/{name}")
    public String greeting2(@PathVariable("name") String name) {
        return String.format(template, name) ;		
    }
}
  • Paso 5: Configura el puerto donde el servicio escuchará peticiones y las propiedades de acceso a Eureka tal y como aquí se indica
  • Paso 6: Ejecuta la aplicación y comprueba que Greeting Service aparece registrado en la consola de Eureka

Registro Greeting-Service

Construcción del Edge Service 

A continuación vamos a implementar el API Gateway con Zuul para facilitar el consumo vía REST del microservicio Greeting Service.

  • Paso 1: Crea un nuevo proyecto Maven. En mi caso EdgeService
  • Paso 2: Configura el fichero pom.xml de proyecto y asegúrate que entre las dependencias se incluye:
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>	
<dependency>	
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

Aquí puedes encontrar un ejemplo completo con la configuración de este fichero

  • Paso 3: Crea la clase principal de la aplicación Spring Boot y añade las anotaciones @EnableZuulProxy para habilitar Zuul y @EnableDiscoveryClient para conectar con el Service Discovery (Eureka)
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class EdgeServiceApp {
	public static void main(String[] args) {

		SpringApplication.run(EdgeServiceApp.class, args); 
	}
}

Paso 4: Establece las siguientes propiedades en el fichero application.yml del proyecto:

  • Mapeo de rutas: en el ejemplo indico que todas las peticiones de entrada que lleguen al Edge Service con la uri /greeting/** se deriven al endpoint del servicio cuyo identificador de registro en Eureka es greeting-service
zuul:
  routes:
    greetings:
      path: /greeting/**
      serviceId: GREETING-SERVICE
      stripPrefix: false
  • Acceso al Service Discovery y el puerto donde estará levantado el servidor
# Discovery Server Access
eureka:
  client:
    registerWithEureka: false
    serviceUrl:
      defaultZone: https://localhost:1111/eureka/

server:
  port: 8080   # HTTP (Tomcat) port

 

  • Paso 5: Ejecutar la aplicación y comprobar que funciona correctamente. Para verificarlo haremos una petición a https://localhost:8080/greeting/Rob que será capturada por Zuul, el cual, a través de Ribbon, llamará a Eureka para obtener la URL de publicación de Greeting Service. A continuación se enrutará el flujo de la ejecución hacia el nuevo endpoint obtenido y se devolverá un resultado a la petición original

edge service result

Filtros

Además de las capacidades de enrutado que hemos visto, Zuul ofrece muchas otras funcionalidades para sacarle partido a nuestro Edge Service, como por ejemplo los filtros, los cuales permiten realizar un amplio número de acciones durante el ciclo de vida de las peticiones HTTP.

Existen cuatro tipos distintos de filtros, correspondientes a cada uno de los estados por los que pasa una petición: PRE, ROUTING, POST, ERROR

Request LifecycleVamos a implementar un filtro de tipo PRE para que nos proporcione información de la petición que se ha hecho. Debido a su sencillez, utilizaré la implementación propuesta por Kasper Nissen en su fantástico post.

  • Paso 1: Crea una nueva clase que extienda de ZuulFilter
public class PreFilter extends ZuulFilter {
    private static Logger log = LoggerFactory.getLogger(PreFilter.class);

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
        return null;
    }
}
  • Paso 2: Añade un nuevo bean del filtro en la clase principal de la aplicación
public class EdgeServiceApp {

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

	@Bean
	public PreFilter preFilter() {
		return new PreFilter();
	}
}
  • Paso 3: Inicia de nuevo el Edge Service, realiza una petición y comprueba que el filtro PreFilter se ha ejecutado, escribiendo por la consola de salida la siguiente traza de log:
2016-09-03 16:36:37.656  INFO 2522 --- [nio-8080-exec-4] n.r.m.zuul.filters.PreFilter : GET request to https://localhost:8080/greeting/Rob

Con esto finalizamos el ejercicio práctico. Si quieres, puedes descargar el código fuente completo desde mi cuenta de GitHub

Referencias:

Comparte este artículo si te gustó:

12 Responses

  1. Excelente Post, una pregunta, puedo usar Zuul para enrutamiento estatico sin descubrimiento (Eureka), actualmente cuento con muchos servicios Rest funcionando en produccion, lo que deseo es poner una capa de proxy y enrutamiento con Zuul, no quiero descubrir servicios, sino manualmente establecer el enrutamiento a url definidas.
    Puedes mencionar un ejemplo completo.
    Gracias en avanzada.

  2. Muy buen tutorial, he seguido varios de diferentes fuentes y por ejemplo con el código localizado en GITHUB al momento de ejecutar los servicios me sale el siguiente error:

    Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: GREETING-SERVICE

    Esto pasa en los primeros segundos casi llegando al minuto, después todo funciona correctamente.

    Me podrian indicar que puede ser este error y como solucionarlo ?

  3. Hola Roberto,

    Estoy en el proceso de una implementación de una arquitectura basada en microservicios, utlizando laravel como backend y angular como frontend, podria utilizar zuul como api gateway o que me recomendarias utilizar en esta implementación?

    Saludos

  4. Blog a favoritos. De entrada se nota la calidad de los temas tratados. Espero sigas escribiendo muchas más entradas como ésta.

  5. Hola

    Yo creo que ha de ser por que aquí se menciona configurar zuul con la dependencia de Eureka Server lo cual es incorrecto. Debería ser un Eureka Client.

    Saludos

  6. Hola Roberto,

    Interesante tu articulo, pero tengo una consulta, en este articulo mencionas de como crear un API Gateway con rutas dinamicas, y aqui mi confusión.

    En el archivo de configuración, se registro el path del servicio REST, pero si tengo 10 micro servicios (Producto, Compra, Catalogo, Cotización, etc. etc..) debo colocar el path de todos en el archivo de recursos del API Gateway, tal como lo hiciste con el servicio greeting

    path: /greeting/**
    serviceId: GREETING-SERVICE

    Yo me hubiera imaginado, que el API Gateway, reciba un nombre de cualquier servicio y luego vaya a consultar al servicio de registros Eureka de cual es su PATH y luego redireccione la solicitud al servicio REST final. ¿Es posible hacer esto sin utilizar el PATH en duro en el archivo de recursos?

    Gracias

    • Hola Oscar! efectivamente el gateway está escuchando en un path concreto que se especifica como routa en el fichero de configuración. Si recibe una petición para esa ruta, preguntaría a Eureka por el identificador del microservicio que quiere localizar, el cual le devolverá el endpoint a que redirigiría el flujo de la petición
      Saludos

    • Hola Erick

      En el fichero de configuración se establece, por cada microservicio, los path donde Zuul va a estar escuchando peticiones y la ruta donde debe redirigir la petición. Esta última ruta, la puedes configurar directamente en el fichero de configuración o bien solicitársela a Eureka a través de un identificador de servicio, que es justamente el modo con el que he realizado el ejemplo

      Muchas gracias por tu comentario!

      Saludos

  7. Hola Roberto, muy didacticos e interesantes tus articulos. Estoy en mis pininos en microservicios y siguiendo los post que publicaste lo voy implementando. Agradecere mucho me puedas ayudar con este caso.

    Ya tengo montado spring-cloud-config, eureka, zuul. Cuando los ejecuto en windows no tengo inconvenientes funcionan correctamente. Sin embargo cuando lo ejecuton en linux (ubunto 16.x, en DigitalOcean), spring-cloud-config levanta correctamente, eureka levanta correctamente, cuando levanto zuul le hace un kill a eureka y viceversa, si levanto zuul cuando levanto eureka le hace un kill a zuul. No encuentro el origen del problema, si me pudieras dar una luz te lo agradecere.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.