Eureka y openFeign para la comunicación entre Microservicios

Eureka y OpenFeign

Eureka será la encargada de saber qué microservicios están disponibles, los registrará y de esta manera un microservicio podrá preguntar a Eureka y localizar el resto de microservicios

OpenFeign nos va a permitir realizar peticiones entre microservicios de una forma muy sencilla. Además permitirá que cada cliente tenga su propio LoadBalancer

Estructura

Microservicios

Para comprender el concepto veremos un ejemplo de proyecto son solo 2 microservicios

El primer microservicio es Events, que contendrá la información de eventos de una aplicación en la que la gente se puede apuntar a diferentes eventos(deportivos, de hobbies…)

Por otra parte Bookings contendrá las reservas realizadas por los usuarios, los cuales pueden reservar un sitio para un evento

Situación

Pongámonos en la situación de que un usuario quiere reservar una plaza para un evento, al crear una reserva(booking) necesitaremos avisar al microservicio Events para que pueda tener un conteo del número de personas que se han apuntado

Descubrimiento con Eureka

Crear servidor Eureka

En primer lugar será necesario crear un nuevo proyecto con la dependencia de ==Eureka Server==


Luego, en el application.yml de este proyecto habrá que especificar la url donde se encontrará este servidor Eureka

server:  
  port: 8071  
  
spring:  
  application:  
    name: "eurekaserver"  

eureka:  
  instance:  
    hostname: localhost  
  client:  
    fetchRegistry: false  
    registerWithEureka: false  
    serviceUrl:  
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  • fetchRegistry: Si se activa entonces se obtendrá el registro de otros servicios(cosa que no le interesa, solo le interesa a los microservicios que se quieran comunicar)
  • registerWithEureka: Al activarse, la instancia se registrará en el servidor de eureka, y no tiene sentido registrar eureka en el propio eureka.
  • serviceUrl.defaultZone: Por último especificamos la url donde se encontrará el servidor Eureka.

Por último añadimos la anotación @EnableEurekaServer en la clase principal del proyecto.

@SpringBootApplication  
@EnableEurekaServer  
public class EurekaserverApplication {  
  
    public static void main(String[] args) {  
       SpringApplication.run(EurekaserverApplication.class, args);  
    }  
  
}

Resultado

Accediendo a http://localhost:8071/ ya podemos ver Eureka funcionando:

Y obviamente aún no tenemos ninguna instancia registrada.

Conectar Microservicios con Eureka

Habiendo terminado con el servidor Eureka, ahora seguimos con los microservicios, los cuales se tienen que registrar en el servidor Eureka.

En primer lugar, los microservicios(en este caso Events y Bookings) requieren de la dependencia eureka client:

Así que la añadimos al pom.xml :

<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>  
</dependency>  

Eso sí, también es necesario añadir el dependencyManagement:

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

El spring-cloud.version se especifica en las properties:

<spring-cloud.version>2023.0.3</spring-cloud.version>

En caso de cualquier duda, ve al initilizr, elige la dependencia, clicka en Explore, y a partir de aquí copia la dependencia, el dependencyManagement y el spring-cloud.version como acabamos de ver.


En segundo lugar, cada microservicio necesita saber donde se encuentra el servidor Eureka, por lo que habrá que señalarlo en el application.yml:

server:  
  port: 8090  
  
spring:  
  application:  
    name: "booking"
    
eureka:  
  instance:  
    preferIpAddress: true  
  client:  
    fetchRegistry: true  
    registerWithEureka: true  
    serviceUrl:  
      defaultZone: http://localhost:8071/eureka/

Realizaremos lo mismo en el microservicio Events

Resultado

Ya está toda la configuración del descubrimiento terminada, a partir de aquí cada microservicio puede ser conocedor del paradero de otro microservicio gracias a Eureka!!

De hecho, podemos ver que al iniciar los microservicios, estos se enceuntran registrados en Eureka:

OpenFeign

Ahora Bookings ya sabe donde se encuentraEvents, solo queda que se comunique con él.

Aquí es cuando entra OpenFeign

Y solo necesitamos configurar OpenFeign en el microservicio que realiza la petición(Bookings), el que recibe la petición(Events) no requiere ningún cambio

Configuración

El microservicio Bookings quiere avisar a Events de que va crear una nueva reserva, pero quiere avisarle justo antes de crear esta reserva para el evento

En resumen, Bookings quiere ejecutar un método de Events concretamente el que se encarga de aumentar en una unidad la cuenta de reservas de un evento: EventController.java:

@PutMapping("/update/currentBookingsCount")  
public ResponseEntity<ResponseDto> updateCurrentBookingsCount(@Valid @RequestParam Long eventId) {  
  
    boolean isUpdated = iEventService.increaseCurrentBookings(eventId);  
  
    if (isUpdated)  
        return ResponseEntity  
                .status(OK)  
                .body(new ResponseDto(STATUS_200, MESSAGE_200));  
    else  
        return ResponseEntity  
                .status(EXPECTATION_FAILED)  
                .body(new ResponseDto(STATUS_417, MESSAGE_417_UPDATE));  
}

Vale, pero ¿Cómo puede el MS Bookings ejecutar ese endpoint?

  • En primer lugar hay que añadir la dependencia de openFeign.
  • Y en segundo lugar añadir la anotación @EnableFeignClients encima de la clase principal del microservicio.
<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-starter-openfeign</artifactId>  
</dependency>

Y para que Bookings pueda ejecutar ese endpoint del microservicio Events tiene que crear una interfaz FeignClient, esta puede ser creada dentro de la carpeta services, y la llamaremos EventsFeignClient

@FeignClient(name = "event")  
public interface EventsFeignClient {  
  
    @PutMapping(value = "/api/update/currentBookingsCount")  
    public ResponseEntity<ResponseDto> updateCurrentBookingsCount(@RequestParam Long eventId);  
}

No hace falta hacer nada más, ya que FeignClient tiene un enfoque declarativo, al igual que JPA, es decir, que por detrás genera automáticamente el código necesario para realizar la petición. Nosotros le indicamos solamente el nombre del servicio(el que está suscrito en eureka) y el método del controlador que queremos ejecutar


Así es como conseguimos comunicarnos con el microservicio Events, y lo podemos hacer desde cualquier parte del código, solo hay que inyectar la dependencia(El Bean) y realizar la llamada:

@Override  
public void createBooking(BookingDto bookingDto) {  
  
    Optional<Booking> existingBooking = bookingRepository.findByUserIdAndServiceId(bookingDto.getUserId(), bookingDto.getEventId());  
  
    if (existingBooking.isPresent())  
        throw new BookingAlreadyExistsException("The book with serviceId: " +  
                bookingDto.getEventId() + " and userId: " +  
                bookingDto.getUserId() + " already exists");  
  
    Booking booking = BookingMapper.mapToBooking(bookingDto, new Booking());  
    ResponseEntity<ResponseDto> responseDtoResponseEntity = eventsFeignClient.updateCurrentBookingsCount(bookingDto.getEventId());  
  
    if (responseDtoResponseEntity.getStatusCode() == HttpStatus.OK)  
        bookingRepository.save(booking);  
  
}

De este modo, solo se creará la reserva si hay espacio suficiente para ese evento y si el microservicio Events nos devuelve un HTTP Status = OK (200)