Guía para Spring Cloud Task: microservicios Spring Boot de corta duración

En este artículo, nos sumergiremos en Spring Cloud Task, que se utiliza para crear microservicios de corta duración en el ecosistema Java/Spring.

Introducción

Los microservicios se están desarrollando a nuestro alrededor hoy en día. Muchos de estos servicios son de corta duración. Las tareas programadas, la sincronización de datos, la agregación de datos, la generación de informes y servicios similares son de corta duración. Por lo general, se espera que comiencen, se ejecuten hasta su finalización y finalicen.

Se han creado muchas aplicaciones y programadores externos para cumplir con este propósito, sin embargo, a veces necesita una tarea personalizada que requiere una integración profunda con la aplicación de su organización.

La plataforma Spring Boot pone esta funcionalidad a disposición de los desarrolladores a través de la Spring Cloud Task API.

¿Qué es Spring Cloud Task?

Normalmente, se espera que los servicios sean de larga duración. Un servicio Spring Boot promedio incluye un servidor web integrado como Tomcat, Jetty o Undertow. Un servicio finaliza porque se detuvo a propósito o porque ocurrió un error de tiempo de ejecución como un OOM (OutOfMemoryError).

Spring Boot se creó de esta manera, pero a medida que cambiaron los paradigmas y la arquitectura de microservicios se hizo popular, muchos servicios se volvieron efímeros. Esto fue excesivo ya que un servicio de notificación de corta duración no necesita tener un servidor incorporado y podría ser mucho más liviano.

Spring Cloud Task es la respuesta de Spring al problema de los microservicios de corta duración en Spring Boot.

Con Spring Cloud Task, obtiene un proceso JVM bajo demanda que realiza una tarea y finaliza inmediatamente.

En este artículo, vincularemos mucho al proyecto oficial Spring Cloud Task disponible en Github.

Arquitectura técnica de Spring Cloud Task

Spring Cloud Task utiliza algunas anotaciones para configurar el sistema y una base de datos (al menos para producción) para registrar el resultado de cada invocación.

Para convertir su aplicación Spring Boot en una tarea en la nube, debe anotar una de las clases de configuración de su aplicación con @EnableTask.

Esta anotación importa la [TaskLifecycleConfiguration](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org/springframework/cloud/ task/configuration/TaskLifecycleConfiguration.java) en su proyecto. La clase TaskLifecycleConfiguration es la clase de configuración para TaskLifecycleListener, TaskRepository y otras clases útiles necesarias para dar vida a todas las funcionalidades de Spring Cloud Task.

Implementación

Spring Initializr

Una buena manera de iniciar su proyecto básico Spring Boot es usar Spring Initializr. Seleccione su dependencia de base de datos preferida, la dependencia Spring Cloud Task y la dependencia Spring Data JPA:

spring initializr dependencies

Si ya tiene un proyecto en ejecución, usando Maven, agregue las dependencias adecuadas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-task</artifactId>
     <version>${version}</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>${version}</version>
</dependency>

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>${version}</version>
</dependency>

La dependencia spring-cloud-starter-task incluye spring-boot-starter, spring-cloud-task-core, spring-cloud-task-batch y spring-cloud-task-stream.

La dependencia spring-cloud-task-core es la principal que usaremos; puede importar spring-boot-starter y la dependencia anterior por separado.

Alternativamente, si estás usando Gradle:

1
compile group: 'org.springframework.cloud', name: 'spring-cloud-starter-task', version: '2.2.3.RELEASE'

Configuración de la aplicación

Para registrar una aplicación como Spring Cloud Task, debe anotar una de las clases de configuración con [@EnableTask](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring- nube-tarea-core/src/main/java/org/springframework/cloud/task/configuration/EnableTask.java):

1
2
3
4
5
6
7
@EnableTask
@SpringBootApplication
public class SampleSpringCloudTaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(SampleSpringCloudTaskApplication.class, args);
    }
}

Dado que la anotación @SpringBootApplication es una combinación de @EnableAutoConfiguration, @Configuration y @ComponentScan, está perfectamente bien anotar la clase principal con la anotación @EnableTask.

Repositorio de tareas

Se puede crear una tarea sin una base de datos. En ese caso, utiliza una instancia en memoria de H2 para administrar los eventos del repositorio de tareas. Esto está bien para el desarrollo, pero no para la producción. Para la producción, se recomienda una fuente de datos. Conserva todos los registros de las ejecuciones y errores de sus tareas.

Todos los eventos de tareas se conservan usando el [Repositorio de tareas](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org /springframework/cloud/task/repository/TaskRepository.java).

En un proyecto de fuente de datos única, Spring Cloud Task crea las tablas de tareas en la base de datos especificada.

