Descripción del Requisito
En un escenario de arquitectura de microservicios, se presentó la necesidad de desarrollar un sistema con dos componentes principales: un centro de distribución de tareas y un ejecutor de tareas. El centro de distribución se encarga de descomponer tareas complejas y distribuirlas entre múltiples ejecutores. Ambos componentes deben admitir la expansión horizontal para manejar cargas crecientes.
El desafío técnico surge de la restricción de no poder desplegar un centro de registro independiente debido a limitaciones de hardware del cliente y complejidad de implementación. En lugar de soluciones tradicionales como Zookeeper, Nacos, etcd, Consul o Eureka como servicios separados, se requería integrar las funciones de registro directamente en el centro de distribución.
Implementación de la Solución
Para satisfacer este requisito, se optó por implementar un centro de registro simplificado dentro del propio centro de distribución de tareas. Aunque no sería tan completo como Nacos, debía incluir funcionalidades esenciales:
- Detección de servicios: Almacenar y gestionar información de direcciones de servicios disponibles
- Registro de servicios: Permitir que los proveedores de servicios registren su información de dirección
- Verificación de salud: Realizar chequeos periódicos para garantizar la disponibilidad de servicios
- Escalabilidad horizontal: Soportar múltiples instancias del centro de ditsribución con sincronización adecuada
Integración del Servidor Eureka en el Centro de Distribución
Dado el conocimiento previo con Eureka, se decidió utilizar esta tecnología como base para implementar las funciones de registro. Aunque Eureka Server típicamente se despliega como un servicio independiente, en este caso se integró directamente en el centro de distribución de tareas.
La implemantación del servidor Eureka sigue los pasos estándar:
@SpringBootApplication
@EnableEurekaServer
public class CentroDistribucionApplication {
public static void main(String[] args) {
SpringApplication.run(CentroDistribucionApplication.class, args);
}
}
La configuración correspondiente en application.properties:
server.port=8761
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.server.enable-self-preservation=false
Integración del Cliente Eureka en el Centro de Distribución
Para permitir que el centro de distribución pueda descubrir y comunicarse con los ejecutores de tareas, se configuró también como cliente Eureka. Esto permitía al centro de distribución obtener la lista de instancias de ejecutores disponibles y distribuir las tareas mediante equilibrio de carga.
Primero, se agregó la dependencia de OpenFeign para facilitar las llamadas entre servicios:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Luego, se habilitaron las anotaciones necesarias en la aplicación principal:
@SpringBootApplication
@EnableEurekaServer
@EnableEurekaClient
@EnableFeignClients
public class CentroDistribucionApplication {
public static void main(String[] args) {
SpringApplication.run(CentroDistribucionApplication.class, args);
}
}
Se definió una interfaz Feign para comunicarse con el servicio de ejecución:
@FeignClient("EJECUTOR-TAREAS")
public interface ServicioEjecutor {
@GetMapping("/tareas/ejecutar")
RespuestaEjecucion ejecutarTarea(@RequestParam("idTarea") Long idTarea);
}
Finalmente, se implementó el endpoint principal en el controlador del centro de distribución:
@RestController
@RequestMapping("/distribucion")
public class ControladorDistribucion {
private static final Logger LOGGER = LoggerFactory.getLogger(ControladorDistribucion.class);
@Autowired
private ServicioEjecutor servicioEjecutor;
@GetMapping("/trabajar")
public String distribuirTrabajo(@RequestParam("idTrabajo") Long idTrabajo) {
LOGGER.info("Recibida solicitud de ejecución para trabajo [id={}]", idTrabajo);
LOGGER.info("Descomponiendo trabajo [id={}]", idTrabajo);
List<Long> idsTareas = descomponerTrabajo(idTrabajo);
LOGGER.info("Trabajo [id={}] descompuesto en tareas [{}]", idTrabajo, idsTareas);
for (Long idTarea : idsTareas) {
RespuestaEjecucion resultado = servicioEjecutor.ejecutarTarea(idTarea);
LOGGER.info("Resultado de ejecución de tarea [{}]: {}", idTarea, resultado);
}
return "Completado";
}
private List<Long> descomponerTrabajo(Long idTrabajo) {
// Lógica de descomposición de trabajo
return Arrays.asList(123L, 456L, 789L);
}
}
La configuración del cliente Eureka se ajustó para permitir el registro y descubrimiento de servicios:
spring.application.name=centro-distribucion
server.port=8080
eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.fetch-registry=true
eureka.client.register-with-eureka=true
Verificación Funcional
Con múltiples instancias del ejecutor de tareas en ejecución, se verificó que el centro de distribución correctamente distribuía las tareas entre las diferentes instancias mediante equilibrio de carga. Cada instancia de ejecutor registraba su estado en el centro de distribución, permitiendo un monitoreo adecuado y una distribución eficiente de las tareas.
Conclusiones
- Siempre que sea posible, se recomienda desplegar los centros de registro como servicios independientes, sin mezclar con lógica de negocio
- En casos donde las restricciones lo impidan, es viable integrar funcionalidades de servidor y cliente Eureka en un mismo servicio li>La experimentación con diferentes enfoques técnicos puede revelar soluciones viables para problemas aparentemente complejos