概述
Spring Cloud Task可以让你更简单的创建短时运行的微服务,Spring Cloud团队为在JVM上运行短时应用的需求提供基本的技术支持,由它开发的项目可以在local,cloud,或者Spring Data Flow中启动。通过这个文档,我想你应该可以明白什么是 Spring Cloud Task ? 它能做什么?该怎么来使用它?文档会尽量以通俗的语言来介绍它,以一个简单的 Spring Cloud Task 应用为示例来讨论Spring Cloud Task的设计理念和核心概念。
运行环境
JAVA7或者更高,官方推荐在JAVA8上运行Task应用,最好有maven支持。
数据库环境
Spring Cloud Task需要数据库来存储 task 的执行结果。当然可以运行在无数据库的环境下,但那样的话,重启应用之后所有的环境变量都会消失。现版本的Spring Cloud Task支持的数据库如下所示:
- DB2
- H2
- HSQLDB
- MySql
- Oracle
- Postgres
- SqlServer
第一个 Spring Cloud Task 应用
在这个章节中,我们将创建一个 Spring Cloud Task 的 "Hello Worlk"程序来了解框架特性。该demo使用Apache Maven作为工程管理工具。
spring.io 网站上有很多为使用 spring boot 而创建的 “Getting Started” 向导文件,当你在寻找解决一些问题的方法时,首先应该先查看一下这里的代码。向导文件以简要明了的步骤教你如何在 spring.io 上寻找你想要的东西,并帮助你创建新项目。“Getting Started”可以自动生成工程代码框架来帮助你用最正确的方式使用框架进行编程。
在开始demo编码之前,首先打开命令行检查计算机上已安装的java和maven的版本。
$ java -version
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)
$ mvn -v
Apache Maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-11T15:58:10-05:00)
Maven home: /usr/local/Cellar/maven/3.2.3/libexec
Java version: 1.8.0_31, vendor: Oracle Corporation
这个示例demo需要你首先创建一个独立的工作文件夹,文章随后的部分假定你已经创建了自己的工作文件夹,并处于这个文件夹中。
文档示例的代码位置是 https://github.com/weiwei02/spring-cloud-task-learning ,如有需要可以前往 star 或者 clone。
1. 创建 POM 文件
首先我们需要创建Maven的 pom.xml
文件。pom文件定制了你要编译工程所需的工具和依赖。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.weiwei02</groupId>
<artifactId>spring-cloud-task-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-cloud-task-demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud-task.version>1.2.2.RELEASE</spring-cloud-task.version>
<spring.boot.version>1.5.9.RELEASE</spring.boot.version>
<spring-cloud-task.version>1.2.2.RELEASE</spring-cloud-task.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-task-dependencies</artifactId>
<version>${spring-cloud-task.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这个文档给定了如何编译你的项目,你可以试着使用 mvn package
命令将你的项目打包为 .jar 压缩包(不过此刻项目中并没有什么东西)。
2. classpath 依赖
一个Spring Cloud Task 项目必须同时是一个Spring Boot项目,在上面的POM文件中,我们通过指定spring-boot-starter-parent
为该项目的父项目来将它转化为Spring Boot项目。
Spring Boot提供了许多额外的 "Starter POMs",很多starter对Spring Cloud Task来说都是非常有用的,如spring-boot-starter-batch
,spring-boot-starter-jdbc
等,也有许多starter对其没有什么帮助,如spring-boot-starter-web
.Spring Boot自带的bean探测器会自动扫描classpath下的starter,spring-boot-starter-web
如果在classpath下,在启动应用时就会自动启动Servlet容器。
在这个示例中,我们仅仅需要添加一些简单的额外依赖,用于编写task程序和存储数据.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-task</artifactId>
<version>${spring-cloud-task.version}</version>
</dependency>
3. 编写代码
首先,我们需要创建一个java文件。Maven 会将src/main/java
目录下的源码自动编译,要创建的文件信息如下 src/main/java/com/github/weiwei02/springcloudtaskdemo/SpringCloudTaskDemoApplication.java
package com.github.weiwei02.springcloudtaskdemo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.task.configuration.EnableTask;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableTask
public class SpringCloudTaskDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudTaskDemoApplication.class, args);
}
@Bean
public CommandLineRunner commandLineRunner() {
return new HelloWorldCommandLineRunner();
}
public static class HelloWorldCommandLineRunner implements CommandLineRunner {
public void run(String... strings) throws Exception {
System.out.println("Hello World!");
}
}
}
同时也需要你在 src/main/resources
目录下创建名为application.yml
的配置文件,并配置两项属性:应用名称(在运行时将被当做task的名称);日志级别,指定log的级别可以更方便的让我们调试应用。
spring:
application:
name: HelloWorld
logging:
level.org.springframework.cloud.task: DEBUG
3.1 @EnableTask 注解
@EnableTask
注解可能是我们在这个java文件中见到的第一个陌生的东西,这个class级别的注解如果标记在启动类或配置类上意味着会自动导入额外的默认task配置类SimpleTaskConfiguration
,开启 Spring Cloud Task功能。这个task配置类会自动注册TaskRepository
任务仓库作为基础组件管理后面的task。
虽然我们没有在数据库中持久化存储task相关信息,TaskRepository
仍然可以开箱即用的在内存中创建一个Map
纪录每个task的结果。不过在生产环境采用Map
来存储task的最终结果是不可信的,应用重启后结果会丢失。在这个小demo里,我们使用这种方式来快速开始开发,并用日志纪录会对任务仓库所做的一些操作,后续的文档里,我会详细的介绍Spring Cloud Task的配置。
当示例项目运行时,Spring Boot会执行我们的HelloWorldCommandLineRunner
类并在标准输出流中打印 "Hello World!"。TaskLifecyceListener
监听器会纪录从任务开始至结束全生命周期所做过的操作。
3.2 main 方法
main方法是所有java应用的入口,我们的main方法将应用执行权限委托给SpringApplication
类去执行应用,你可以从Spring Boot的相关文档来了解学习关于这个类的详情原理,这里不再累赘叙述。
3.3 CommandLineRunner 命令行运行接口
在spring应用中有很多种方式去启用应用逻辑,Spring Boot提供了很多方便的启动工具来帮你完成应用逻辑调用和管理,你可以使用 *Runner
接口(如CommandLineRunner
或 ApplicationRunner
),一个标准的task应用会在这两个运行器类中选择一个来完成业务逻辑的调用。
一个task的生命周期就是从*Runner#run
方法开始,直到该task的所有任务都被执行完成。Spring Boot允许应用中有多个 *Runner
接口的实现,所以Spring Cloud Task也不会违反这个约定对多个*Runner
实现有什么影响。
如果应用不是通过
CommandLineRunner
或ApplicationRunner
,而是由其它机制启动的(如InitializingBean#afterPropertiesSet
),那么Spring Cloud Task将不会对它进行记录。
4. 运行示例
在这一小节里,你应该让你的应用跑起来了。因为这个应用是一种spring boot应用,我们可以通过$ mvn spring-boot:run
命令,使用maven启动我们的项目。
$ mvn spring-boot:run
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.9.RELEASE)
2017-12-10 05:29:42.276 [HelloWorld] [main] INFO [com.github.weiwei02.springcloudtaskdemo.SpringCloudTaskDemoApplication] -Starting SpringCloudTaskDemoApplication on DESKTOP-2U56LQR with PID 5112 (I:\workspace\github\spring-cloud\spring-cloud-task-learning\spring-cloud-task-demo\target\classes started by weiwei in I:\workspace\github\spring-cloud\spring-cloud-task-learning)
2017-12-10 05:29:42.278 [HelloWorld] [main] INFO [com.github.weiwei02.springcloudtaskdemo.SpringCloudTaskDemoApplication] -No active profile set, falling back to default profiles: default
2017-12-10 05:29:42.322 [HelloWorld] [main] INFO [org.springframework.context.annotation.AnnotationConfigApplicationContext] -Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7d68ef40: startup date [Sun Dec 10 05:29:42 CST 2017]; root of context hierarchy
2017-12-10 05:29:43.027 [HelloWorld] [main] DEBUG [org.springframework.cloud.task.configuration.SimpleTaskConfiguration] -Using org.springframework.cloud.task.configuration.DefaultTaskConfigurer TaskConfigurer
2017-12-10 05:29:43.028 [HelloWorld] [main] DEBUG [org.springframework.cloud.task.configuration.DefaultTaskConfigurer] -No DataSource was found, using ResourcelessTransactionManager
2017-12-10 05:29:43.847 [HelloWorld] [main] INFO [org.springframework.jmx.export.annotation.AnnotationMBeanExporter] -Registering beans for JMX exposure on startup
2017-12-10 05:29:43.851 [HelloWorld] [main] INFO [org.springframework.context.support.DefaultLifecycleProcessor] -Starting beans in phase 0
2017-12-10 05:29:43.851 [HelloWorld] [main] DEBUG [org.springframework.cloud.task.repository.support.SimpleTaskRepository] -Creating: TaskExecution{executionId=0, parentExecutionId=null, exitCode=null, taskName='application', startTime=Sun Dec 10 05:29:43 CST 2017, endTime=null, exitMessage='null', externalExecutionId='null', errorMessage='null', arguments=[]}
Hello World!
2017-12-10 05:29:43.863 [HelloWorld] [main] DEBUG [org.springframework.cloud.task.repository.support.SimpleTaskRepository] -Updating: TaskExecution with executionId=0 with the following {exitCode=0, endTime=Sun Dec 10 05:29:43 CST 2017, exitMessage='null', errorMessage='null'}
2017-12-10 05:29:43.863 [HelloWorld] [main] INFO [org.springframework.context.annotation.AnnotationConfigApplicationContext] -Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7d68ef40: startup date [Sun Dec 10 05:29:42 CST 2017]; root of context hierarchy
2017-12-10 05:29:43.865 [HelloWorld] [main] INFO [org.springframework.context.support.DefaultLifecycleProcessor] -Stopping beans in phase 0
2017-12-10 05:29:43.866 [HelloWorld] [main] INFO [org.springframework.jmx.export.annotation.AnnotationMBeanExporter] -Unregistering JMX-exposed beans on shutdown
2017-12-10 05:29:43.867 [HelloWorld] [main] INFO [com.github.weiwei02.springcloudtaskdemo.SpringCloudTaskDemoApplication] -Started SpringCloudTaskDemoApplication in 1.809 seconds (JVM running for 2.35)
Process finished with exit code 0
5. 测试
当编写 Spring Cloud Task应用的单元测试时,谨记Spring Cloud Task等到所有task完成之后才会结束自己的生命周期。如果你使用spring的测试套件来管理测试时应用上下文时,如果由Spring Cloud Task来关闭应用程序上下文,可能会抛出java.lang.IllegalStateException: The ApplicationContext loaded for **** is not active. Ensure that the context has not been closed programmatically.
异常。@TestPropertySource(properties = {"spring.cloud.task.closecontext_enable=false"})
可以关闭 Spring Cloud Task的自动关闭上下文的功能。如下代码所示:
package com.github.weiwei02.springcloudtaskdemo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(properties = {"spring.cloud.task.closecontext_enable=false"})
public class SpringCloudTaskDemoApplicationTests {
@Test
public void contextLoads() {
}
}
引用
本文是我在学习使用Spring Cloud Task 时的笔记,在本文的写过过程中参考了大量其它资料,有些材料来源于网络,我由衷的表示感谢,但由于原作者不明,恕不能一一记述。
- Spring Cloud Task Reference Guide.[Michael Minella, Glenn Renfro].v1.2.4RELEASE
- Spring Cloud Task 项目仓库
- Spring Cloud Data Flow Reference Guide#Task
关于
示例源码
Spring Cloud Task learning 的 task-demo 子项目
后记
Spring Cloud Task是一个优秀的项目,但是我找遍网络,也难以找出系统的、准确的中文相关文档。本系列文章以保证对Spring Cloud Task相关概念和设计理解的正确性为标准,尽量采用通俗易懂的语言,希望能给各位带来一些便捷。
本文内容主要是对 Spring Cloud Task 1.2.2-RELEASE 官方文档的翻译,不过作者水平有限,有不尽然的地方敬请指出。本项目和文档中所用的内容仅供学习和研究之用,转载或引用时请指明出处。如果你对文档有疑问或问题,请在项目中给我留言或发email到
weiwei02@vip.qq.com 我的github:
https://github.com/weiwei02/ 我相信技术能够改变世界 。