Por razones de seguridad, los navegadores prohíben las llamadas AJAX a recursos fuera del origen actual. El intercambio de recursos de origen cruzado (CORS) es una especificación W3C, implementada por la mayoría de navegadores, que permite especificar qué tipo de solicitudes están autorizadas.
Para solventar este problema es suficiente con añadir la siguiente configuración:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
Para habilitar las solicitudes de origen cruzado es necesario tener alguna
configuración CORS explícitamente declarada. El HandlerMapping de Spring
MVC proporciona soporte para el CORS. Cada HandlerMapping puede ser
configurado individualmente. Después de mapear con éxito una petición a
un handler, se comprueba la configuración del CORS, la cual es
interceptada y validada. Se puede combinar la configuración CORS global a nivel de HandlerMapping
con una configuración más fina a nivel de clase o de método, por ejemplo,
con la anotación @CrossOrigin. En el ejemplo anterior, se hace uso de una configuración global de CORS.
Aclarar que, con allowedOrigins se debe establecer uno o más dominios
específicos. Para simplificarlo se hace uso de *, pero no es lo adecuado. De esta sencilla forma permitimos consumir nuestros servicios desde otros dominios.
registry.addMapping("/**") .allowedMethods("*") .allowedOrigins("*") .maxAge(3600); } }
Introducción al desarrollo de
microservicios
Qué son
La arquitectura de microservicios (en inglés, Microservices Architecture,
MSA) consiste en construir una aplicación como un conjunto de pequeños
servicios, los cuales se ejecutan en su propio proceso y se comunican con
mecanismos ligeros (normalmente una API de recursos HTTP). Cada
microservicio se encarga de implementar una funcionalidad completa del
negocio y es desplegado de forma independiente pudiendo estar
programado en distintos lenguajes y usar diferentes tecnologías de
almacenamiento de datos. La arquitectura de microservicios es una manera de construir sistemas de
software descomponiendo los modelos de dominio de negocio en contextos
más pequeños, consistentes y delimitados, implementados por los servicios.
Estos servicios son aislados y autónomos, pero se comunican para
proporcionar alguna funcionalidad de negocio. Los microservicios suelen ser
implementados y operados por pequeños equipos con suficiente autonomía
para que cada equipo y servicio pueda cambiar sus detalles de
implementación interna con un impacto mínimo en el resto del sistema. Estos equipos se comunican a través de promesas, que son una forma en
que un servicio puede publicar intenciones para otros componentes o
sistemas que pueden desear utilizar el servicio. Especifican estas promesas
con interfaces de sus servicios o mediante wikis que documentan sus
servicios. Los microservicios nos permiten: ● Comprender lo que hace el servicio sin enredarse con otras
preocupaciones de una aplicación más grande. ● Construir rápidamente el servicio localmente.
● Elegir la tecnología adecuada para el problema concreto. ● Testear el servicio concreto de manera aislada.
● Construir/implementar/lanzar el servicio cuando sea necesario para el negocio, que puede ser independiente de otros servicios.
● Identificar y escalar horizontalmente partes de la arquitectura donde sea necesario.
● Mejorar la resiliencia del sistema en su conjunto.
Los microservicios ayudan a desacoplar nuestros servicios y equipos para escalarlos rápidamente. Permiten a los equipos concentrarse en brindar el servicio y realizar cambios cuando sea necesario y hacerlo sin costosos puntos de sincronización.
Los microservicios tienen muchos beneficios pero vienen con sus propios inconvenientes:
● Requieren más recursos.
● La complejidad operativa es mucho mayor. ● Es más difícil depurar los problemas.
● Es difícil comprender el sistema de manera integral.
● Es imprescindible diseñar el sistema de gestión de errores.
Patrones de los microservicios
Como hemos visto, las arquitecturas de microservicios ofrecen importantes
ventajas pero también plantean retos que deben ser resueltos. Para
ayudarnos en una correcta implementación de los mismos, han ido
surgiendo diferentes patrones o recetas que nos ayudan a no cometer los
mismos errores que otros han sufrido previamente, a la hora de abordar los
problemas comunes que suelen aparecer. Existen multitud de patrones
pero hemos seleccionado algunos de los más relevantes para este documento.
Service Discovery
Actualmente, gran parte de las aplicaciones requieren de un API para
funcionar, dicha API ofrece endpoints para que la aplicación pueda
interactuar con el backend. Uno de los grandes problemas, dada la gran
cantidad de servicios, es conocer donde está alojado dicho servicio, ya que
cada uno responde a una dirección y puerto específico y esto se acentúa en
arquitecturas Cloud donde pueden cambiar dinámicamente de IP o puerto,
ya sea por fallos o por gestionar réplicas según la demanda de nuestra
aplicación; es aquí donde entra en juego el patrón Service Discovery. Entonces, ¿por qué se considera una mala práctica las hard-coded URLs? ● Los cambios requieren modificaciones del código. ● Al hacer un despliegue, por ejemplo en Heroku, te encontrarás con
URLs dinámicas que van cambiando continuamente. ● Si un servicio tiene mucha demanda se puede replicar, cada uno
tendrá su propia URL entonces necesitas un mecanismo que se
encargue del balanceo de carga. ● Despliegues en múltiples entornos complican el manejo de diferentes
URLs. Por todas estas razones, necesitamos el Service Discovery, un patrón para
microservicios que nos permite invocar servicios sin conocer su ubicación física.
Imagínate que disponemos de tres servicios que se van a consumir. El
primer paso es añadir una capa de abstracción, Discovery Server, que se
encargará de saber donde están alojados dichos servicios y así proveer al
cliente la URL para posteriormente hacer la llamada. Así, el cliente
conocerá en todo momento la existencia de estos: 1. Cada servicio que quiera ser descubierto se registra en el Discovery
Server. 2. El cliente solicita la URL de un servicio a consumir. 3. El Discovery Server provee la dirección de dicho servicio. 4. El cliente realiza la llamada. Existen dos modelos de descubrimiento de servicios: ● Si de la tarea de descubrir el servicio se encarga mayoritariamente el
cliente, estaremos hablando de Client side discovery (modelo visto
anteriormente). Éste es el modelo que utiliza Spring Cloud. ● En cambio, una alternativa es que sea el cliente quien pase un
mensaje al Discovery server y éste sea el encargado de transmitirlo al
servicio adecuado.
Ambos modelos son válidos, cada uno tiene sus propias ventajas y desventajas.
En caso de que un microservicio deba acceder a otro, lo ideal sería que de alguna manera pudiera saber en qué direcciones están las instancias de ese otro microservicio funcionando.
Para ello, en Spring se utiliza Eureka Server del paquete Spring Cloud Netflix. Estos últimos crearon un montón de librerías aplicables a nuestro ecosistema como pueden ser Ribbon, Hystrix, Zuul, Feign, etc.
Debemos especificar a los clientes que no se guarden en su caché local las direcciones de las diferentes instancias mediante la propiedad eureka.client.fetch-registry=false, esto es para que consulte al servidor Eureka cada vez que necesite acceder a un servicio. En un entorno de producción, a menudo se pone a true para agilizar las peticiones.
Por otra parte, los microservicios serán Eureka clients registrados en el Eureka Server.
Además, si utilizamos los paquetes Ribbon y Feign conseguiremos que nuestra aplicación sea capaz de encontrar las diferentes instancias de un microservicio y balancear las peticiones de carga. En el siguiente enlace podréis consultar la implementación de un caso práctico sobre Feign.