[spring cloud task]1 简介与示例

概述

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 接口(如CommandLineRunnerApplicationRunner),一个标准的task应用会在这两个运行器类中选择一个来完成业务逻辑的调用。

一个task的生命周期就是从*Runner#run方法开始,直到该task的所有任务都被执行完成。Spring Boot允许应用中有多个 *Runner 接口的实现,所以Spring Cloud Task也不会违反这个约定对多个*Runner实现有什么影响。

如果应用不是通过CommandLineRunnerApplicationRunner,而是由其它机制启动的(如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 时的笔记,在本文的写过过程中参考了大量其它资料,有些材料来源于网络,我由衷的表示感谢,但由于原作者不明,恕不能一一记述。

  1. Spring Cloud Task Reference Guide.[Michael Minella, Glenn Renfro].v1.2.4RELEASE
  2. Spring Cloud Task 项目仓库
  3. 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/ 我相信技术能够改变世界 。

链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容