455 lines
20 KiB
Plaintext
455 lines
20 KiB
Plaintext
|
|
[[features]]
|
|
= Features
|
|
|
|
[[partintro]]
|
|
--
|
|
This section goes into more detail about Spring Cloud Task, including how to use it, how
|
|
to configure it, and the appropriate extension points.
|
|
--
|
|
|
|
[[features-lifecycle]]
|
|
== The lifecycle of a Spring Cloud Task
|
|
|
|
In most cases, the modern cloud environment is designed around the execution of processes
|
|
that are not expected to end. If they do end, they are typically restarted. While most
|
|
platforms do have some way to run a process that is not restarted when it ends, the
|
|
results of that run are typically not maintained in a consumable way. Spring Cloud
|
|
Task offers the ability to execute short-lived processes in an environment and record the
|
|
results. Doing so allows for a microservices architecture around short-lived processes as
|
|
well as longer running services through the integration of tasks by messages.
|
|
|
|
While this functionality is useful in a cloud environment, the same issues can arise in a
|
|
traditional deployment model as well. When running Spring Boot applications with a
|
|
scheduler such as cron, it can be useful to be able to monitor the results of the
|
|
application after its completion.
|
|
|
|
Spring Cloud Task takes the approach that a Spring Boot application can have a start and
|
|
an end and still be successful. Batch applications are one example of how processes that
|
|
are expected to end (and that are often short-lived) can be helpful.
|
|
|
|
Spring Cloud Task records the lifecycle events of a given task. Most long-running
|
|
processes, typified by most web applications, do not save their lifecycle events. The
|
|
tasks at the heart of Spring Cloud Task do.
|
|
|
|
The lifecycle consists of a single task execution. This is a physical execution of a
|
|
Spring Boot application configured to be a task (that is, it has the Sprint Cloud Task dependencies).
|
|
|
|
At the beginning of a task, before any `CommandLineRunner` or `ApplicationRunner`
|
|
implementations have been run, an entry in the `TaskRepository` that records the start
|
|
event is created. This event is triggered through `SmartLifecycle#start` being triggered
|
|
by the Spring Framework. This indicates to the system that all beans are ready for use and
|
|
comes before running any of the `CommandLineRunner` or `ApplicationRunner` implementations
|
|
provided by Spring Boot.
|
|
|
|
NOTE: The recording of a task only occurs upon the successful bootstrapping of an
|
|
`ApplicationContext`. If the context fails to bootstrap at all, the task's run is not
|
|
recorded.
|
|
|
|
Upon completion of all of the `*Runner#run` calls from Spring Boot or the failure of an
|
|
`ApplicationContext` (indicated by an `ApplicationFailedEvent`), the task execution is
|
|
updated in the repository with the results.
|
|
|
|
NOTE: If the application requires the `ApplicationContext` to be closed at the
|
|
completion of a task (all `*Runner#run` methods have been called and the task
|
|
repository has been updated), set the property `spring.cloud.task.closecontextEnabled`
|
|
to true.
|
|
|
|
[[features-task-execution-details]]
|
|
=== The TaskExecution
|
|
|
|
The information stored in the `TaskRepository` is modeled in the `TaskExecution` class and
|
|
consists of the following information:
|
|
|
|
|===
|
|
|Field |Description
|
|
|
|
|`executionid`
|
|
|The unique ID for the task's run.
|
|
|
|
|`exitCode`
|
|
|The exit code generated from an `ExitCodeExceptionMapper` implementation. If there is no
|
|
exit code generated but an `ApplicationFailedEvent` is thrown, 1 is set. Otherwise, it is
|
|
assumed to be 0.
|
|
|
|
|`taskName`
|
|
|The name for the task, as determined by the configured `TaskNameResolver`.
|
|
|
|
|`startTime`
|
|
|The time the task was started, as indicated by the `SmartLifecycle#start` call.
|
|
|
|
|`endTime`
|
|
|The time the task was completed, as indicated by the `ApplicationReadyEvent`.
|
|
|
|
|`exitMessage`
|
|
|Any information available at the time of exit. This can programmatically be set by a
|
|
`TaskExecutionListener`.
|
|
|
|
|`errorMessage`
|
|
|If an exception is the cause of the end of the task (as indicated by an
|
|
`ApplicationFailedEvent`), the stack trace for that exception is stored here.
|
|
|
|
|`arguments`
|
|
|A `List` of the string command line arguments as they were passed into the executable
|
|
boot application.
|
|
|===
|
|
|
|
[[features-lifecycle-exit-codes]]
|
|
=== Mapping Exit Codes
|
|
|
|
When a task completes, it tries to return an exit code to the OS. If we take a look
|
|
at our xref:getting-started.adoc#getting-started-developing-first-task[original example], we can see that we are
|
|
not controlling that aspect of our application. So, if an exception is thrown, the JVM
|
|
returns a code that may or may not be of any use to you in debugging.
|
|
|
|
Consequently, Spring Boot provides an interface, `ExitCodeExceptionMapper`, that lets you
|
|
map uncaught exceptions to exit codes. Doing so lets you indicate, at the level of exit
|
|
codes, what went wrong. Also, by mapping exit codes in this manner, Spring Cloud Task
|
|
records the returned exit code.
|
|
|
|
If the task terminates with a SIG-INT or a SIG-TERM, the exit code is zero unless
|
|
otherwise specified within the code.
|
|
|
|
NOTE: While the task is running, the exit code is stored as a null in the repository.
|
|
Once the task completes, the appropriate exit code is stored based on the guidelines described
|
|
earlier in this section.
|
|
|
|
[[features-configuration]]
|
|
== Configuration
|
|
|
|
Spring Cloud Task provides a ready-to-use configuration, as defined in the
|
|
`DefaultTaskConfigurer` and `SimpleTaskConfiguration` classes. This section walks through
|
|
the defaults and how to customize Spring Cloud Task for your needs.
|
|
|
|
[[features-data-source]]
|
|
=== DataSource
|
|
|
|
Spring Cloud Task uses a datasource for storing the results of task executions. By
|
|
default, we provide an in-memory instance of H2 to provide a simple method of
|
|
bootstrapping development. However, in a production environment, you probably want to
|
|
configure your own `DataSource`.
|
|
|
|
If your application uses only a single `DataSource` and that serves as both your business
|
|
schema and the task repository, all you need to do is provide any `DataSource` (the
|
|
easiest way to do so is through Spring Boot's configuration conventions). This
|
|
`DataSource` is automatically used by Spring Cloud Task for the repository.
|
|
|
|
If your application uses more than one `DataSource`, you need to configure the task
|
|
repository with the appropriate `DataSource`. This customization can be done through an
|
|
implementation of `TaskConfigurer`.
|
|
|
|
[[features-table-prefix]]
|
|
=== Table Prefix
|
|
One modifiable property of `TaskRepository` is the table prefix for the task tables. By
|
|
default, they are all prefaced with `TASK_`. `TASK_EXECUTION` and `TASK_EXECUTION_PARAMS`
|
|
are two examples. However, there are potential reasons to modify this prefix. If the
|
|
schema name needs to be prepended to the table names or if more than one set of task
|
|
tables is needed within the same schema, you must change the table prefix. You can do so
|
|
by setting the `spring.cloud.task.tablePrefix` to the prefix you need, as follows:
|
|
|
|
`spring.cloud.task.tablePrefix=yourPrefix`
|
|
|
|
By using the `spring.cloud.task.tablePrefix`, a user assumes the responsibility to
|
|
create the task tables that meet both the criteria for the task table schema but
|
|
with modifications that are required for a user's business needs.
|
|
You can utilize the Spring Cloud Task Schema DDL as a guide when creating your own Task DDL as seen
|
|
https://github.com/spring-cloud/spring-cloud-task/tree/master/spring-cloud-task-core/src/main/resources/org/springframework/cloud/task[here].
|
|
|
|
|
|
[[features-table-initialization]]
|
|
=== Enable/Disable table initialization
|
|
In cases where you are creating the task tables and do not wish for Spring Cloud Task to
|
|
create them at task startup, set the `spring.cloud.task.initialize-enabled` property to
|
|
`false`, as follows:
|
|
|
|
`spring.cloud.task.initialize-enabled=false`
|
|
|
|
It defaults to `true`.
|
|
|
|
NOTE: The property `spring.cloud.task.initialize.enable` has been deprecated.
|
|
|
|
[[features-generated_task_id]]
|
|
=== Externally Generated Task ID
|
|
|
|
In some cases, you may want to allow for the time difference between when a task is
|
|
requested and when the infrastructure actually launches it. Spring Cloud Task lets you
|
|
create a `TaskExecution` when the task is requested. Then pass the execution ID of the
|
|
generated `TaskExecution` to the task so that it can update the `TaskExecution` through
|
|
the task's lifecycle.
|
|
|
|
A `TaskExecution` can be created by calling the `createTaskExecution` method on an
|
|
implementation of the `TaskRepository` that references the datastore that holds
|
|
the `TaskExecution` objects.
|
|
|
|
In order to configure your Task to use a generated `TaskExecutionId`, add the
|
|
following property:
|
|
|
|
`spring.cloud.task.executionid=yourtaskId`
|
|
|
|
[[features-external_task_id]]
|
|
=== External Task Id
|
|
|
|
Spring Cloud Task lets you store an external task ID for each
|
|
`TaskExecution`. In order to configure your Task to use a generated `TaskExecutionId`, add the
|
|
following property:
|
|
|
|
`spring.cloud.task.external-execution-id=<externalTaskId>`
|
|
|
|
[[features-parent_task_id]]
|
|
=== Parent Task Id
|
|
|
|
Spring Cloud Task lets you store a parent task ID for each `TaskExecution`. An example of
|
|
this would be a task that executes another task or tasks and you want to record which task
|
|
launched each of the child tasks. In order to configure your Task to set a parent
|
|
`TaskExecutionId` add the following property on the child task:
|
|
|
|
`spring.cloud.task.parent-execution-id=<parentExecutionTaskId>`
|
|
|
|
[[features-task-configurer]]
|
|
=== TaskConfigurer
|
|
|
|
The `TaskConfigurer` is a strategy interface that lets you customize the way components of
|
|
Spring Cloud Task are configured. By default, we provide the `DefaultTaskConfigurer` that
|
|
provides logical defaults: `Map`-based in-memory components (useful for development if no
|
|
`DataSource` is provided) and JDBC based components (useful if there is a `DataSource`
|
|
available).
|
|
|
|
The `TaskConfigurer` lets you configure three main components:
|
|
|
|
|===
|
|
|Component |Description |Default (provided by `DefaultTaskConfigurer`)
|
|
|
|
|`TaskRepository`
|
|
|The implementation of the `TaskRepository` to be used.
|
|
|`SimpleTaskRepository`
|
|
|
|
|`TaskExplorer`
|
|
|The implementation of the `TaskExplorer` (a component for read-only access to the task
|
|
repository) to be used.
|
|
|`SimpleTaskExplorer`
|
|
|
|
|`PlatformTransactionManager`
|
|
|A transaction manager to be used when running updates for tasks.
|
|
|`JdbcTransactionManager` if a `DataSource` is used.
|
|
`ResourcelessTransactionManager` if it is not.
|
|
|===
|
|
|
|
You can customize any of the components described in the preceding table by creating a
|
|
custom implementation of the `TaskConfigurer` interface. Typically, extending the
|
|
`DefaultTaskConfigurer` (which is provided if a `TaskConfigurer` is not found) and
|
|
overriding the required getter is sufficient. However, implementing your own from scratch
|
|
may be required.
|
|
|
|
NOTE: Users should not directly use getter methods from a `TaskConfigurer` directly
|
|
unless they are using it to supply implementations to be exposed as Spring Beans.
|
|
|
|
[[features-task-execution-listener]]
|
|
=== Task Execution Listener
|
|
|
|
`TaskExecutionListener` lets you register listeners for specific events that occur during
|
|
the task lifecycle. To do so, create a class that implements the
|
|
`TaskExecutionListener` interface. The class that implements the `TaskExecutionListener`
|
|
interface is notified of the following events:
|
|
|
|
* `onTaskStartup`: Prior to storing the `TaskExecution` into the `TaskRepository`.
|
|
* `onTaskEnd`: Prior to updating the `TaskExecution` entry in the `TaskRepository` and
|
|
marking the final state of the task.
|
|
* `onTaskFailed`: Prior to the `onTaskEnd` method being invoked when an unhandled
|
|
exception is thrown by the task.
|
|
|
|
Spring Cloud Task also lets you add `TaskExecution` Listeners to methods within a bean
|
|
by using the following method annotations:
|
|
|
|
* `@BeforeTask`: Prior to the storing the `TaskExecution` into the `TaskRepository`
|
|
* `@AfterTask`: Prior to the updating of the `TaskExecution` entry in the `TaskRepository`
|
|
marking the final state of the task.
|
|
* `@FailedTask`: Prior to the `@AfterTask` method being invoked when an unhandled
|
|
exception is thrown by the task.
|
|
|
|
The following example shows the three annotations in use:
|
|
|
|
[source,java]
|
|
----
|
|
public class MyBean {
|
|
|
|
@BeforeTask
|
|
public void methodA(TaskExecution taskExecution) {
|
|
}
|
|
|
|
@AfterTask
|
|
public void methodB(TaskExecution taskExecution) {
|
|
}
|
|
|
|
@FailedTask
|
|
public void methodC(TaskExecution taskExecution, Throwable throwable) {
|
|
}
|
|
}
|
|
----
|
|
|
|
NOTE: Inserting an `ApplicationListener` earlier in the chain than `TaskLifecycleListener` exists may cause unexpected effects.
|
|
|
|
[[features-task-execution-listener-Exceptions]]
|
|
==== Exceptions Thrown by Task Execution Listener
|
|
If an exception is thrown by a `TaskExecutionListener` event handler, all listener
|
|
processing for that event handler stops. For example, if three `onTaskStartup` listeners
|
|
have started and the first `onTaskStartup` event handler throws an exception, the other
|
|
two `onTaskStartup` methods are not called. However, the other event handlers (`onTaskEnd`
|
|
and `onTaskFailed`) for the `TaskExecutionListeners` are called.
|
|
|
|
The exit code returned when a exception is thrown by a `TaskExecutionListener`
|
|
event handler is the exit code that was reported by the
|
|
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/ExitCodeEvent.html[ExitCodeEvent].
|
|
If no `ExitCodeEvent` is emitted, the Exception thrown is evaluated to see
|
|
if it is of type
|
|
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-application-exit[ExitCodeGenerator].
|
|
If so, it returns the exit code from the `ExitCodeGenerator`. Otherwise, `1`
|
|
is returned.
|
|
|
|
In the case that an exception is thrown in an `onTaskStartup` method, the exit code for the application will be `1`.
|
|
If an exception is thrown in either a `onTaskEnd` or `onTaskFailed`
|
|
method, the exit code for the application will be the one established using the rules enumerated above.
|
|
|
|
NOTE: In the case of an exception being thrown in a `onTaskStartup`, `onTaskEnd`, or `onTaskFailed`
|
|
you can not override the exit code for the application using `ExitCodeExceptionMapper`.
|
|
|
|
|
|
|
|
[[features-task-execution-listener-exit-messages]]
|
|
==== Exit Messages
|
|
|
|
You can set the exit message for a task programmatically by using a
|
|
`TaskExecutionListener`. This is done by setting the `TaskExecution's` `exitMessage`,
|
|
which then gets passed into the `TaskExecutionListener`. The following example shows
|
|
a method that is annotated with the `@AfterTask` `ExecutionListener` :
|
|
|
|
[source,java]
|
|
@AfterTask
|
|
public void afterMe(TaskExecution taskExecution) {
|
|
taskExecution.setExitMessage("AFTER EXIT MESSAGE");
|
|
}
|
|
|
|
An `ExitMessage` can be set at any of the listener events (`onTaskStartup`,
|
|
`onTaskFailed`, and `onTaskEnd`). The order of precedence for the three listeners follows:
|
|
|
|
. `onTaskEnd`
|
|
. `onTaskFailed`
|
|
. `onTaskStartup`
|
|
|
|
For example, if you set an `exitMessage` for the `onTaskStartup` and `onTaskFailed`
|
|
listeners and the task ends without failing, the `exitMessage` from the `onTaskStartup`
|
|
is stored in the repository. Otherwise, if a failure occurs, the `exitMessage` from
|
|
the `onTaskFailed` is stored. Also if you set the `exitMessage` with an
|
|
`onTaskEnd` listener, the `exitMessage` from the `onTaskEnd` supersedes
|
|
the exit messages from both the `onTaskStartup` and `onTaskFailed`.
|
|
|
|
[[features-single-instance-enabled]]
|
|
=== Restricting Spring Cloud Task Instances
|
|
|
|
Spring Cloud Task lets you establish that only one task with a given task name can be run
|
|
at a time. To do so, you need to establish the <<features-task-name, task name>> and set
|
|
`spring.cloud.task.single-instance-enabled=true` for each task execution. While the first
|
|
task execution is running, any other time you try to run a task with the same
|
|
<<features-task-name, task name>> and `spring.cloud.task.single-instance-enabled=true`, the
|
|
task fails with the following error message: `Task with name "application" is already
|
|
running.` The default value for `spring.cloud.task.single-instance-enabled` is `false`. The
|
|
following example shows how to set `spring.cloud.task.single-instance-enabled` to `true`:
|
|
|
|
`spring.cloud.task.single-instance-enabled=true or false`
|
|
|
|
To use this feature, you must add the following Spring Integration dependencies to your
|
|
application:
|
|
|
|
[source,xml]
|
|
<dependency>
|
|
<groupId>org.springframework.integration</groupId>
|
|
<artifactId>spring-integration-core</artifactId>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>org.springframework.integration</groupId>
|
|
<artifactId>spring-integration-jdbc</artifactId>
|
|
</dependency>
|
|
|
|
NOTE: The exit code for the application will be 1 if the task fails because this feature
|
|
is enabled and another task is running with the same task name.
|
|
|
|
[[single-instance-usage-for-spring-aot-and-native-compilation]]
|
|
==== Single Instance Usage for Spring AOT And Native Compilation
|
|
To use Spring Cloud Task's single-instance feature when creating a natively compiled app, you need to enable the feature at build time.
|
|
To do so, add the process-aot execution and set `spring.cloud.task.single-step-instance-enabled=true` as a JVM argument, as follows:
|
|
[source,xml]
|
|
<plugin>
|
|
<groupId>org.springframework.boot</groupId>
|
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
<executions>
|
|
<execution>
|
|
<id>process-aot</id>
|
|
<goals>
|
|
<goal>process-aot</goal>
|
|
</goals>
|
|
<configuration>
|
|
<jvmArguments>
|
|
-Dspring.cloud.task.single-instance-enabled=true
|
|
</jvmArguments>
|
|
</configuration>
|
|
</execution>
|
|
</executions>
|
|
</plugin>
|
|
|
|
[[enabling-observations-for-applicationrunner-and-commandlinerunner]]
|
|
=== Enabling Observations for ApplicationRunner and CommandLineRunner
|
|
|
|
|
|
To Enable Task Observations for `ApplicationRunner` or `CommandLineRunner` set `spring.cloud.task.observation.enabled` to true.
|
|
|
|
An example task application with observations enables using the `SimpleMeterRegistry` can be found https://github.com/spring-cloud/spring-cloud-task/tree/main/spring-cloud-task-samples/task-observations[here].
|
|
|
|
[[disabling-spring-cloud-task-auto-configuration]]
|
|
=== Disabling Spring Cloud Task Auto Configuration
|
|
|
|
In cases where Spring Cloud Task should not be autoconfigured for an implementation, you can disable Task's auto configuration.
|
|
This can be done either by adding the following annotation to your Task application:
|
|
```
|
|
@EnableAutoConfiguration(exclude={SimpleTaskAutoConfiguration.class})
|
|
```
|
|
You may also disable Task auto configuration by setting the `spring.cloud.task.autoconfiguration.enabled` property to `false`.
|
|
|
|
[[closing-the-context]]
|
|
=== Closing the Context
|
|
If the application requires the `ApplicationContext` to be closed at the
|
|
completion of a task (all `*Runner#run` methods have been called and the task
|
|
repository has been updated), set the property `spring.cloud.task.closecontextEnabled`
|
|
to `true`.
|
|
|
|
Another case to close the context is when the Task Execution completes however the application does not terminate.
|
|
In these cases the context is held open because a thread has been allocated
|
|
(for example: if you are using a TaskExecutor). In these cases
|
|
set the `spring.cloud.task.closecontextEnabled` property to `true` when launching your task.
|
|
This will close the application's context once the task is complete.
|
|
Thus allowing the application to terminate.
|
|
|
|
[[enable-task-metrics]]
|
|
=== Enable Task Metrics
|
|
Spring Cloud Task integrates with Micrometer and creates observations for the Tasks it executes.
|
|
To enable Task Observability integration, you must add `spring-boot-starter-actuator`, your preferred registry implementation (if you want to publish metrics), and micrometer-tracing (if you want to publish tracing data) to your task application.
|
|
An example maven set of dependencies to enable task observability and metrics using Influx would be:
|
|
|
|
[source,xml]
|
|
<dependency>
|
|
<groupId>org.springframework.boot</groupId>
|
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>io.micrometer</groupId>
|
|
<artifactId>micrometer-registry-influx</artifactId>
|
|
<scope>runtime</scope>
|
|
</dependency>
|
|
|
|
[[spring-task-and-spring-cloud-task]]
|
|
=== Spring Task and Spring Cloud Task Properties
|
|
|
|
The term `task` is frequently used word in the industry. In one such example Spring Boot offers the `spring.task` while Spring Cloud Task offers the `spring.cloud.task` properties.
|
|
This has caused some confusion in the past that these two groups of properties are directly related. However, they represent 2 different set of features offered in the Spring ecosystem.
|
|
|
|
* `spring.task` refers to the properties that configure the `ThreadPoolTaskScheduler`.
|
|
* `spring.cloud.task` refers to the properties that configure features of Spring Cloud Task.
|