Sin embargo, en un proyecto de múltiples fuentes de datos, debe elegir la fuente de datos para usar con Spring Cloud Task. Puede encontrar un ejemplo de un proyecto de múltiples fuentes de datos en los [Proyectos de muestra de Spring Cloud Task](https://github.com/spring-cloud/spring-cloud-task/tree/master/spring-cloud-task -muestras/múltiples fuentes de datos).

En el ejemplo, se especifican 2 fuentes de datos en la [Configuración de fuente de datos](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-samples/multiple- datasources/src/main/java/io/spring/configuration/DataSourceConfiguration.java) clase:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Configuration
public class DataSourceConfiguration {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .build();
    }

    @Bean
    public DataSource secondDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }
}

Para especificar la fuente de datos que utilizará Spring Cloud Task, un [Configurador de tareas personalizadas](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task -samples/multiple-datasources/src/main/java/io/spring/configuration/CustomTaskConfigurer.java) se crea el componente. Extiende DefaultTaskConfigurer, pasando la fuente de datos calificada en el constructor:

1
2
3
4
5
6
7
@Component
public class CustomTaskConfigurer extends DefaultTaskConfigurer {
    @Autowired
    public CustomTaskConfigurer(@Qualifier("secondDataSource") DataSource dataSource)  {
        super(dataSource);
    }
}

A menos que se especifique un ‘TaskRepository’ personalizado mediante la extensión de ‘SimpleTaskRepository’, el [SimpleTaskRepository](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/ src/main/java/org/springframework/cloud/task/repository/support/SimpleTaskRepository.java) es el TaskRepository predeterminado utilizado por Spring Cloud Task.

Configurador de tareas

El TaskConfigurer se utiliza para personalizar las configuraciones de Spring Cloud Task. DefaultTaskConfigurer es el configurador predeterminado que se utiliza si no se proporciona ningún configurador de tareas personalizado que implemente la interfaz TaskConfigurer.

[Configurador de tareas predeterminado](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org/springframework/cloud/task /configuration/DefaultTaskConfigurer.java) proporciona componentes basados ​​en mapas si no hay una fuente de datos disponible y componentes JDBC si se proporciona una fuente de datos.

Explorador de tareas

El TaskExplorer, como su nombre indica, es un explorador para la ejecución de tareas. Es útil para recopilar información de tareas real del repositorio de tareas.

De forma predeterminada, Spring Cloud Tasks usa el [SimpleTaskExplorer](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org/ springframework/cloud/task/repository/support/SimpleTaskExplorer.java).

Desde el TaskExplorer, puede consultar mucha información útil sobre las ejecuciones de tareas, como el recuento total de TaskExecutions, ejecutar TaskExecutions actualmente, encontrar todas las TaskExecutions, etc.

Ejecución de tareas

[Ejecución de tareas](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/ repository/TaskExecution.java) es el estado de la Tarea para cada Ejecución. Toda la información almacenada en TaskRepository se modela en esta clase. Es la unidad básica para cada tarea.

Parte de la información almacenada es

  • executionId - ID único asociado con la ejecución de la tarea.
  • exitcode - Código de salida registrado para la tarea.
  • taskName - Nombre definido por el usuario para la tarea.
  • startTime - Hora de inicio de la tarea.
  • endTime - Marca de tiempo de finalización de la tarea.
Ejecutar una tarea

Para ejecutar nuestra tarea, debemos implementar la interfaz Runner y proporcionarla como un bean en nuestra clase de configuración.

Por lo general, se implementa CommandLineRunner o ApplicationRunner:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Component
public class SampleCommandLineRunner implements CommandLineRunner {
    @Override
     public void run(String...args) throws Exception {
         System.out.println("Specified Task");
     }
}

    @Configuration
    public class TaskConfiguration {
     @Bean
     public SampleCommandLineRunner sampleCommandLineRunner() {
          return new SampleCommandLineRunner();
     }
}

Y con eso, desde un método principal, podemos llamar al SampleCommandLineRunner:

1
2
3
4
5
6
@SpringBootApplication
public class SomeApplication {
    public static void main(String[] args) {
        SpringApplication.run(SampleCommandLineRunner.class, args);
    }
}

TaskExecutionListener

Todas las TaskExecutions tienen un ciclo de vida. Spring Cloud Task registra estos eventos. Al comienzo de una tarea, antes de que se haya ejecutado cualquier implementación de la interfaz Runner, se crea una entrada en TaskRepository que registra el evento de inicio.

Cuando una tarea se completa o falla, se emite otro evento. [TaskExecutionListener](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org/springframework/cloud/task/listener/ TaskExecutionListener.java) le permite registrar oyentes para escuchar este evento durante todo el ciclo de vida.

Puede especificar tantos oyentes como desee para el mismo evento.

Spring proporciona dos enfoques para hacer esto: usar la interfaz TaskExecutionListener o el enfoque de anotación de bean de método.

Para el primero, proporciona un componente que implementa la interfaz TaskExecutionListener y sus tres métodos:

1
2
3
void  onTaskStartup(TaskExecution  taskExecution);
void  onTaskEnd(TaskExecution  taskExecution);
void  onTaskFailed(TaskExecution  taskExecution, Throwable  throwable);
  • onTaskStartup(): se invoca después de que TaskExecution se haya almacenado en TaskRepository.

  • onTaskEnd(): se invoca después de que TaskExecution se haya actualizado en TaskRepository, al finalizar la tarea.

  • onTaskFailed(): se invoca si se produce una excepción no detectada durante la ejecución de la tarea.

Por otro lado, utilizando el enfoque de anotación de bean de método, crea un componente y lo proporciona como un bean en su configuración de Spring:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
public class AppConfigurtion {

    @Bean
    public AppTaskListener appTaskListener() {
        return new AppTaskListener();
    }
}

@Component
class AppTaskListener {
    @BeforeTask
    public void beforeTaskInvocation(TaskExecution taskExecution) {
        System.out.println("Before task");
    }

    @AfterTask
    public void afterTaskInvocation(TaskExecution taskExecution) {
        System.out.println("After task");
    }

    @FailedTask
    public void afterFailedTaskInvocation(TaskExecution taskExecution, Throwable throwable) {
        System.out.println("Failed task");
    }
}
  • @BeforeTask es análogo a onTaskStartup().
  • @AfterTask es análogo a onTaskEnd().
  • @FailedTask es análogo a onTaskFailed().

Mensajes de salida de tareas

Aunque puede especificar cualquier cantidad de oyentes para un evento en particular, si un controlador de eventos TaskExecutionListener lanza una excepción, todo el procesamiento de oyentes para ese controlador de eventos se detiene.

Por ejemplo, si se han iniciado tres detectores onTaskStartup() y el primer controlador de eventos onTaskStartup() genera una excepción, no se llama a los otros dos métodos onTaskStartup.

Sin embargo, se llaman los otros controladores de eventos (onTaskEnd() y onTaskFailed()) para TaskExecutionListeners.

Cuando ocurren excepciones, TaskExecutionListener devuelve un código de salida y un mensaje de salida. Anotar un método con @AfterTask nos permitirá configurar el mensaje de salida:

1
2
3
4
    @AfterTask
    public void afterError(TaskExecution taskExecution) {
        taskExecution.setExitMessage("Custom Exit Message");
    }

Se puede configurar un Mensaje de salida en cualquiera de los eventos de escucha, aunque solo se configurarán los mensajes relevantes. Si una tarea se ejecuta correctamente, el evento onTaskFailed() no se activará. Cuando finaliza la tarea, se establece el mensaje del evento onTaskEnd().

Personalización de tareas en la nube

El TaskConfigurer puede anular muchas propiedades especificando valores personalizados en el archivo applications.properties o applications.yaml.

Las propiedades de la tarea Spring Cloud tienen el prefijo spring.cloud.task en el archivo applications.properties.

Algunas de las propiedades que se pueden anular son:

  • spring.cloud.task.tablePrefix: este es el prefijo de tabla para las tablas de tareas para TaskRepository. El prefijo de tabla predeterminado es "TASK_".
  • spring.cloud.task.initialize-enabled=false: se usa para habilitar o deshabilitar la creación de tablas de tareas al inicio de la tarea. Por defecto es verdadero.
  • spring.cloud.task.executionid=yourtaskId: se usa para configurar Spring Cloud Task para usar su ID de tarea personalizada. De forma predeterminada, Spring genera una identificación de ejecución de tarea para cada ejecución de tarea.

Para ver más propiedades personalizables, consulte [pedir propiedades](https://github.com/spring-cloud/spring-cloud-task/blob/master/spring-cloud-task-core/src/main/java/org /springframework/cloud/task/configuration/TaskProperties.java).

Registro de eventos de tareas en la nube

Por lo general, es útil durante el desarrollo para ver los registros de depuración de su aplicación. Para cambiar el nivel de registro de una Spring Cloud Task, agregue esto a su archivo applications.properties:

1
logging.level.org.springframework.cloud.task=DEBUG

Conclusión

En este artículo, presentamos Spring Cloud Task, qué es y los problemas que resuelve. También cubrimos ejemplos de cómo configurarlo con una fuente de datos y ejecutar una tarea con las interfaces Runner.

Además, explicamos la arquitectura Spring Cloud Task y todos los modelos de dominio como TaskExecution, TaskExecutionListener, etc. utilizados para lograr todas las funcionalidades.