# Spring 云数据流参考 GUID

# 前言

## 1. 关于文档

此版本的文档可在[HTML](https://docs.spring.io/spring-cloud-dataflow/docs/2.9.2/reference/htmlsingle)中找到。

可以找到 Spring 云数据流参考指南的最新副本[here](https://docs.spring.io/spring-cloud-dataflow/docs/current-SNAPSHOT/reference/html/)。

本文件的副本可供你自己使用并分发给他人，但前提是你不对此类副本收取任何费用，并且还需每一份副本均包含本版权声明，无论是以印刷形式还是以电子方式分发。

## 2. 获得帮助

云数据流有问题吗？我们愿意提供帮助！

* 问一个问题。我们监控[stackoverflow.com](https://stackoverflow.com)中带有[`spring-cloud-dataflow`]标记的问题（https://stackoverflow.com/tags/ Spring-cloud-dataflow）。

* 在[github.com/spring-cloud/spring-cloud-dataflow/issues](https://github.com/spring-cloud/spring-cloud-dataflow/issues)处使用 Spring 云数据流报告错误。

* 在[Gitter](https://gitter.im/spring-cloud/spring-cloud-dataflow)上与社区和开发人员聊天。

|   |所有的云数据流都是开源的，包括文档！如果你在 DOCS 中发现问题<br/>，或者你只是想改进它们，请[参与进来](https://github.com/spring-cloud/spring-cloud-dataflow)。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

# 开始

## 3. 开始-本地

有关设置 Docker Compose 和手动安装的更多信息，请参见 microSite 的[本地机器](https://dataflow.spring.io/docs/installation/local/)部分。

在本地安装了数据流服务器之后，你可能想要开始将现成的预构建应用程序部署到相干流或批处理数据管道中。我们有一些指南可以帮助你同时开始[Stream](https://dataflow.spring.io/docs/stream-developer-guides/)和[Batch](https://dataflow.spring.io/docs/batch-developer-guides/)处理。

## 4. 入门-Cloud Foundry

本节介绍如何在 Cloud Foundry 上开始使用 Spring 云数据流。有关在 Cloud Foundry 上安装 Spring 云数据流的更多信息，请参见 microSite 的[Cloud Foundry](https://dataflow.spring.io/docs/installation/cloudfoundry/)部分。

一旦在 Cloud Foundry 上安装了数据流服务器，你可能想开始着手将现成的预构建应用程序部署到连贯的流或批处理数据管道中。我们有一些指南可以帮助你同时开始[Stream](https://dataflow.spring.io/docs/stream-developer-guides/)和[Batch](https://dataflow.spring.io/docs/batch-developer-guides/)处理。

## 5. 开始-Kubernetes

[Spring Cloud Data Flow](https://cloud.spring.io/spring-cloud-dataflow/)是一个用于构建数据集成和实时数据处理管道的工具包。

管道由 Spring 引导应用程序组成，这些应用程序是用 Spring Cloud Stream 或 Spring Cloud Task 微服务框架构建的。这使得 Spring 云数据流适合一系列数据处理用例，从导入-导出到事件流和预测分析。

该项目提供了使用 Spring 云数据流的支持，Kubernetes 是这些管道的运行时，应用程序打包为 Docker 映像。

有关在 Kubernetes 上安装 Spring 云数据流的更多信息，请参见 microSite 的[Kubernetes](https://dataflow.spring.io/docs/installation/kubernetes/)部分。

在 Kubernetes 上安装了数据流服务器之后，你可能想要着手将现成的预构建应用程序部署到一个连贯的流或批处理数据管道中。我们有一些指南可以帮助你同时开始[Stream](https://dataflow.spring.io/docs/stream-developer-guides/)和[Batch](https://dataflow.spring.io/docs/batch-developer-guides/)处理。

### 5.1.应用程序和服务器属性

本节介绍如何定制应用程序的部署。你可以使用许多属性来影响部署的应用程序的设置。属性可以在每个应用程序的基础上应用，也可以在所有已部署应用程序的适当服务器配置中应用。

|   |在每个应用程序的基础上设置的属性总是优先于作为服务器配置设置的属性。这种安排使你可以在每个应用程序的基础上重写全局服务器级别的属性。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

要为所有部署的任务应用的属性在`src/kubernetes/server/server-config-[binder].yaml`文件中定义，在`src/kubernetes/skipper/skipper-config-[binder].yaml`中为流定义。将`[binder]`替换为你正在使用的消息传递中间件——例如，`rabbit`或`kafka`。

#### 5.1.1.内存和 CPU 设置

应用程序使用默认的内存和 CPU 设置进行部署。如果需要，可以调整这些值。下面的示例显示了如何将`Limits`设置为`1000m`，对于内存为`CPU`，将`1024Mi`设置为`Requests`，对于 CPU 为`800m`，对于内存为`640Mi`:

```
deployer.<application>.kubernetes.limits.cpu=1000m
deployer.<application>.kubernetes.limits.memory=1024Mi
deployer.<application>.kubernetes.requests.cpu=800m
deployer.<application>.kubernetes.requests.memory=640Mi
```

这些值将导致使用以下容器设置：

```
Limits:
  cpu:	1
  memory:	1Gi
Requests:
  cpu:	800m
  memory:	640Mi
```

你还可以控制全局设置`cpu`和`memory`的默认值。

下面的示例展示了如何为流设置 CPU 和内存：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    limits:
                      memory: 640mi
                      cpu: 500m
```

下面的示例展示了如何为任务设置 CPU 和内存：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    limits:
                      memory: 640mi
                      cpu: 500m
```

到目前为止，我们使用的设置仅影响容器的设置。它们不会影响容器中 JVM 进程的内存设置。如果你想要设置 JVM 内存设置，那么你可以设置一个环境变量来这样做。有关详细信息，请参见下一节。

#### 5.1.2.环境变量

要影响给定应用程序的环境设置，可以使用`spring.cloud.deployer.kubernetes.environmentVariables`Deployer 属性。例如，生产设置中的一个常见要求是影响 JVM 内存参数。你可以通过使用`JAVA_TOOL_OPTIONS`环境变量来实现这一点，如下例所示：

```
deployer.<application>.kubernetes.environmentVariables=JAVA_TOOL_OPTIONS=-Xmx1024m
```

|   |`environmentVariables`属性接受以逗号分隔的字符串。如果一个环境变量包含的值<br/>也是逗号分隔的字符串，那么它必须用单引号括起来——例如，`spring.cloud.deployer.kubernetes.environmentVariables=spring.cloud.stream.kafka.binder.brokers='somehost:9092,<br/>anotherhost:9093'`|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

这将覆盖所需`<application>`的 JVM 内存设置（用应用程序的名称替换`<application>`）。

#### 5.1.3.活性和准备状态探查

`liveness`和`readiness`探针分别使用称为`/health`和`/info`的路径。它们分别使用`delay`的`10`和`period`的`60`和`10`。在部署流时，可以使用 Deployer 属性更改这些默认值。活性和准备状态探测仅应用于流。

下面的示例通过设置部署程序属性来更改`liveness`探测（将`<application>`替换为应用程序的名称）：

```
deployer.<application>.kubernetes.livenessProbePath=/health
deployer.<application>.kubernetes.livenessProbeDelay=120
deployer.<application>.kubernetes.livenessProbePeriod=20
```

可以将其声明为流的服务器全局配置的一部分，如下例所示：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    livenessProbePath: /health
                    livenessProbeDelay: 120
                    livenessProbePeriod: 20
```

类似地，你可以将`liveness`交换为`readiness`，以覆盖默认的`readiness`设置。

默认情况下，端口 8080 被用作探测端口。可以使用 Deployer 属性更改`liveness`和`readiness`探测端口的默认值，如下例所示：

```
deployer.<application>.kubernetes.readinessProbePort=7000
deployer.<application>.kubernetes.livenessProbePort=7000
```

可以将其声明为流的全局配置的一部分，如下例所示：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    readinessProbePort: 7000
                    livenessProbePort: 7000
```

|   |默认情况下，`liveness`和`readiness`探测路径使用 Spring Boot2.x+actuator 端点。要使用 Spring Boot1.x 执行器端点路径，必须调整`liveness`和`readiness`值，如下面的示例所示（将`<application>`替换为应用程序的名称）：<br/><br/>```<br/>deployer.<application>.kubernetes.livenessProbePath=/health<br/>deployer.<application>.kubernetes.readinessProbePath=/info<br/>```<br/>在每个应用程序的基础上自动将`liveness`和`readiness`端点都设置为默认的 Spring 引导 1.x 路径，你可以设置以下属性：<br/>r=“gt=”“606”/><593"/>|
|---| |

你可以使用存储在[Kubernetes 的秘密](https://kubernetes.io/docs/concepts/configuration/secret/)中的凭据访问安全的探测端点。如果凭据包含在秘密的`credentials`块的`data`键名下，则可以使用现有的秘密。你可以在每个应用程序的基础上配置探测身份验证。启用后，将使用相同的凭据和身份验证类型将其应用于`liveness`和`readiness`探测端点。目前，只支持`Basic`身份验证。

创造一个新的秘密：

1. 使用用于访问安全探测端点的凭据生成 base64 字符串。

   基本身份验证将用户名和密码编码为`username:password`格式的 base64 字符串。

   下面的示例（其中包括输出，你应该用你的值替换`user`和`pass`）展示了如何生成 base64 字符串：

   ```
   $ echo -n "user:pass" | base64
   dXNlcjpwYXNz
   ```

2. 使用编码的凭据，创建一个包含以下内容的文件（例如，`myprobesecret.yml`）：

   ```
   apiVersion: v1
   kind: Secret
   metadata:
     name: myprobesecret
   type: Opaque
   data:
     credentials: GENERATED_BASE64_STRING
   ```

3. 将`GENERATED_BASE64_STRING`替换为前面生成的 base64 编码的值。

4. 使用`kubectl`创建秘密，如下例所示：

   ```
   $ kubectl create -f ./myprobesecret.yml
   secret "myprobesecret" created
   ```

5. 将以下部署人员属性设置为在访问探测端点时使用身份验证，如下例所示：

   ```
   deployer.<application>.kubernetes.probeCredentialsSecret=myprobesecret
   ```

   将`<application>`替换为要对其应用身份验证的应用程序的名称。

#### 5.1.4.使用`SPRING_APPLICATION_JSON`

你可以使用`SPRING_APPLICATION_JSON`环境变量来设置数据流服务器属性（包括 Maven 存储库设置的配置），这些属性在所有数据流服务器实现中都是通用的。这些设置位于部署 YAML 的容器`env`部分的服务器级别。下面的示例展示了如何做到这一点：

```
env:
- name: SPRING_APPLICATION_JSON
  value: "{ \"maven\": { \"local-repository\": null, \"remote-repositories\": { \"repo1\": { \"url\": \"https://repo.spring.io/libs-snapshot\"} } } }"
```

#### 5.1.5.专用 Docker 注册表

你可以在每个应用程序的基础上从私有注册表中提取 Docker 映像。首先，你必须在集群中创建一个秘密。按照[从私有注册表中提取图像](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/)指南创建秘密。

创建了秘密之后，就可以使用`imagePullSecret`属性来设置要使用的秘密，如下例所示：

```
deployer.<application>.kubernetes.imagePullSecret=mysecret
```

将`<application>`替换为应用程序的名称，并将`mysecret`替换为前面创建的秘密的名称。

你还可以在全局服务器级别上配置图像提取秘密。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    imagePullSecret: mysecret
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    imagePullSecret: mysecret
```

用前面创建的秘密的名称替换`mysecret`。

#### 5.1.6.注解

你可以在每个应用程序的基础上向 Kubernetes 对象添加注释。支持的对象类型是 pod`Deployment`、`Service`和`Job`。注释以`key:value`格式定义，允许用逗号分隔多个注释。有关注释的更多信息和用例，请参见[注解](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)。

下面的示例展示了如何配置应用程序来使用注释：

```
deployer.<application>.kubernetes.podAnnotations=annotationName:annotationValue
deployer.<application>.kubernetes.serviceAnnotations=annotationName:annotationValue,annotationName2:annotationValue2
deployer.<application>.kubernetes.jobAnnotations=annotationName:annotationValue
```

用应用程序的名称和注释值替换`<application>`。

#### 5.1.7.入口点样式

入口点样式会影响将应用程序属性传递给要部署的容器的方式。目前，支持三种样式：

* `exec`（默认）：将部署请求中的所有应用程序属性和命令行参数作为容器参数传递。应用程序属性被转换为`--key=value`的格式。

* `shell`:将所有应用程序属性和命令行参数作为环境变量传递。每个应用程序或命令行参数属性都被转换为大写字符串，并且`.`字符被替换为`_`。

* `boot`:创建一个名为`SPRING_APPLICATION_JSON`的环境变量，该变量包含所有应用程序属性的 JSON 表示。来自部署请求的命令行参数被设置为容器参数。

|   |在所有情况下，在服务器级配置和每个应用程序的基础上定义的环境变量都会按原样发送到容器。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------|

你可以按以下方式配置应用程序：

```
deployer.<application>.kubernetes.entryPointStyle=<Entry Point Style>
```

将`<application>`替换为应用程序的名称，并将`<Entry Point Style>`替换为所需的入口点样式。

你还可以在全局服务器级别配置入口点样式。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    entryPointStyle: entryPointStyle
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    entryPointStyle: entryPointStyle
```

用所需的入口点样式替换`entryPointStyle`。

你应该选择`exec`或`shell`的入口点样式，以对应于在容器的`ENTRYPOINT`中定义`ENTRYPOINT`语法的方式。有关`exec`与`shell`的更多信息和用例，请参见 Docker 文档的[ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint)部分。

使用`boot`入口点样式对应于使用`exec`样式`ENTRYPOINT`。来自部署请求的命令行参数将传递给容器，添加的应用程序属性将映射到`SPRING_APPLICATION_JSON`环境变量中，而不是命令行参数。

|   |当使用`boot`入口点样式时，`deployer.<application>.kubernetes.environmentVariables`属性不能包含`SPRING_APPLICATION_JSON`。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 5.1.8.部署服务帐户

你可以通过属性为应用程序部署配置自定义服务帐户。你可以使用现有的服务帐户或创建新的服务帐户。创建服务帐户的一种方法是使用`kubectl`，如下例所示：

```
$ kubectl create serviceaccount myserviceaccountname
serviceaccount "myserviceaccountname" created
```

然后，你可以按以下方式配置各个应用程序：

```
deployer.<application>.kubernetes.deploymentServiceAccountName=myserviceaccountname
```

将`<application>`替换为应用程序的名称，并将`myserviceaccountname`替换为服务帐户的名称。

你还可以在全局服务器级别配置服务帐户名。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    deploymentServiceAccountName: myserviceaccountname
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    deploymentServiceAccountName: myserviceaccountname
```

将`myserviceaccountname`替换为要应用于所有部署的服务帐户名称。

#### 5.1.9.图像拉取策略

图像拉取策略定义了何时应该将 Docker 图像拉到本地注册中心。目前，有三项政策得到支持：

* `IfNotPresent`（默认值）：如果图像已经存在，请不要拉它。

* `Always`:无论图像是否已经存在，都要拉它。

* `Never`:永远不要拉图像。只使用已经存在的图像。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.imagePullPolicy=Always
```

将`<application>`替换为应用程序的名称，并将`Always`替换为所需的图像拉出策略。

你可以在全局服务器级别配置一个映像拉出策略。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    imagePullPolicy: Always
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    imagePullPolicy: Always
```

将`Always`替换为所需的图像拉出策略。

#### 5.1.10.部署标签

你可以在与[Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)相关的对象上设置自定义标签。有关标签的更多信息，请参见[Labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/)。标签以`key:value`格式指定。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.deploymentLabels=myLabelName:myLabelValue
```

用应用程序的名称替换`<application>`，用标签名称替换`myLabelName`，用标签的值替换`myLabelValue`。

此外，你还可以应用多个标签，如下例所示：

```
deployer.<application>.kubernetes.deploymentLabels=myLabelName:myLabelValue,myLabelName2:myLabelValue2
```

#### 5.1.11.公差

公差与污点一起工作，以确保 POD 不被调度到特定节点上。公差被设置到 POD 配置中，而污点被设置到节点上。有关更多信息，请参见 Kubernetes 引用的[污点和公差](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)部分。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.tolerations=[{key: 'mykey', operator: 'Equal', value: 'myvalue', effect: 'NoSchedule'}]
```

根据所需的容忍配置，将`<application>`替换为应用程序的名称和键值对。

你也可以在全局服务器级别配置容差。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    tolerations:
                      - key: mykey
                        operator: Equal
                        value: myvalue
                        effect: NoSchedule
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    tolerations:
                      - key: mykey
                        operator: Equal
                        value: myvalue
                        effect: NoSchedule
```

根据所需的容忍配置替换`tolerations`键值对。

#### 5.1.12.秘密参考文献

秘密可以被引用，它们的全部数据内容可以被解码，并作为单个变量插入到 POD 环境中。有关更多信息，请参见 Kubernetes 引用的[将秘密中的所有键值对配置为容器环境变量](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/#configure-all-key-value-pairs-in-a-secret-as-container-environment-variables)部分。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.secretRefs=testsecret
```

你还可以指定多个秘密，如下所示：

```
deployer.<application>.kubernetes.secretRefs=[testsecret,anothersecret]
```

将`<application>`替换为应用程序的名称，并将`secretRefs`属性替换为应用程序环境和秘密的适当值。

你也可以在全局服务器级别配置秘密引用。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    secretRefs:
                      - testsecret
                      - anothersecret
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    secretRefs:
                      - testsecret
                      - anothersecret
```

将`secretRefs`中的项替换为一个或多个秘密名称。

#### 5.1.13.秘密密钥引用

秘密可以被引用，它们的解码值可以被插入到 POD 环境中。有关更多信息，请参见 Kubernetes 引用的[使用秘密作为环境变量](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-environment-variables)部分。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.secretKeyRefs=[{envVarName: 'MY_SECRET', secretName: 'testsecret', dataKey: 'password'}]
```

将`<application>`替换为应用程序的名称，并将`envVarName`、`secretName`和`dataKey`属性替换为应用程序环境和秘密的适当值。

你也可以在全局服务器级别配置密钥引用。

下面的示例展示了如何对流进行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    secretKeyRefs:
                      - envVarName: MY_SECRET
                        secretName: testsecret
                        dataKey: password
```

下面的示例展示了如何对任务执行此操作：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    secretKeyRefs:
                      - envVarName: MY_SECRET
                        secretName: testsecret
                        dataKey: password
```

将`envVarName`、`secretName`和`dataKey`属性替换为适合你的秘密的值。

#### 5.1.14.配置图引用

配置图可以被引用，其全部数据内容可以被解码，并作为单个变量插入到 POD 环境中。有关更多信息，请参见 Kubernetes 引用的[将配置映射中的所有键值对配置为容器环境变量](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables)部分。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.configMapRefs=testcm
```

你还可以指定多个 ConfigMap 实例，如下所示：

```
deployer.<application>.kubernetes.configMapRefs=[testcm,anothercm]
```

将`<application>`替换为应用程序的名称，并将`configMapRefs`属性替换为应用程序环境和配置图的适当值。

你还可以在全局服务器级别配置配置配置映射引用。

下面的示例展示了如何对流进行此操作。编辑适当的`skipper-config-(binder).yaml`，使用相应的活页夹替换`(binder)`:

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    configMapRefs:
                      - testcm
                      - anothercm
```

下面的示例展示了如何通过编辑`server-config.yaml`文件来完成任务：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    configMapRefs:
                      - testcm
                      - anothercm
```

将`configMapRefs`中的项替换为一个或多个秘密名称。

#### 5.1.15.配置映射密钥引用

可以引用配置图并将其相关的键值插入到 POD 环境中。有关更多信息，请参见 Kubernetes 引用的[使用配置图数据定义容器环境变量](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#define-container-environment-variables-using-configmap-data)部分。

下面的示例展示了如何单独配置应用程序：

```
deployer.<application>.kubernetes.configMapKeyRefs=[{envVarName: 'MY_CM', configMapName: 'testcm', dataKey: 'platform'}]
```

将`<application>`替换为应用程序的名称，并将`envVarName`、`configMapName`和`dataKey`属性替换为应用程序环境和配置图的适当值。

你还可以在全局服务器级别配置配置配置映射引用。

下面的示例展示了如何对流进行此操作。编辑适当的`skipper-config-(binder).yaml`，使用相应的活页夹替换`(binder)`:

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    configMapKeyRefs:
                      - envVarName: MY_CM
                        configMapName: testcm
                        dataKey: platform
```

下面的示例展示了如何通过编辑`server-config.yaml`文件来完成任务：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    configMapKeyRefs:
                      - envVarName: MY_CM
                        configMapName: testcm
                        dataKey: platform
```

用配置映射的适当值替换`envVarName`、`configMapName`和`dataKey`属性。

#### 5.1.16.POD 安全上下文

你可以配置 POD 安全上下文，以便在指定的 UID（用户 ID）或 GID（组 ID）下运行进程。当你不希望在缺省`root`UID 和 GID 下运行进程时，这是很有用的。你可以定义`runAsUser`或`fsGroup`，并且可以将它们配置为一起工作。有关更多信息，请参见 Kubernetes 引用的[安全环境](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)部分。

下面的示例展示了如何单独配置应用程序吊舱：

```
deployer.<application>.kubernetes.podSecurityContext={runAsUser: 65534, fsGroup: 65534}
```

将`<application>`替换为应用程序的名称，并将`runAsUser`和/或`fsGroup`属性替换为容器环境的适当值。

你也可以在全局服务器级别配置 POD 安全上下文。

下面的示例展示了如何对流进行此操作。编辑适当的`skipper-config-(binder).yaml`，使用相应的活页夹替换`(binder)`:

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    podSecurityContext:
                      runAsUser: 65534
                      fsGroup: 65534
```

下面的示例展示了如何通过编辑`server-config.yaml`文件来完成任务：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    podSecurityContext:
                      runAsUser: 65534
                      fsGroup: 65534
```

用容器环境的适当值替换`runAsUser`和/或`fsGroup`属性。

#### 5.1.17.服务端口

部署应用程序时，将使用`8080`的默认端口创建一个 Kubernetes 服务对象。如果设置了`server.port`属性，它将重写默认端口值。你可以在每个应用程序的基础上向服务对象添加额外的端口。你可以使用逗号分隔符添加多个端口。

下面的示例展示了如何为应用程序配置服务对象上的附加端口：

```
deployer.<application>.kubernetes.servicePorts=5000
deployer.<application>.kubernetes.servicePorts=5000,9000
```

将`<application>`替换为应用程序的名称和端口的值。

#### 5.1.18.statefulset init 容器

当通过使用 statefulset 部署应用程序时，将使用 init 容器在 POD 中设置实例索引。默认情况下，使用的图像是`busybox`，你可以对其进行自定义。

下面的示例展示了如何单独配置应用程序吊舱：

```
deployer.<application>.kubernetes.statefulSetInitContainerImageName=myimage:mylabel
```

将`<application>`替换为应用程序的名称，并将`statefulSetInitContainerImageName`属性替换为适合你的环境的值。

你也可以在全局服务器级别配置 statefulset init 容器。

下面的示例展示了如何对流进行此操作。编辑适当的`skipper-config-(binder).yaml`，使用相应的活页夹替换`(binder)`:

```
data:
  application.yaml: |-
    spring:
      cloud:
        skipper:
          server:
            platform:
              kubernetes:
                accounts:
                  default:
                    statefulSetInitContainerImageName: myimage:mylabel
```

下面的示例展示了如何通过编辑`server-config.yaml`文件来完成任务：

```
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    statefulSetInitContainerImageName: myimage:mylabel
```

将`statefulSetInitContainerImageName`属性替换为适合你的环境的值。

#### 5.1.19.init 容器

部署应用程序时，可以根据每个应用程序设置自定义的 init 容器。有关更多信息，请参阅 Kubernetes 引用的[init 容器](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)部分。

下面的示例展示了如何为应用程序配置 init 容器：

```
deployer.<application>.kubernetes.initContainer={containerName: 'test', imageName: 'busybox:latest', commands: ['sh', '-c', 'echo hello']}
```

用应用程序的名称替换`<application>`，并设置适合 init 容器的`initContainer`属性的值。

#### 5.1.20.生命周期支持

部署应用程序时，可以附加`postStart`和`preStop`[生命周期处理程序](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/)来执行命令。除了`exec`之外，Kubernetes API 还支持其他类型的处理程序。此功能可以扩展为在将来的版本中支持其他操作。要配置上面链接页中所示的生命周期处理程序，请使用以下属性键将每个命令指定为逗号分隔的列表：

```
deployer.<application>.kubernetes.lifecycle.postStart.exec.command=/bin/sh,-c,'echo Hello from the postStart handler > /usr/share/message'
deployer.<application>.kubernetes.lifecycle.preStop.exec.command=/bin/sh,-c,'nginx -s quit; while killall -0 nginx; do sleep 1; done'
```

#### 5.1.21.额外的集装箱

在部署应用程序时，可能需要将一个或多个容器与主容器一起部署。这将允许你适应一些部署模式，例如 Sidecar 和 Adapter，以备多容器 POD 设置的情况。

下面的示例展示了如何为应用程序配置其他容器：

```
deployer.<application>.kubernetes.additionalContainers=[{name: 'c1', image: 'busybox:latest', command: ['sh', '-c', 'echo hello1'], volumeMounts: [{name: 'test-volume', mountPath: '/tmp', readOnly: true}]},{name: 'c2', image: 'busybox:1.26.1', command: ['sh', '-c', 'echo hello2']}]
```

# 应用程序

选择预构建的[stream](https://cloud.spring.io/spring-cloud-stream-app-starters/)和[任务或批处理](https://cloud.spring.io/spring-cloud-task-app-starters/)启动器应用程序，用于各种数据集成和处理场景，以促进学习和实验。下一节中的表格包括预先构建的应用程序，以供浏览。有关更多详细信息，请查看如何[注册支持的应用程序](#supported-apps-and-tasks)。

## 6. 可用的应用程序

|                                                                             Source                                                                              |处理器|                                                                                    Sink                                                                                    |                                                                 Task                                                                 |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------|
|           [sftp](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-sftp-source)            |[tcp-client](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-tcp-client-processor)|                  [mqtt](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-mqtt-sink)                  |        [timestamp](https://docs.spring.io/spring-cloud-task-app-starters/docs/current/reference/htmlsingle/#_timestamp_task)         |
|            [jms](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-jms-source)             |[可脚本转换](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-scriptable-transform)|                   [log](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-log-sink)                   |[composed-task-runner](https://docs.spring.io/spring-cloud-task-app-starters/docs/current/reference/htmlsingle/#_composed_task_runner)|
|            [ftp](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-ftp-source)             |[transform](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-clound-stream-modules-transform-processor)|            [throughput](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-throughput-sink)            |  [timestamp-batch](https://docs.spring.io/spring-cloud-task-app-starters/docs/current/reference/htmlsingle/#_timestamp_batch_task)   |
|           [time](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-time-source)            |[header-enricher](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-header-enricher-processor)|               [mongodb](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-mongodb-sink)               |                                                                                                                                      |
| [load-generator](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-load-generator-source)  |[Python-HTTP](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-python-http-processor)|                   [ftp](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-ftp-sink)                   |                                                                                                                                      |
|         [syslog](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-syslog-source)          |[推特-情绪](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-twitter-sentiment-processor)|                  [jdbc](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-jdbc-sink)                  |                                                                                                                                      |
|           [s3](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-aws-s3-source)            |[splitter](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-splitter)|             [cassandra](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-cassandra-sink)             |                                                                                                                                      |
|    [loggregator](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-loggregator-source)     |[图像识别](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-image-recognition-processor)|                [router](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-router-sink)                |                                                                                                                                      |
|[triggertask (deprecated)](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-trigger-source)|[bridge](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-bridge-processor)|             [redis-pubsub](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-redis-sink)              |                                                                                                                                      |
|  [twitterstream](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-twitterstream-source)   |[pmml](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-pmml-processor)|                  [file](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-file-sink)                  |                                                                                                                                      |
|        [mongodb](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-mongodb-source)         |[Python-Jython](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-python-jython-processor)|             [websocket](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-websocket-sink)             |                                                                                                                                      |
|     [gemfire-cq](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-gemfire-cq-source)      |[Groovy-transform](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-groovy-transform-processor)|                  [s3](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-aws-s3-sink)                  |                                                                                                                                      |
|           [http](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-http-source)            |[httpclient](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-httpclient-processor)|                [rabbit](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-rabbit-sink)                |                                                                                                                                      |
|         [rabbit](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-rabbit-source)          |[filter](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-filter-processor)|               [counter](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-counter-sink)               |                                                                                                                                      |
|            [tcp](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-tcp-source)             |[姿态估计](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-pose-estimation-processor)|                [pgcopy](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-pgcopy-sink)                |                                                                                                                                      |
|        [trigger](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-trigger-source)         |[grpc](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-grpc-processor)|                                                   [gpfdist](https://github.com/spring-cloud-stream-app-starters/gpfdist)                                                   |                                                                                                                                      |
|           [mqtt](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-mqtt-source)            |[groovy-filter](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-groovy-filter-processor)|                  [sftp](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-sftp-sink)                  |                                                                                                                                      |
|     [tcp-client](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-tcp-client-source)      |[aggregator](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-aggregator-processor)|[task-launcher-dataflow](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-task-launcher-dataflow-sink)|                                                                                                                                      |
|           [mail](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-mail-source)            |[counter](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-counter-processor)|                  [hdfs](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-hdfs-sink)                  |                                                                                                                                      |
|           [jdbc](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-jdbc-source)            |[tensorflow](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-tensorflow-processor)|                   [tcp](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-tcp-sink)                   |                                                                                                                                      |
|        [gemfire](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-gemfire-source)         |[tasklaunchrequest-transform（不推荐）](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-tasklaunchrequest-transform)|               [gemfire](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-gemfire-sink)               |                                                                                                                                      |
|           [file](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-file-source)            |[物体检测](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-object-detection-processor)|                                                                                                                                                                            |                                                                                                                                      |
|  [sftp-dataflow](https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/htmlsingle/#spring-cloud-stream-modules-sftp-dataflow-source)   |                                                                                                                                                                                              |                                                                                                                                                                            |                                                                                                                                      |

# 建筑

## 7. 导言

Spring 云数据流简化了专注于数据处理用例的应用程序的开发和部署。

微网站的[建筑](https://dataflow.spring.io/docs/concepts/architecture/)部分描述了数据流的体系结构。

# 配置

## 8. Maven

如果希望覆盖特定的 Maven 配置属性（远程存储库、代理和其他属性）或在代理后面运行数据流服务器，则需要在启动数据流服务器时将这些属性指定为命令行参数，如以下示例所示：

```
$ java -jar spring-cloud-dataflow-server-2.9.2.jar --spring.config.additional-location=/home/joe/maven.yml
```

前面的命令假定为与下面类似的`maven.yaml`:

```
maven:
  localRepository: mylocal
  remote-repositories:
    repo1:
      url: https://repo1
      auth:
        username: user1
        password: pass1
      snapshot-policy:
        update-policy: daily
        checksum-policy: warn
      release-policy:
        update-policy: never
        checksum-policy: fail
    repo2:
      url: https://repo2
      policy:
        update-policy: always
        checksum-policy: fail
  proxy:
    host: proxy1
    port: "9010"
    auth:
      username: proxyuser1
      password: proxypass1
```

默认情况下，该协议被设置为`http`。如果代理不需要用户名和密码，则可以省略 auth 属性。另外，默认情况下， Maven `localRepository`被设置为`${user.home}/.m2/repository/`。如前面的示例所示，你可以指定远程存储库以及它们的身份验证（如果需要）。如果远程存储库位于代理之后，则可以指定代理属性，如前面的示例所示。

你可以为每个远程存储库配置指定存储库策略，如前面的示例所示。键`policy`既适用于`snapshot`，也适用于`release`存储库策略。

有关受支持的存储库策略列表，请参见[存储库策略](https://github.com/apache/maven-resolver/blob/master/maven-resolver-api/src/main/java/org/eclipse/aether/repository/RepositoryPolicy.java)主题。

因为这是 Spring boot`@ConfigurationProperties`，所以你需要通过将它们添加到`SPRING_APPLICATION_JSON`环境变量中来进行指定。下面的示例展示了 JSON 的结构：

```
$ SPRING_APPLICATION_JSON='
{
  "maven": {
    "local-repository": null,
    "remote-repositories": {
      "repo1": {
        "url": "https://repo1",
        "auth": {
          "username": "repo1user",
          "password": "repo1pass"
        }
      },
      "repo2": {
        "url": "https://repo2"
      }
    },
    "proxy": {
      "host": "proxyhost",
      "port": 9018,
      "auth": {
        "username": "proxyuser",
        "password": "proxypass"
      }
    }
  }
}
'
```

### 8.1.马车

对于使用`Wagon`运输和 Maven 有一个有限的支持。目前，这是为了支持*先发制人*基于`http`的存储库的身份验证而存在的，并且需要手动启用。

通过将`maven.use-wagon`属性设置为`true`，可以启用基于 wagon 的`http`传输。然后，你可以为每个远程存储库启用*先发制人*身份验证。配置大致遵循[HttpClient HTTP Wagon](https://maven.apache.org/guides/mini/guide-http-settings.html)中的类似模式。在撰写本文时， Maven 自己网站中的文档有点误导性，并且缺少大多数可能的配置选项。

`maven.remote-repositories.<repo>.wagon.http`名称空间包含所有与 Wagon`http`相关的设置，它下面的键直接映射到受支持的`http`方法—即`all`，`put`，`get`和`head`，就像 Maven 自己的配置一样。在这些方法配置下，然后可以设置各种选项，例如`use-preemptive`。将带有所有请求的 auth 头发送到指定的远程存储库的 Simpl*先发制人*配置看起来像以下示例：

```
maven:
  use-wagon: true
  remote-repositories:
    springRepo:
      url: https://repo.example.org
      wagon:
        http:
          all:
            use-preemptive: true
      auth:
        username: user
        password: password
```

与配置`all`方法不同，你可以只调整`get`和`head`请求的设置，如下所示：

```
maven:
  use-wagon: true
  remote-repositories:
    springRepo:
      url: https://repo.example.org
      wagon:
        http:
          get:
            use-preemptive: true
          head:
            use-preemptive: true
            use-default-headers: true
            connection-timeout: 1000
            read-timeout: 1000
            headers:
              sample1: sample2
            params:
              http.socket.timeout: 1000
              http.connection.stalecheck: true
      auth:
        username: user
        password: password
```

这里有`use-default-headers`、`connection-timeout`、`read-timeout`、request`headers`和 httpclient`params`的设置。有关参数的更多信息，请参见[货车配置](https://github.com/apache/maven-wagon/blob/master/wagon-providers/wagon-http-shared/src/main/java/org/apache/maven/wagon/shared/http/ConfigurationUtils.java)。

## 9. 安全

默认情况下，数据流服务器是不安全的，并在未加密的 HTTP 连接上运行。你可以通过启用 HTTPS 并要求客户机使用[OAuth 2.0](https://oauth.net/2/)进行身份验证来保护 REST 端点以及数据流仪表板。

|   |附录[Azure](#appendix-identity-provider-azure)包含如何<br/>设置*Azure 活动目录*集成的更多信息。|
|---|------------------------------------------------------------------------------------------------------------------------------------|

|   |默认情况下，REST 端点（管理、管理和健康状态）以及仪表板 UI 不需要经过身份验证的访问。|
|---|----------------------------------------------------------------------------------------------------------------------------------------|

虽然理论上可以结合 Spring 云数据流选择任何 OAuth 提供商，但我们建议使用[CloudFoundry 用户帐户和认证（UAA）服务器](https://github.com/cloudfoundry/uaa)。

UAAOpenID 不仅得到了认证，并由 Cloud Foundry 使用，而且你还可以在本地独立部署场景中使用它。此外，UAA 不仅提供了自己的用户存储，而且还提供了全面的 LDAP 集成。

#### 9.1.启用 HTTPS

默认情况下，仪表板、管理和健康端点使用 HTTP 作为传输。你可以通过在`application.yml`中的配置中添加一个证书来切换到 HTTPS，如下例所示：

```
server:
  port: 8443                                         (1)
  ssl:
    key-alias: yourKeyAlias                          (2)
    key-store: path/to/keystore                      (3)
    key-store-password: yourKeyStorePassword         (4)
    key-password: yourKeyPassword                    (5)
    trust-store: path/to/trust-store                 (6)
    trust-store-password: yourTrustStorePassword     (7)
```

|**1**|由于默认端口是`9393`，你可以选择将该端口更改为更常见的 HTTPS 典型端口。|
|-----|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|密钥存储在密钥存储库中的别名（或名称）。|
|**3**|密钥存储文件的路径。你还可以通过使用 Classpath 前缀来指定 Classpath 资源-例如：`classpath:path/to/keystore`。|
|**4**|密钥存储库的密码。|
|**5**|密钥的密码。|
|**6**|信任存储文件的路径。你还可以通过使用 Classpath 前缀来指定 Classpath 资源-例如：`classpath:path/to/trust-store`|
|**7**|信任存储的密码。|

|   |如果启用了 HTTPS，它将完全取代 HTTP 作为<br/>上的协议，REST 端点和数据流仪表板将在该协议上进行交互。普通 HTTP 请求<br/>失败。因此，请确保相应地配置你的 shell。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

##### 使用自签名证书

出于测试目的或在开发过程中，创建自签名证书可能比较方便。要开始，请执行以下命令来创建证书：

```
$ keytool -genkey -alias dataflow -keyalg RSA -keystore dataflow.keystore \
          -validity 3650 -storetype JKS \
          -dname "CN=localhost, OU=Spring, O=Pivotal, L=Kailua-Kona, ST=HI, C=US"  (1)
          -keypass dataflow -storepass dataflow
```

|**1**|`CN`是这里的重要参数。它应该匹配你试图访问的域-例如，`localhost`。|
|-----|---------------------------------------------------------------------------------------------------------------------|

然后将以下行添加到你的`application.yml`文件中：

```
server:
  port: 8443
  ssl:
    enabled: true
    key-alias: dataflow
    key-store: "/your/path/to/dataflow.keystore"
    key-store-type: jks
    key-store-password: dataflow
    key-password: dataflow
```

这就是数据流服务器所需做的全部工作。一旦启动服务器，你应该能够在`[localhost:8443/](https://localhost:8443/)`上访问它。由于这是一个自签名的证书，你应该在浏览器中点击一个警告，你需要忽略它。

|   |*从来没有*在生产中使用自签名证书。|
|---|---------------------------------------------------|

##### 自签名证书和 shell

默认情况下，自签名证书是 shell 的一个问题，需要额外的步骤才能使 shell 与自签名证书一起工作。有两种选择：

* 将自签名证书添加到 JVM 信任存储库中。

* 跳过证书验证。

###### 将自签名证书添加到 JVM 信任存储库

为了使用 JVM 信任存储库选项，你需要从密钥存储库导出先前创建的证书，如下所示：

```
$ keytool -export -alias dataflow -keystore dataflow.keystore -file dataflow_cert -storepass dataflow
```

接下来，你需要创建一个 shell 可以使用的信任库，如下所示：

```
$ keytool -importcert -keystore dataflow.truststore -alias dataflow -storepass dataflow -file dataflow_cert -noprompt
```

现在，你已经准备好使用以下 JVM 参数启动数据流壳层了：

```
$ java -Djavax.net.ssl.trustStorePassword=dataflow \
       -Djavax.net.ssl.trustStore=/path/to/dataflow.truststore \
       -Djavax.net.ssl.trustStoreType=jks \
       -jar spring-cloud-dataflow-shell-2.9.2.jar
```

|   |如果在通过 SSL 建立连接时遇到麻烦，可以使用<br/>日志，并将`javax.net.debug`JVM 参数设置为`ssl`。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|

不要忘记使用以下命令锁定数据流服务器：

```
dataflow:> dataflow config server https://localhost:8443/
```

###### 跳过证书验证

或者，你也可以通过提供可选的`--dataflow.skip-ssl-validation=true`命令行参数来绕过认证验证。

如果你设置了这个命令行参数，shell 将接受任何（自签名的）SSL 证书。

|   |如果可能的话，你应该避免使用这个选项。禁用信任管理器<br/>会破坏 SSL 的目的，并使你的应用程序容易受到中间人攻击。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 9.2.使用 OAuth2.0 进行身份验证

为了支持身份验证和授权， Spring 云数据流使用[OAuth 2.0](https://oauth.net/2/)。它允许你将 Spring 云数据流集成到单点登录（SSO）环境中。

|   |Spring 在云数据流 2.0 中，OAuth2 是用于提供身份验证和授权的唯一机制<br/>。|
|---|------------------------------------------------------------------------------------------------------------------|

使用了以下 OAuth2 授予类型：

* **授权代码**:用于 GUI（浏览器）集成。访问者将被重定向到你的 OAuth 服务进行身份验证。

* **密码**:由 shell（和 REST 集成）使用，因此访问者可以使用用户名和密码登录

* **客户凭据**:直接从 OAuth 提供程序检索访问令牌，并使用授权 HTTP 头将其传递给数据流服务器

|   |当前， Spring 云数据流使用不透明的令牌和不透明的<br/>令牌。|
|---|------------------------------------------------------------------------------------------|

你可以通过两种方式访问 REST 端点：

* **基本身份验证**，它使用*密码授予类型*对你的 OAuth2 服务进行身份验证

* **访问令牌**，它使用客户端*凭据授予类型*

|   |当你设置身份验证时，你确实应该启用 HTTPS，尤其是在生产环境中。|
|---|------------------------------------------------------------------------------------------------------------------|

你可以通过向`application.yml`添加以下内容或通过设置环境变量来打开 OAuth2 身份验证。下面的示例显示了[CloudFoundry 用户帐户和认证（UAA）服务器](https://github.com/cloudfoundry/uaa)所需的最小设置：

```
spring:
  security:
    oauth2:                                                           (1)
      client:
        registration:
          uaa:                                                        (2)
            client-id: myclient
            client-secret: mysecret
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            authorization-grant-type: authorization_code
            scope:
            - openid                                                  (3)
        provider:
          uaa:
            jwk-set-uri: http://uaa.local:8080/uaa/token_keys
            token-uri: http://uaa.local:8080/uaa/oauth/token
            user-info-uri: http://uaa.local:8080/uaa/userinfo    (4)
            user-name-attribute: user_name                            (5)
            authorization-uri: http://uaa.local:8080/uaa/oauth/authorize
      resourceserver:
        opaquetoken:
          introspection-uri: http://uaa.local:8080/uaa/introspect (6)
          client-id: dataflow
          client-secret: dataflow
```

|**1**|提供此属性将激活 OAuth2 安全性。|
|-----|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|提供者 ID。你可以指定多个提供者。|
|**3**|由于 UAA 是 OpenID 提供程序，因此你必须至少指定`openid`范围。<br/>如果你的提供程序还提供了其他范围来控制角色分配，<br/>你还必须在此指定这些范围。|
|**4**|OpenID 端点。用于检索用户信息，如用户名。强制性的。|
|**5**|包含用户名的响应的 JSON 属性。|
|**6**|用于内省和验证一个直接传入的令牌。强制性的。|

你可以使用 curl 验证基本身份验证是否正常工作，如下所示：

```
curl -u myusername:mypassword http://localhost:9393/ -H 'Accept: application/json'
```

因此，你应该看到一个可用的 REST 端点列表。

|   |当你使用 Web 浏览器访问根 URL 并启用<br/>安全性时，你将被重定向到仪表板 UI。要查看休息端点的<br/>列表，请指定`application/json``Accept`标头。也要确保<br/>通过使用[Postman](https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en)（Chrome）<br/>或[RESTClient](https://addons.mozilla.org/en-GB/firefox/addon/restclient/)（Firefox）等工具添加`Accept`头。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

除了基本的身份验证，你还可以提供一个访问令牌，以访问 REST API。为此，从你的 OAuth2 提供程序检索一个 OAuth2 访问令牌，并使用**授权**HTTP 报头将该访问令牌传递给 REST API，如下所示：

```
$ curl -H "Authorization: Bearer <ACCESS_TOKEN>" http://localhost:9393/ -H 'Accept: application/json'
```

#### 9.3.自定义授权

前面的内容主要涉及身份验证——即如何评估用户的身份。在本节中，我们将讨论可用的 ** 授权 ** 选项—即谁可以做什么。

该授权规则在`dataflow-server-defaults.yml`（ Spring 云数据流核心模块的一部分）中定义。

由于安全角色的确定是特定于环境的， Spring 云数据流在默认情况下将所有角色分配给经过身份验证的 OAuth2 用户。`DefaultDataflowAuthoritiesExtractor`类用于此目的。

或者，通过将你的提供者的布尔属性`map-oauth-scopes`设置为`true`（默认值为`false`），可以将 Spring 云数据流映射到数据流角色。例如，如果你的提供者的 ID 是`uaa`，则属性将是`spring.cloud.dataflow.security.authorization.provider-role-mappings.uaa.map-oauth-scopes`。

有关更多详细信息，请参见[角色映射](#configuration-security-role-mapping)一章。

你还可以通过提供你自己的 Spring  Bean 定义来定制角色映射行为，该定义扩展了 Spring 云数据流的`AuthorityMapper`接口。在这种情况下，自定义 Bean 的定义优先于 Spring 云数据流提供的默认定义。

默认方案使用七个角色来保护 Spring 云数据流公开的[REST 端点](#api-guide):

* **角色 \_ 创建**:用于任何涉及创建的内容，例如创建流或任务

* **角色 \_ 部署**:用于部署流或启动任务

* **角色 \_destroy**:用于任何涉及删除流、任务等的内容。

* **角色 \_ 管理**:用于引导管理端点

* **角色 \_ 修改**:对于任何涉及系统状态突变的内容

* **角色 \_ 日程安排**:用于调度相关操作（例如调度任务）

* **角色 \_ 视图**:对于任何与检索状态有关的内容

正如本节前面提到的，所有与授权相关的默认设置都在`dataflow-server-defaults.yml`中指定，这是 Spring 云数据流核心模块的一部分。尽管如此，如果需要，你可以重写这些设置——例如，在`application.yml`中。该配置采用 YAML 列表的形式（因为某些规则可能优先于其他规则）。因此，你需要复制和粘贴整个列表，并根据你的需要对其进行定制（因为无法合并列表）。

|   |始终引用`application.yml`文件的版本，因为下面的代码片段可能已经过时了。|
|---|-----------------------------------------------------------------------------------------------------|

默认规则如下：

```
spring:
  cloud:
    dataflow:
      security:
        authorization:
          enabled: true
          loginUrl: "/"
          permit-all-paths: "/authenticate,/security/info,/assets/**,/dashboard/logout-success-oauth.html,/favicon.ico"
          rules:
            # About

            - GET    /about                          => hasRole('ROLE_VIEW')

            # Audit

            - GET /audit-records                     => hasRole('ROLE_VIEW')
            - GET /audit-records/**                  => hasRole('ROLE_VIEW')

            # Boot Endpoints

            - GET /management/**                  => hasRole('ROLE_MANAGE')

            # Apps

            - GET    /apps                           => hasRole('ROLE_VIEW')
            - GET    /apps/**                        => hasRole('ROLE_VIEW')
            - DELETE /apps/**                        => hasRole('ROLE_DESTROY')
            - POST   /apps                           => hasRole('ROLE_CREATE')
            - POST   /apps/**                        => hasRole('ROLE_CREATE')
            - PUT    /apps/**                        => hasRole('ROLE_MODIFY')

            # Completions

            - GET /completions/**                    => hasRole('ROLE_VIEW')

            # Job Executions & Batch Job Execution Steps && Job Step Execution Progress

            - GET    /jobs/executions                => hasRole('ROLE_VIEW')
            - PUT    /jobs/executions/**             => hasRole('ROLE_MODIFY')
            - GET    /jobs/executions/**             => hasRole('ROLE_VIEW')
            - GET    /jobs/thinexecutions            => hasRole('ROLE_VIEW')

            # Batch Job Instances

            - GET    /jobs/instances                 => hasRole('ROLE_VIEW')
            - GET    /jobs/instances/*               => hasRole('ROLE_VIEW')

            # Running Applications

            - GET    /runtime/streams                => hasRole('ROLE_VIEW')
            - GET    /runtime/streams/**             => hasRole('ROLE_VIEW')
            - GET    /runtime/apps                   => hasRole('ROLE_VIEW')
            - GET    /runtime/apps/**                => hasRole('ROLE_VIEW')

            # Stream Definitions

            - GET    /streams/definitions            => hasRole('ROLE_VIEW')
            - GET    /streams/definitions/*          => hasRole('ROLE_VIEW')
            - GET    /streams/definitions/*/related  => hasRole('ROLE_VIEW')
            - POST   /streams/definitions            => hasRole('ROLE_CREATE')
            - DELETE /streams/definitions/*          => hasRole('ROLE_DESTROY')
            - DELETE /streams/definitions            => hasRole('ROLE_DESTROY')

            # Stream Deployments

            - DELETE /streams/deployments/*          => hasRole('ROLE_DEPLOY')
            - DELETE /streams/deployments            => hasRole('ROLE_DEPLOY')
            - POST   /streams/deployments/**         => hasRole('ROLE_MODIFY')
            - GET    /streams/deployments/**         => hasRole('ROLE_VIEW')

            # Stream Validations

            - GET /streams/validation/               => hasRole('ROLE_VIEW')
            - GET /streams/validation/*              => hasRole('ROLE_VIEW')

            # Stream Logs
            - GET /streams/logs/*                    => hasRole('ROLE_VIEW')

            # Task Definitions

            - POST   /tasks/definitions              => hasRole('ROLE_CREATE')
            - DELETE /tasks/definitions/*            => hasRole('ROLE_DESTROY')
            - GET    /tasks/definitions              => hasRole('ROLE_VIEW')
            - GET    /tasks/definitions/*            => hasRole('ROLE_VIEW')

            # Task Executions

            - GET    /tasks/executions               => hasRole('ROLE_VIEW')
            - GET    /tasks/executions/*             => hasRole('ROLE_VIEW')
            - POST   /tasks/executions               => hasRole('ROLE_DEPLOY')
            - POST   /tasks/executions/*             => hasRole('ROLE_DEPLOY')
            - DELETE /tasks/executions/*             => hasRole('ROLE_DESTROY')

            # Task Schedules

            - GET    /tasks/schedules                => hasRole('ROLE_VIEW')
            - GET    /tasks/schedules/*              => hasRole('ROLE_VIEW')
            - GET    /tasks/schedules/instances      => hasRole('ROLE_VIEW')
            - GET    /tasks/schedules/instances/*    => hasRole('ROLE_VIEW')
            - POST   /tasks/schedules                => hasRole('ROLE_SCHEDULE')
            - DELETE /tasks/schedules/*              => hasRole('ROLE_SCHEDULE')

            # Task Platform Account List */

            - GET    /tasks/platforms                => hasRole('ROLE_VIEW')

            # Task Validations

            - GET    /tasks/validation/               => hasRole('ROLE_VIEW')
            - GET    /tasks/validation/*              => hasRole('ROLE_VIEW')

            # Task Logs
            - GET /tasks/logs/*                       => hasRole('ROLE_VIEW')

            # Tools

            - POST   /tools/**                       => hasRole('ROLE_VIEW')
```

每一行的格式如下：

```
HTTP_METHOD URL_PATTERN '=>' SECURITY_ATTRIBUTE
```

地点：

* HTTP\_Method 是一种 HTTP 方法（如 PUT 或 GET），大写的情况。

* URL\_Pattern 是一种 Ant 风格的 URL 模式。

* Security\_Attribute 是一个 SPEL 表达式。见[基于表达式的访问控制](https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#el-access)。

* 每个字符都用一个或空白字符（空格、制表符等）隔开。

请注意，上面是一个 YAML 列表，而不是一个位于`spring.cloud.dataflow.security.authorization.rules`键下的映射（因此在每一行的开始处使用’-’破折号）。

##### 授权——shell 和 dashboard 行为

当启用安全性时，仪表板和外壳是角色感知的，这意味着，根据分配的角色，并非所有功能都是可见的。

例如，用户没有必要角色的 shell 命令被标记为不可用。

|   |目前，shell 的`help`命令列出了不可用的命令。<br/>请跟踪以下问题：[github.com/spring-projects/spring-shell/issues/115](https://github.com/spring-projects/spring-shell/issues/115)|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

相反，对于仪表板，UI 不会显示用户未获得授权的页面或页面元素。

##### 保护 Spring 引导管理端点

启用安全性时，[Spring Boot HTTP Management Endpoints](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/html/production-ready-monitoring.html)将以与其他 REST 端点相同的方式进行安全性保护。管理 REST 端点在`/management`下可用，并且需要`MANAGEMENT`角色。

`dataflow-server-defaults.yml`中的默认配置如下：

```
management:
  endpoints:
    web:
      base-path: /management
  security:
    roles: MANAGE
```

|   |目前，你不应该自定义默认的管理路径。|
|---|----------------------------------------------------------------|

#### 9.4.建立 UAA 身份验证

对于本地部署场景，我们建议使用[CloudFoundry 用户帐户和认证（UAA）服务器](https://github.com/cloudfoundry/uaa)，即[OpenID 认证](https://openid.net/certification/)。虽然 UAA 由[Cloud Foundry](https://www.cloudfoundry.org/)使用，但它也是一个功能齐全的独立 OAuth2 服务器，具有 Enterprise 功能，例如[LDAP 集成](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md)。

##### 所需经费

你需要签出、构建和运行 UAA。要做到这一点，请确保：

* 使用 Java8.

* 已安装[Git](https://git-scm.com/)。

* 安装[CloudFoundry UAA 命令行客户端](https://github.com/cloudfoundry/cf-uaac)。

* 在同一台计算机上运行时，对 UAA 使用不同的主机名——例如，`[uaa/](http://uaa/)`。

如果在安装*UAAC*时遇到问题，则可能需要设置`GEM_HOME`环境变量：

```
export GEM_HOME="$HOME/.gem"
```

你还应该确保`~/.gem/gems/cf-uaac-4.2.0/bin`已被添加到你的路径中。

##### 为 JWT 编写 UAA

由于 UAA 是一个 OpenID 提供者，并且使用 JSON Web 令牌，因此它需要一个私钥来对这些 JWTS 进行签名：

```
openssl genrsa -out signingkey.pem 2048
openssl rsa -in signingkey.pem -pubout -out verificationkey.pem
export JWT_TOKEN_SIGNING_KEY=$(cat signingkey.pem)
export JWT_TOKEN_VERIFICATION_KEY=$(cat verificationkey.pem)
```

稍后，一旦 UAA 被启动，你就可以在访问`[uaa:8080/uaa/token_keys](http://uaa:8080/uaa/token_keys)`时看到这些键。

|   |在这里，URL`[uaa:8080/uaa/token_keys](http://uaa:8080/uaa/token_keys)`中的`uaa`是主机名。|
|---|-------------------------------------------------------------------------------------------------------|

##### 下载并启动 UAA

要下载和安装 UAA，请运行以下命令：

```
git clone https://github.com/pivotal/uaa-bundled.git
cd uaa-bundled
./mvnw clean install
java -jar target/uaa-bundled-1.0.0.BUILD-SNAPSHOT.jar
```

UAA 的配置由 YAML 文件`uaa.yml`驱动，或者你可以使用 UAA 命令行客户端编写配置脚本：

```
uaac target http://uaa:8080/uaa
uaac token client get admin -s adminsecret
uaac client add dataflow \
  --name dataflow \
  --secret dataflow \
  --scope cloud_controller.read,cloud_controller.write,openid,password.write,scim.userids,sample.create,sample.view,dataflow.create,dataflow.deploy,dataflow.destroy,dataflow.manage,dataflow.modify,dataflow.schedule,dataflow.view \
  --authorized_grant_types password,authorization_code,client_credentials,refresh_token \
  --authorities uaa.resource,dataflow.create,dataflow.deploy,dataflow.destroy,dataflow.manage,dataflow.modify,dataflow.schedule,dataflow.view,sample.view,sample.create\
  --redirect_uri http://localhost:9393/login \
  --autoapprove openid

uaac group add "sample.view"
uaac group add "sample.create"
uaac group add "dataflow.view"
uaac group add "dataflow.create"

uaac user add springrocks -p mysecret --emails [email protected]
uaac user add vieweronly -p mysecret --emails [email protected]

uaac member add "sample.view" springrocks
uaac member add "sample.create" springrocks
uaac member add "dataflow.view" springrocks
uaac member add "dataflow.create" springrocks
uaac member add "sample.view" vieweronly
```

前面的脚本设置了 DataFlow 客户机和两个用户：

* 用户*洒水车*同时具有两个作用域：`sample.view`和`sample.create`。

* 用户*仅限 Vieweronly*只有一个作用域：`sample.view`。

添加后，你可以快速重复检查 UAA 是否已创建用户：

```
curl -v -d"username=springrocks&password=mysecret&client_id=dataflow&grant_type=password" -u "dataflow:dataflow" http://uaa:8080/uaa/oauth/token -d 'token_format=opaque'
```

前面的命令应该产生类似于下面的输出：

```
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to uaa (127.0.0.1) port 8080 (#0)
* Server auth using Basic with user 'dataflow'
> POST /uaa/oauth/token HTTP/1.1
> Host: uaa:8080
> Authorization: Basic ZGF0YWZsb3c6ZGF0YWZsb3c=
> User-Agent: curl/7.54.0
> Accept: */*
> Content-Length: 97
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 97 out of 97 bytes
< HTTP/1.1 200
< Cache-Control: no-store
< Pragma: no-cache
< X-XSS-Protection: 1; mode=block
< X-Frame-Options: DENY
< X-Content-Type-Options: nosniff
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Thu, 31 Oct 2019 21:22:59 GMT
<
* Connection #0 to host uaa left intact
{"access_token":"0329c8ecdf594ee78c271e022138be9d","token_type":"bearer","id_token":"eyJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vbG9jYWxob3N0OjgwODAvdWFhL3Rva2VuX2tleXMiLCJraWQiOiJsZWdhY3ktdG9rZW4ta2V5IiwidHlwIjoiSldUIn0. .bqYvicyCPB5cIIu_2HEe5_c7nSGXKw7B8-reTvyYjOQ2qXSMq7gzS4LCCQ-CMcb4IirlDaFlQtZJSDE-_UsM33-ThmtFdx--TujvTR1u2nzot4Pq5A_ThmhhcCB21x6-RNNAJl9X9uUcT3gKfKVs3gjE0tm2K1vZfOkiGhjseIbwht2vBx0MnHteJpVW6U0pyCWG_tpBjrNBSj9yLoQZcqrtxYrWvPHaa9ljxfvaIsOnCZBGT7I552O1VRHWMj1lwNmRNZy5koJFPF7SbhiTM8eLkZVNdR3GEiofpzLCfoQXrr52YbiqjkYT94t3wz5C6u1JtBtgc2vq60HmR45bvg","refresh_token":"6ee95d017ada408697f2d19b04f7aa6c-r","expires_in":43199,"scope":"scim.userids openid sample.create cloud_controller.read password.write cloud_controller.write sample.view","jti":"0329c8ecdf594ee78c271e022138be9d"}
```

通过使用`token_format`参数，你可以请求令牌为：

* 不透明

* JWT

## 10. 配置-本地

### 10.1.功能切换

Spring 云数据流服务器提供了一组特定的特性，这些特性可以在启动时启用/禁用。这些特性包括用于以下方面的所有生命周期操作和 REST 端点（服务器和客户端实现，包括 shell 和 UI）：

* Streams（需要 Skipper）

* 任务

* 任务调度程序

在启动数据流服务器时，可以通过设置以下布尔属性来启用和禁用这些功能：

* `spring.cloud.dataflow.features.streams-enabled`

* `spring.cloud.dataflow.features.tasks-enabled`

* `spring.cloud.dataflow.features.schedules-enabled`

默认情况下，Stream（需要 Skipper）和 Tasks 是启用的，而 Task Scheduler 是默认情况下禁用的。

REST`/about`端点提供关于已启用和禁用的特性的信息。

### 10.2.数据库

关系数据库用于存储流和任务定义以及执行任务的状态。 Spring 云数据流为**H2**、**MySQL**、**甲骨文**、**PostgreSQL**、**DB2**和**SQL 服务器**提供模式。当服务器启动时，架构会自动创建。

默认情况下， Spring 云数据流提供了**H2**数据库的嵌入式实例。**H2**数据库适合用于开发目的，但不建议用于生产用途。

|   |**H2**数据库不支持作为外部模式。|
|---|-----------------------------------------------------|

**MySQL**（通过 MariaDB 驱动程序）、**PostgreSQL**、**SQL 服务器**和嵌入式**H2**的 JDBC 驱动程序是可用的，无需额外配置。如果你正在使用任何其他数据库，那么你需要在服务器的 Classpath 上放置相应的 JDBC 驱动程序 JAR。

数据库属性可以作为环境变量或命令行参数传递给数据流服务器。

#### 10.2.1.MySQL

下面的示例展示了如何使用 MariaDB 驱动程序定义 MySQL 数据库连接。

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:mysql://localhost:3306/mydb \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
```

最高*5.7*的 MySQL 版本可以与 MariaDB 驱动程序一起使用。从版本*8.0*开始，必须使用 MySQL 自己的驱动程序。

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:mysql://localhost:3306/mydb \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=com.mysql.jdbc.Driver
```

|   |由于许可证限制，我们无法捆绑 MySQL 驱动程序。你需要自己将其添加到<br/>服务器的 Classpath 中。|
|---|-------------------------------------------------------------------------------------------------------------------------|

#### 10.2.2.马里亚布

下面的示例展示了如何使用命令行参数定义 MariaDB 数据库连接。

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:mariadb://localhost:3306/mydb?useMysqlMetadata=true \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
```

从 MariaDB V2.4.1Connector 发行版开始，还需要向 JDBC URL 添加`useMysqlMetadata=true`。在 MySQL 和 MariaDB 完全转换为两个不同的数据库之前，这是一个必需的解决方案。

MariaDB 版本*10.3*引入了对真实数据库序列的支持，这是又一个突破性的变化，而这些数据库周围的工具完全支持 MySQL 和 MariaDB 作为单独的数据库类型。解决办法是使用较老的方言，而不是尝试使用序列。

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:mariadb://localhost:3306/mydb?useMysqlMetadata=true \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDB102Dialect \
    --spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
```

#### 10.2.3.PostgreSQL

下面的示例展示了如何使用命令行参数定义 PostgreSQL 数据库连接：

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:postgresql://localhost:5432/mydb \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=org.postgresql.Driver
```

#### 10.2.4.SQL 服务器

下面的示例展示了如何使用命令行参数定义 SQL Server 数据库连接：

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url='jdbc:sqlserver://localhost:1433;databaseName=mydb' \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
```

#### 10.2.5.DB2

下面的示例展示了如何使用命令行参数定义 DB2 数据库连接：

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:db2://localhost:50000/mydb \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=com.ibm.db2.jcc.DB2Driver
```

|   |由于许可限制，我们无法捆绑 DB2Driver。你需要自己将其添加到<br/>服务器的 Classpath 中。|
|---|-----------------------------------------------------------------------------------------------------------------------|

#### 10.2.6.甲骨文

下面的示例展示了如何使用命令行参数定义 Oracle 数据库连接：

```
java -jar spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.9.2.jar \
    --spring.datasource.url=jdbc:oracle:thin:@localhost:1521/MYDB \
    --spring.datasource.username= \
    --spring.datasource.password= \
    --spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
```

|   |由于许可证限制，我们无法捆绑 Oracle Driver。你需要自己将其添加到<br/>服务器的 Classpath 中。|
|---|--------------------------------------------------------------------------------------------------------------------------|

#### 10.2.7.添加自定义 JDBC 驱动程序

要为数据库添加自定义驱动程序（例如，Oracle），你应该重建数据流服务器，并将依赖项添加到 Maven `pom.xml`文件中。你需要修改`spring-cloud-dataflow-server`模块的 Maven `pom.xml`。GitHub 存储库中有 GA 发布标记，因此你可以切换到所需的 GA 标记，以便在生产就绪代码库中添加驱动程序。

要为 Spring 云数据流服务器添加自定义的 JDBC 驱动程序依赖项：

1. 选择与要重建和克隆 GitHub 存储库的服务器版本对应的标记。

2. 编辑 Spring-cloud-dataflow-server/ POM.xml，并在`dependencies`部分中添加所需的数据库驱动程序的依赖项。在下面的示例中，选择了一个 Oracle 驱动程序：

```
<dependencies>
...
  <dependency>
    <groupId>com.oracle.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>12.2.0.1</version>
  </dependency>
...
</dependencies>
```

1. 按照[Building Spring Cloud Data Flow](#building)中所述构建应用程序

还可以通过向 dataFlow-server.yml 文件添加必要的属性，在重建服务器时提供默认值，如下面的 PostgreSQL 示例所示：

```
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: myuser
    password: mypass
    driver-class-name:org.postgresql.Driver
```

1. 或者，你可以使用你的构建文件构建自定义的云数据流服务器 Spring。如果需要添加驱动程序 JAR，则在[回购样本](https://github.com/spring-cloud/spring-cloud-dataflow-samples/tree/master/custom-dataflow-builds)中有自定义服务器构建的示例。

#### 10.2.8.模式处理

在默认情况下，数据库模式是用*飞道*管理的，如果可以给数据库用户足够的权限，这是很方便的。

下面是对*船长*服务器启动时发生的情况的描述：

* Flyway 检查`flyway_schema_history`表是否存在。

* 如果架构不是空的，那么基线（到版本 1）是否存在，因为如果使用共享 DB，*数据流*表可能就在适当的位置。

* 如果模式为空，Flyway 假定从头开始。

* 完成所有需要的模式迁移。

下面是对*数据流*服务器启动时发生的情况的描述：

* Flyway 检查`flyway_schema_history_dataflow`表是否存在。

* 如果架构不是空的，那么基线（到版本 1）是否存在，因为如果使用共享 DB，*船长*表可能就在适当的位置。

* 如果模式为空，Flyway 假定从头开始。

* 完成所有需要的模式迁移。

* 由于历史原因，如果我们检测到模式来自*1.7.x*行，我们将这些转换为*2.0.x*起所需的结构，并完全继续使用 Flyway。

|   |我们的源代码[schemas](https://github.com/spring-cloud/spring-cloud-dataflow/tree/master/spring-cloud-dataflow-server-core/src/main/resources/schemas)中有模式 DDL，如果*飞道*通过使用配置`spring.flyway.enabled=false`禁用，则可以手动使用它。如果公司的数据库<br/>受到限制，即应用程序本身无法创建模式，那么这是一个很好的选择。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 10.3.部署人员属性

你可以使用[本地部署人员](https://github.com/spring-cloud/spring-cloud-deployer-local)的以下配置属性来自定义如何部署流和任务。当使用数据流 shell 进行部署时，可以使用语法`deployer.<appName>.local.<deployerPropertyName>`。参见下面的 shell 用法示例。这些属性还用于在数据流服务器和 Skipper 中的本地平台中配置[本地任务平台](#configuration-local-tasks)以部署流时使用。

|Deployer Property Name|                                              Description                                               |默认值|
|----------------------|--------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
|workingDirectoriesRoot|                Directory in which all created processes will run and create log files.                 |java.io.tmpdir|
|   envVarsToInherit   |Array of regular expression patterns for environment variables that are passed to launched applications.|Windows 上的 \<"TMP", "LANG", "LANGUAGE", "LC\_.\*", "PATH", "SPRING\_APPLICATION\_JSON"\>和 UNIX 上的 \<"TMP", "LANG", "LANGUAGE", "LC\_.\*", "PATH"\>|
|  deleteFilesOnExit   |                      Whether to delete created files and directories on JVM exit.                      |                                                                        true                                                                         |
|       javaCmd        |                                          Command to run java                                           |爪哇|
|   shutdownTimeout    |                            Max number of seconds to wait for app shutdown.                             |                                                                         30                                                                          |
|       javaOpts       |                          The Java Options to pass to the JVM, e.g -Dtest=foo                           |                                                                      \<none\>                                                                       |
|    inheritLogging    |    allow logging to be redirected to the output stream of the process that triggered child process.    |                                                                        false                                                                        |
|      debugPort       |                                       Port for remote debugging                                        |                                                                      \<none\>                                                                       |

例如，要在`ticktock`流中为 Time 应用程序设置 Java 选项，请使用以下流部署属性。

```
dataflow:> stream create --name ticktock --definition "time --server.port=9000 | log"
dataflow:> stream deploy --name ticktock --properties "deployer.time.local.javaOpts=-Xmx2048m -Dtest=foo"
```

为了方便起见，你可以设置`deployer.memory`属性来设置 Java 选项`-Xmx`，如下例所示：

```
dataflow:> stream deploy --name ticktock --properties "deployer.time.memory=2048m"
```

在部署时，如果在`deployer.<app>.local.javaOpts`属性中指定一个`-Xmx`选项以及`deployer.<app>.local.memory`选项的值，则`javaOpts`属性中的值具有优先权。此外，部署应用程序时的`javaOpts`属性集具有优于数据流服务器的`spring.cloud.deployer.local.javaOpts`属性的优先权。

### 10.4.伐木

Spring 云数据流`local`服务器被自动配置为使用`RollingFileAppender`进行日志记录。日志配置位于名为`logback-spring.xml`的文件中包含的 Classpath 上。

默认情况下，日志文件被配置为使用：

```
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring-cloud-dataflow-server}"/>
```

使用`RollingPolicy`的注销配置：

```
<appender name="FILE"
			  class="ch.qos.logback.core.rolling.RollingFileAppender">
		<file>${LOG_FILE}.log</file>
		<rollingPolicy
				class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
			<!-- daily rolling -->
			<fileNamePattern>${LOG_FILE}.${LOG_FILE_ROLLING_FILE_NAME_PATTERN:-%d{yyyy-MM-dd}}.%i.gz</fileNamePattern>
			<maxFileSize>${LOG_FILE_MAX_SIZE:-100MB}</maxFileSize>
			<maxHistory>${LOG_FILE_MAX_HISTORY:-30}</maxHistory>
			<totalSizeCap>${LOG_FILE_TOTAL_SIZE_CAP:-500MB}</totalSizeCap>
		</rollingPolicy>
		<encoder>
			<pattern>${FILE_LOG_PATTERN}</pattern>
		</encoder>
	</appender>
```

要检查当前 Spring 云数据流服务器`java.io.tmpdir`服务器的`local`服务器，

```
jinfo <pid> | grep "java.io.tmpdir"
```

如果要更改或重写任何属性`LOG_FILE`、`LOG_TEMP`、`LOG_TEMP`、`LOG_FILE_MAX_SIZE`、`LOG_FILE_MAX_HISTORY`和`LOG_FILE_TOTAL_SIZE_CAP`，请将它们设置为系统属性。

### 10.5.溪流

数据流服务器将流的生命周期的管理委托给 Skipper 服务器。将配置属性`spring.cloud.skipper.client.serverUri`设置为 Skipper 的位置，例如

```
$ java -jar spring-cloud-dataflow-server-2.9.2.jar --spring.cloud.skipper.client.serverUri=https://192.51.100.1:7577/api
```

Show Streams 的配置是通过在 Skipper 服务器上配置`platform accounts`来部署的，以及部署到哪些平台。有关更多信息，请参见[platforms](https://docs.spring.io/spring-cloud-skipper/docs/current/reference/htmlsingle/#platforms)上的文档。

### 10.6.任务

数据流服务器负责部署任务。数据流启动的任务将其状态写入数据流服务器使用的同一数据库。对于 Spring 批处理作业的任务，作业和步骤执行数据也存储在该数据库中。与 Skipper 推出的流一样，任务可以在多个平台上启动。如果没有定义平台，则使用类[LocalDeployerProperties](https://github.com/spring-cloud/spring-cloud-deployer-local/blob/master/spring-cloud-deployer-local/src/main/java/org/springframework/cloud/deployer/spi/local/LocalDeployerProperties.java)的默认值创建一个名为`default`的平台，该值在表[本地部署人员属性](#configuration-local-deployer)中进行了总结

要为本地平台配置新的平台帐户，请在你的`application.yaml`文件中的`spring.cloud.dataflow.task.platform.local`节下通过另一种 Spring 启动支持的机制提供一个条目。在下面的示例中，创建了两个名为`localDev`和`localDevDebug`的本地平台帐户。诸如`shutdownTimeout`和`javaOpts`之类的键是本地部署程序属性。

```
spring:
  cloud:
    dataflow:
      task:
        platform:
          local:
            accounts:
              localDev:
                shutdownTimeout: 60
                javaOpts: "-Dtest=foo -Xmx1024m"
              localDevDebug:
                javaOpts: "-Xdebug -Xmx2048m"
```

|   |通过将一个平台定义为`default`，你可以跳过使用`platformName`，否则将需要使用该平台。|
|---|------------------------------------------------------------------------------------------------------------------------|

启动任务时，使用 Task Launch 选项`--platformName`传递平台帐户名的值`platformName`如果不传递`platformName`的值，则将使用该值`default`。

|   |当将任务部署到多个平台时，任务的配置需要连接到与数据流服务器相同的数据库。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------|

你可以配置本地运行的数据流服务器，以便将任务部署到 Cloud Foundry 或 Kubernetes。有关更多信息，请参见[Cloud Foundry 任务平台配置](#configuration-cloudfoundry-tasks)和[Kubernetes 任务平台配置](#configuration-kubernetes-tasks)部分。

在[dataflow.spring.io](http://dataflow.spring.io)上的[对任务的多平台支持](https://dataflow.spring.io/docs/recipes/multi-platform-deployment/)一节中提供了跨多个平台启动和调度任务的详细示例。

##### 开始船长

```
git clone https://github.com/spring-cloud/spring-cloud-skipper.git
cd spring-cloud/spring-cloud-skipper
./mvnw clean package -DskipTests=true
java -jar spring-cloud-skipper-server/target/spring-cloud-skipper-server-2.2.0.BUILD-SNAPSHOT.jar
```

##### 启动 Spring 云数据流

```
git clone https://github.com/spring-cloud/spring-cloud-dataflow.git
cd spring-cloud-dataflow
./mvnw clean package -DskipTests=true
cd ..
```

创建一个包含以下内容的 YAML 文件 scdf.yml:

```
spring:
  cloud:
    dataflow:
      security:
        authorization:
          provider-role-mappings:
            uaa:
              map-oauth-scopes: true
              role-mappings:
                ROLE_CREATE: foo.create
                ROLE_DEPLOY: foo.create
                ROLE_DESTROY: foo.create
                ROLE_MANAGE: foo.create
                ROLE_MODIFY: foo.create
                ROLE_SCHEDULE: foo.create
                ROLE_VIEW: foo.view
  security:
    oauth2:
      client:
        registration:
          uaa:
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            authorization-grant-type: authorization_code
            client-id: dataflow
            client-secret: dataflow
            scope:                                                       (1)
            - openid
            - foo.create
            - foo.view
        provider:
          uaa:
            jwk-set-uri: http://uaa:8080/uaa/token_keys
            token-uri: http://uaa:8080/uaa/oauth/token
            user-info-uri: http://uaa:8080/uaa/userinfo                  (2)
            user-name-attribute: user_name
            authorization-uri: http://uaa:8080/uaa/oauth/authorize
      resourceserver:
        opaquetoken:                                                     (3)
          introspection-uri: http://uaa:8080/uaa/introspect
          client-id: dataflow
          client-secret: dataflow
```

|**1**|如果你使用作用域来标识角色，请确保还请求<br/>相关的作用域，例如`dataflow.view`，`dataflow.create`，并且不要忘记请求`openid`作用域|
|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|用于检索配置文件信息，例如用于显示目的的用户名（强制使用）|
|**3**|用于令牌内省和验证（强制）|

当向 Spring 云数据流传递外部检索的（不透明的）OAuth 访问令牌时，`introspection-uri`属性尤其重要。 Spring 在这种情况下，云数据流将获取 OAuth 访问，并使用 UAA 的[自检令牌端点](https://docs.cloudfoundry.org/api/uaa/version/74.4.0/index.html#introspect-token)不仅检查令牌的有效性，而且还从 UAA 检索相关联的 OAuth 范围

最后启动 Spring 云数据流：

```
java -jar spring-cloud-dataflow/spring-cloud-dataflow-server/target/spring-cloud-dataflow-server-2.4.0.BUILD-SNAPSHOT.jar --spring.config.additional-location=scdf.yml
```

##### 角色映射

默认情况下，所有角色都分配给登录到 Spring 云数据流的用户。但是，你可以设置属性：

`spring.cloud.dataflow.security.authorization.provider-role-mappings.uaa.map-oauth-scopes: true`

这将指示底层`DefaultAuthoritiesExtractor`将 OAuth 范围映射到相应的权限。支持以下范围：

* 范围`dataflow.create`映射到`CREATE`角色

* 范围`dataflow.deploy`映射到`DEPLOY`角色

* 范围`dataflow.destroy`映射到`DESTROY`角色

* 范围`dataflow.manage`映射到`MANAGE`角色

* 范围`dataflow.modify`映射到`MODIFY`角色

* 范围`dataflow.schedule`映射到`SCHEDULE`角色

* 范围`dataflow.view`映射到`VIEW`角色

此外，你还可以将任意范围映射到每个数据流角色：

```
spring:
  cloud:
    dataflow:
      security:
        authorization:
          provider-role-mappings:
            uaa:
              map-oauth-scopes: true                                    (1)
              role-mappings:
                ROLE_CREATE: dataflow.create                            (2)
                ROLE_DEPLOY: dataflow.deploy
                ROLE_DESTROY: dataflow.destoy
                ROLE_MANAGE: dataflow.manage
                ROLE_MODIFY: dataflow.modify
                ROLE_SCHEDULE: dataflow.schedule
                ROLE_VIEW: dataflow.view
```

|**1**|启用从 OAuth 范围到数据流角色的显式映射支持|
|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|当启用角色映射支持时，你必须为<br/>所有 7 个 Spring 云数据流角色[Cloud Foundry Deployer 属性](#configuration-cloudfoundry-deployer)，**角色 \_ 部署**，**角色 \_destroy**，**角色 \_ 管理**，**角色 \_ 修改**，**角色 \_ 日程安排**提供映射。|

|   |你可以将 OAuth 范围分配给多个 Spring 云数据流角色，从而使你在授权配置的粒度方面具有灵活性。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 10.6.4.LDAP 身份验证

LDAP 身份验证（轻量级目录访问协议）是由 Spring 云数据流使用的 UAA 间接提供的。UAA 本身提供[全面的 LDAP 支持](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md)。

|   |虽然你可以使用自己的 OAuth2 身份验证服务器，但是这里记录的 LDAP 支持<br/>需要使用 UAA 作为身份验证服务器。对于任何<br/>其他提供者，请参阅该特定提供者的文档。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

UAA 支持使用以下模式对 LDAP（轻量级目录访问协议）服务器进行身份验证：

* [直接绑定](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md#ldap-search-and-bind)

* [搜索和绑定](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md#ldap-bind)

* [搜索和比较](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md#ldap-search-and-compare)

|   |当与外部身份提供程序（例如 LDAP）集成时，UAA 内的身份验证<br/>变为**锁链**。UAA 首先尝试使用<br/>用户的凭据，在外部提供者<br/>ldap 之前对 UAA 用户存储区进行身份验证。有关更多信息，请参见*用户帐户和身份验证 LDAP 集成*Github 文档中的[链式认证](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md#chained-authentication)。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

##### LDAP 角色映射

OAuth2 身份验证服务器（UAA）为[将 LDAP 组映射到 OAuth 范围](https://github.com/cloudfoundry/uaa/blob/develop/docs/UAA-LDAP.md#scopes)提供了全面的支持。

存在以下备选方案：

* `ldap/ldap-groups-null.xml`不会映射任何组

* `ldap/ldap-groups-as-scopes.xml`将从一个 LDAP 属性检索组名称。例如`CN`

* `ldap/ldap-groups-map-to-scopes.xml`组将使用外部 \_group\_ 映射表映射到 UAA 组。

这些值是通过配置属性`ldap.groups.file controls`指定的。在封面下，这些值引用一个 Spring XML 配置文件。

|   |在测试和开发过程中，可能需要对 LDAP 组和用户进行<br/>的频繁更改，并查看反映在 UAA 中的更改。但是，用户[secrets](https://kubernetes.io/docs/concepts/configuration/secret/)信息在登录期间被缓存。下面的脚本<br/>有助于快速检索更新的信息：<br/><br/>```<br/>#!/bin/bash<br/>uaac token delete --all<br/>uaac target http://localhost:8080/uaa<br/>uaac token owner get cf <username> -s "" -p  <password><br/>uaac token client get admin -s adminsecret<br/>uaac user get <username><br/>```|
|---| |

##### LDAP 安全和 UAA 示例应用程序

为了快速启动和运行并帮助你了解安全架构，我们在 Github 上提供了[LDAP 安全性和 UAA 示例](https://github.com/spring-cloud/spring-cloud-dataflow-samples/tree/master/security-ldap-uaa-example)。

|   |这只是一个演示/示例应用程序，不应在生产中使用。|
|---|------------------------------------------------------------------------------|

该设置包括：

* Spring 云数据流服务器

* Skipper 服务器

* CloudFoundry 用户帐户和认证（UAA）服务器

* 轻量级目录访问协议服务器（由[Apache 目录服务器](https://directory.apache.org/)提供）

最后，作为本示例的一部分，你将学习如何使用此安全设置配置和启动一个组合任务。

#### 10.6.5. Spring 安全 OAuth2 资源/授权服务器示例

对于本地测试和开发，你还可以使用[Spring Security OAuth](https://projects.spring.io/spring-security-oauth/)提供的资源和授权服务器支持。它允许你使用以下简单的注释轻松地创建自己的（非常基本的）OAuth2 服务器：

* `@EnableResourceServer`

* `@EnableAuthorizationServer`

|   |实际上，UAA 使用了 Spring 安全 OAuth2 下的覆盖，因此基本端点<br/>是相同的。|
|---|--------------------------------------------------------------------------------------------------------|

可以在以下位置找到一个工作示例应用程序：[https://github.com/ghillert/oauth-test-server/](https://github.com/ghillert/oauth-test-server/)

复制该项目并配置 Spring 云数据流，其中包含相应的客户机 ID 和客户机秘密：

```
security:
  oauth2:
    client:
      client-id: myclient
      client-secret: mysecret
      access-token-uri: http://127.0.0.1:9999/oauth/token
      user-authorization-uri: http://127.0.0.1:9999/oauth/authorize
    resource:
      user-info-uri: http://127.0.0.1:9999/me
      token-info-uri: http://127.0.0.1:9999/oauth/check_token
```

|   |此示例应用程序不用于生产。|
|---|----------------------------------------------------------|

#### 10.6.6.数据流壳身份验证

使用 shell 时，可以通过用户名和密码提供凭据，也可以通过指定*凭据-提供者*命令提供凭据。如果你的 OAuth2 提供程序支持*密码*grant 类型，那么你可以通过以下方式启动*数据流壳*:

```
$ java -jar spring-cloud-dataflow-shell-2.9.2.jar         \
  --dataflow.uri=http://localhost:9393                                \   (1)
  --dataflow.username=my_username                                     \   (2)
  --dataflow.password=my_password                                     \   (3)
  --skip-ssl-validation  true                                         \   (4)
```

|**1**|可选的，默认为[本地主机：9393](http://localhost:9393)。|
|-----|----------------------------------------------------------------------------------------------------------------|
|**2**|强制性的。|
|**3**|如果未提供密码，则会提示用户输入密码。|
|**4**|可选的，默认为`false`，忽略证书错误（当使用自签名证书时）。谨慎使用！|

|   |请记住，当启用 Spring 云数据流的身份验证时，如果你想通过用户名/密码身份验证使用 shell，则底层的 OAuth2 提供者支持OAuth2grant 类型。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

在 Data Flow 壳牌 中，你还可以使用以下命令提供凭据：

```
server-unknown:>dataflow config server                                \
  --uri  http://localhost:9393                                        \   (1)
  --username myuser                                                   \   (2)
  --password mysecret                                                 \   (3)
  --skip-ssl-validation  true                                         \   (4)
```

|**1**|可选的，默认为[本地主机：9393](http://localhost:9393)。|
|-----|-------------------------------------------------------------------------------------------|
|**2**|强制..|
|**3**|如果启用了安全性，但未提供密码，则会提示用户使用该密码。|
|**4**|可选的，忽略证书错误（当使用自签名证书时）。谨慎使用！|

下图显示了连接到数据流服务器并对其进行身份验证的典型 shell 命令：

![在 shell 中使用数据流服务器进行目标和身份验证](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-security-shell-target.png)

图 1.在 shell 中使用数据流服务器进行目标和身份验证

一旦成功锁定目标，你应该会看到以下输出：

```
dataflow:>dataflow config info
dataflow config info

╔═══════════╤═══════════════════════════════════════╗
║Credentials│[username='my_username, password=****']║
╠═══════════╪═══════════════════════════════════════╣
║Result     │                                       ║
║Target     │http://localhost:9393                  ║
╚═══════════╧═══════════════════════════════════════╝
```

或者，你可以指定*凭据-提供者*命令，以便直接传入一个承载令牌，而不是提供用户名和密码。这可以在 shell 中进行，或者在启动 shell 时提供`--dataflow.credentials-provider-command`命令行参数。

|   |当使用*凭据-提供者*命令时，请注意你的<br/>指定命令**必须**返回一个*不记名令牌*（访问令牌前缀为*承担者*）。*不记名令牌*例如，在 UNIX 环境中，可以使用以下简单化命令：<br/>```<br/>$ java -jar spring-cloud-dataflow-shell-2.9.2.jar \<br/>  --dataflow.uri=http://localhost:9393 \<br/>  --dataflow.credentials-provider-command="echo Bearer 123456789"<br/>```|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 10.7.关于配置

Spring 关于 RESTful API 结果的云数据流包含显示名称、版本，以及（如果指定的话）用于构成 Spring 云数据流的每个主要依赖项的 URL。结果（如果启用）还包含 Shell 依赖项的 SHA1 和或 SHA256 校验和。通过设置以下属性，可以配置为每个依赖项返回的信息：

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-core.name:用于核心的名称。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-core.version:用于内核的版本。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-dashboard.name:用于仪表板的名称。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-dashboard.version:用于仪表板的版本。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-implementation.name:用于实现的名称。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-implementation.version:用于实现的版本。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.name:用于 shell 的名称。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.version:用于 shell 的版本。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.url:用于下载 shell 依赖项的 URL。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.checksum-sha1:与 shell 依赖项信息一起返回的 sha1 校验和值。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.checksum-sha256:与 shell 依赖项信息一起返回的 sha256 校验和值。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.checksum-sha1-url:如果没有指定`spring.cloud.dataflow.version-info.spring-cloud-dataflow-shell.checksum-sha1`，SCDF 使用在此 URL 处指定的文件的内容进行校验和。

* Spring.cloud.dataflow.version-info. Spring-cloud-dataflow-shell.checksum-sha256-url:如果没有指定`port`，SCDF 使用在此 URL 处指定的文件的内容进行校验和。

#### 10.7.1.启用 shell 校验和值

默认情况下，不会为 shell 依赖项显示校验和值.如果需要启用此功能，请将`spring.cloud.dataflow.version-info.dependency-fetch.enabled`属性设置为 true。

#### 10.7.2.为 URL 保留的值

你可以将保留的值（由花括号包围）插入到 URL 中，以确保链接是最新的：

* 存储库：如果使用构建快照、里程碑或数据流的候选版本，则存储库引用 repo- Spring-io 存储库。否则，它指的是 Maven central。

* 版本：插入 JAR/ POM 的版本。

例如，如果你使用 Spring 云数据流 shell 的 1.2.3.3.Release 版本，则`[myrepository/org/springframework/cloud/spring-cloud-dataflow-shell/{version}/spring-cloud-dataflow-shell-{version}.jar](https://myrepository/org/springframework/cloud/spring-cloud-dataflow-shell/{version}/spring-cloud-dataflow-shell-{version}.jar)`生成`[myrepository/org/springframework/cloud/spring-cloud-dataflow-shell/1.2.3.RELEASE/spring-cloud-dataflow-shell-1.2.3.RELEASE.jar](https://myrepository/org/springframework/cloud/spring-cloud-dataflow-shell/1.2.3.RELEASE/spring-cloud-dataflow-shell-1.2.3.RELEASE.jar)`

## 11. 配置-Cloud Foundry

本节描述了如何配置 Spring 云数据流服务器的特性，例如安全性和使用哪个关系数据库。它还描述了如何配置 Spring Cloud Data Flow Shell 的功能。

### 11.1.功能切换

Data Flow Server 提供了一组特定的特性，你可以在启动时启用或禁用这些特性。这些特性包括用于以下方面的所有生命周期操作和 REST 端点（服务器、客户端实现，包括 Shell 和 UI）：

* 溪流

* 任务

在启动数据流服务器时，可以通过设置以下布尔属性来启用或禁用这些功能：

* `spring.cloud.dataflow.features.streams-enabled`

* `spring.cloud.dataflow.features.tasks-enabled`

默认情况下，所有功能都已启用。

REST 端点（`/features`）提供有关已启用和已禁用功能的信息。

### 11.2.部署人员属性

你可以使用数据流服务器的[Cloud Foundry 部署人员](https://github.com/spring-cloud/spring-cloud-deployer-cloudfoundry)的以下配置属性来定制应用程序的部署方式。在使用数据流壳层进行部署时，可以使用语法`deployer.<appName>.cloudfoundry.<deployerPropertyName>`。参见下面的 shell 用法示例。在配置数据流服务器中的[Cloud Foundry 任务平台](#configuration-cloudfoundry-tasks)和 Skipper 中的 Kubernetes 平台以部署流时，也会使用这些属性。

| Deployer Property Name  |说明|                                                                                                                   Default Value                                                                                                                   |
|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|        services         |要绑定到已部署应用程序的服务的名称。|                                                                                                                     \<none\>                                                                                                                      |
|          host           |作为路由的一部分使用的主机名。|                                                                                                         hostname derived by Cloud Foundry                                                                                                         |
|         domain          |当映射应用程序的路由时要使用的域。|                                                                                                                     \<none\>                                                                                                                      |
|         routes          |应用程序应该绑定到的路由列表。与主机和域互斥。|                                                                                                                     \<none\>                                                                                                                      |
|        buildpack        |用于部署应用程序的 buildpack。不赞成使用构建包。|                                                                 [github.com/cloudfoundry/java-buildpack.git#v4.29.1](https://github.com/cloudfoundry/java-buildpack.git#v4.29.1)                                                                  |
|       buildpacks        |用于部署应用程序的构建包列表。|                                                                 [github.com/cloudfoundry/java-buildpack.git#v4.29.1](https://github.com/cloudfoundry/java-buildpack.git#v4.29.1)                                                                  |
|         memory          |要分配的内存量。默认单元是 mebibytes，支持“m”和“g”后缀|                                                                                                                       1024m                                                                                                                       |
|          disk           |要分配的磁盘空间量。默认的单元是支持的 mebibytes、“m”和“g”后缀。|                                                                                                                       1024m                                                                                                                       |
|       healthCheck       |在已部署的应用程序上执行的健康检查的类型。值可以是 HTTP、None、Process 和 Port|                                                                                                                       PORT                                                                                                                        |
| healthCheckHttpEndpoint |HTTP 健康检查将使用的路径，|                                                                                                                      /health                                                                                                                      |
|   healthCheckTimeout    |健康检查的超时值（以秒为单位）。|                                                                                                                        120                                                                                                                        |
|        instances        |要运行的实例的数量。|                                                                                                                         1                                                                                                                         |
|enableRandomAppNamePrefix|标记，以启用在应用程序名称前加上一个随机前缀。|                                                                                                                       true                                                                                                                        |
|       apiTimeout        |用于阻止 API 调用的超时（以秒为单位）。|                                                                                                                        360                                                                                                                        |
|      statusTimeout      |状态 API 操作的超时（以毫秒为单位）|                                                                                                                       5000                                                                                                                        |
|useSpringApplicationJson |标志，指示应用程序属性是被输入`SPRING_APPLICATION_JSON`还是作为单独的环境变量。|                                                                                                                       true                                                                                                                        |
|     stagingTimeout      |为安装应用程序分配的超时时间。|                                                                                                                    15 minutes                                                                                                                     |
|     startupTimeout      |为启动应用程序分配的超时时间。|                                                                                                                     5 minutes                                                                                                                     |
|      appNamePrefix      |用作已部署应用程序名称前缀的字符串|                                                                     The Spring Boot property `spring.application.name` of the application that is using the deployer library.                                                                     |
|      deleteRoutes       |在取消部署应用程序时是否也要删除路由。|                                                                                                                       true                                                                                                                        |
|        javaOpts         |要传递给 JVM 的 Java 选项，例如-dtest=foo|                                                                                                                     \<none\>                                                                                                                      |
|    push任务Enabled     |是推送任务应用程序，还是假设应用程序在启动时已经存在。|                                                                                                                       true                                                                                                                        |
|autoDeleteMavenArtifacts |是否在部署时从本地存储库中自动删除 Maven 工件。|                                                                                                                       true                                                                                                                        |
|       env.\<key\>       |定义顶层环境变量。这对于自定义[Java 构建包配置](https://github.com/cloudfoundry/java-buildpack#configuration-and-extension)非常有用，因为 Java 构建包不识别`SPRING_APPLICATION_JSON`，这些变量必须作为顶级环境变量包含在应用程序清单中。|The deployer determines if the app has [Java CfEnv](https://github.com/pivotal-cf/java-cfenv) in its classpath. If so, it applies the required [configuration](https://github.com/pivotal-cf/java-cfenv#pushing-your-application-to-cloud-foundry).|

以下是使用 Cloud Foundry 部署属性的一些示例：

* 你可以设置用于部署每个应用程序的构建包。例如，要使用 Java 脱机 buildback，请设置以下环境变量：

```
cf set-env dataflow-server SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_BUILDPACKS java_buildpack_offline
```

* 现在不赞成设置`buildpack`，而赞成`buildpacks`，它允许你在需要时传递多个参数。有关这方面的更多信息，请访问[构建包如何工作](https://docs.cloudfoundry.org/buildpacks/understand-buildpacks.html)。

* 你可以使用[本地主机：9393](http://localhost:9393)环境变量自定义 Cloud Foundry 使用的健康检查机制，以判断应用程序是否正在运行。当前支持的选项是`http`（默认）、`port`和`none`。

你还可以分别设置指定基于 HTTP 的健康检查端点和超时的环境变量：`SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_HEALTH_CHECK_TIMEOUT`和`SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_HEALTH_CHECK_TIMEOUT`。这些默认值为`/health`（ Spring 引导默认位置）和`120`秒。

* 你还可以通过使用 DSL 指定部署属性。例如，如果要将`http`应用程序分配的内存设置为 512M，并将 MySQL 服务绑定到`120`应用程序，则可以运行以下命令：

```
dataflow:> stream create --name mysqlstream --definition "http | jdbc --tableName=names --columns=name"
dataflow:> stream deploy --name mysqlstream --properties "deployer.http.memory=512, deployer.jdbc.cloudfoundry.services=mysql"
```

|   |你可以为流应用程序和任务应用程序分别配置这些设置。要更改任务的设置，请在属性名称中将<br/>替换为`STREAM`，如下例所示：<br/><br/>```<br/>cf set-env dataflow-server SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_MEMORY 512<br/>```|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 11.3.任务

数据流服务器负责部署任务。数据流启动的任务将其状态写入数据流服务器使用的同一数据库。对于 Spring 批处理作业的任务，作业和步骤执行数据也存储在该数据库中。与 Skipper 一样，任务可以在多个平台上启动。当数据流在 Cloud Foundry 上运行时，必须定义一个任务平台。要配置以 Cloud Foundry 为目标的新平台帐户，请在你的`application.yaml`文件中的`spring.cloud.dataflow.task.platform.cloudfoundry`节下通过另一种 Spring 启动支持的机制提供一个条目。在下面的示例中，创建了两个名为`dev`和`qa`的 Cloud Foundry 平台帐户。如`memory`和`disk`的键是[Cloud Foundry Deployer 属性](#configuration-cloudfoundry-deployer)。

```
spring:
  cloud:
    dataflow:
      task:
        platform:
          cloudfoundry:
            accounts:
              dev:
                connection:
                  url: https://api.run.pivotal.io
                  org: myOrg
                  space: mySpace
                  domain: cfapps.io
                  username: [email protected]
                  password: drowssap
                  skipSslValidation: false
                deployment:
                  memory: 512m
                  disk: 2048m
                  instances: 4
                  services: rabbit,mysql
                  appNamePrefix: dev1
              qa:
                connection:
                  url: https://api.run.pivotal.io
                  org: myOrgQA
                  space: mySpaceQA
                  domain: cfapps.io
                  username: [email protected]
                  password: drowssap
                  skipSslValidation: true
                deployment:
                  memory: 756m
                  disk: 724m
                  instances: 2
                  services: rabbitQA,mysqlQA
                  appNamePrefix: qa1
```

|   |通过将一个平台定义为`platformName`，你可以跳过使用`platformName`，否则将需要使用该平台。|
|---|------------------------------------------------------------------------------------------------------------------------|

启动任务时，使用 Task Launch 选项`--platformName`传递平台帐户名的值`platformName`如果不传递`platformName`的值，将使用该值`default`。

|   |当将任务部署到多个平台时，任务的配置需要连接到与数据流服务器相同的数据库。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------|

你可以将 Cloud Foundry 上的数据流服务器配置为将任务部署到 Cloud Foundry 或 Kubernetes。有关更多信息，请参见[Kubernetes 任务平台配置](#configuration-kubernetes-tasks)一节。

在[dataflow.spring.io](http://dataflow.spring.io)上的[对任务的多平台支持](https://dataflow.spring.io/docs/recipes/multi-platform-deployment/)一节中提供了跨多个平台启动和调度任务的详细示例。

### 11.4.应用程序名称和前缀

为了帮助避免在 Cloud Foundry 中与跨空间的路由发生冲突，可以使用一种命名策略，该策略为部署的应用程序提供一个随机的前缀，并且在默认情况下启用。你可以使用`cf set-env`命令重写`cf set-env`并设置相应的属性。

例如，如果你想禁用随机化，可以使用以下命令重写它：

```
cf set-env dataflow-server SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_ENABLE_RANDOM_APP_NAME_PREFIX false
```

### 11.5.自定义路线

作为随机名称的替代方法，或者为了更好地控制已部署应用程序所使用的主机名，你可以使用自定义部署属性，如下例所示：

```
dataflow:>stream create foo --definition "http | log"

sdataflow:>stream deploy foo --properties "deployer.http.cloudfoundry.domain=mydomain.com,
                                          deployer.http.cloudfoundry.host=myhost,
                                          deployer.http.cloudfoundry.route-path=my-path"
```

前面的示例将`http`应用程序绑定到`[myhost.mydomain.com/my-path](https://myhost.mydomain.com/my-path)`URL。注意，这个示例显示了**全部**的可用定制选项。在实践中，你只能使用三个中的一个或两个。

### 11.6.Docker 应用程序

从版本 1.2 开始，通过使用用于 Cloud Foundry 的数据流，可以注册和部署基于 Docker 的应用程序作为流和任务的一部分。

如果使用 Spring boot 和基于 RabbitMQ 的 Docker 映像，则可以提供一个公共部署属性，以方便将应用程序绑定到 RabbitMQ 服务。假设你的 RabbitMQ 服务名为`rabbit`，那么你可以提供以下内容：

```
cf set-env dataflow-server SPRING_APPLICATION_JSON '{"spring.cloud.dataflow.applicationProperties.stream.spring.rabbitmq.addresses": "${vcap.services.rabbit.credentials.protocols.amqp.uris}"}'
```

对于 Spring 云任务应用程序，如果使用名为`mysql`的数据库服务实例，则可以使用类似于以下内容的方法：

```
cf set-env SPRING_DATASOURCE_URL '${vcap.services.mysql.credentials.jdbcUrl}'
cf set-env SPRING_DATASOURCE_USERNAME '${vcap.services.mysql.credentials.username}'
cf set-env SPRING_DATASOURCE_PASSWORD '${vcap.services.mysql.credentials.password}'
cf set-env SPRING_DATASOURCE_DRIVER_CLASS_NAME 'org.mariadb.jdbc.Driver'
```

对于非 Java 或非引导应用程序，Docker 应用程序必须解析`VCAP_SERVICES`变量，才能绑定到任何可用的服务。

|   |传递应用程序属性<br/><br/>当使用非引导应用程序时，你可能希望通过使用传统的<br/>环境变量来传递应用程序属性，而不是使用特殊的`SPRING_APPLICATION_JSON`变量。要做到这一点，请分别为流和任务设置以下变量<br/>:<br/><br/>`TRUST_CERTS`|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 11.7.应用程序级服务绑定

在 Cloud Foundry 中部署流时，你可以利用特定于应用程序的服务绑定，因此并不是所有服务都为 Spring 云数据流精心编制的所有应用程序进行了全局配置。

例如，如果你希望在以下流定义中仅为`mysql`应用程序提供`jdbc`服务绑定，则可以将服务绑定作为部署属性传递：

```
dataflow:>stream create --name httptojdbc --definition "http | jdbc"
dataflow:>stream deploy --name httptojdbc --properties "deployer.jdbc.cloudfoundry.services=mysqlService"
```

其中`mysqlService`是专门绑定到`jdbc`应用程序的服务的名称，而`http`应用程序不通过这种方法获得绑定。

如果有多个服务要绑定，则可以将它们作为逗号分隔的项传递（例如：`deployer.jdbc.cloudfoundry.services=mysqlService,someService`）。

### 11.8.配置服务绑定参数

CloudFoundry API 支持在绑定服务实例时提供配置参数。一些服务代理要求或推荐绑定配置。例如，使用 CF CLI 绑定[谷歌云平台服务](https://docs.pivotal.io/partners/gcp-sb/using.html)看起来像是：

```
cf bind-service my-app my-google-bigquery-example -c '{"role":"bigquery.user"}'
```

同样，[NFS 卷服务](https://docs.cloudfoundry.org/devguide/services/using-vol-services.html)支持绑定配置，例如：

```
cf bind-service my-app nfs_service_instance -c '{"uid":"1000","gid":"1000","mount":"/var/volume1","readonly":true}'
```

从版本 2.0 开始，Cloud Foundry 的数据流允许你提供绑定配置参数，可以在应用程序级别或服务器级别`cloudfoundry.services`部署属性中提供。例如，要绑定到 NFS 服务，如上所示：

```
dataflow:> stream deploy --name mystream --properties "deployer.<app>.cloudfoundry.services='nfs_service_instance uid:1000,gid:1000,mount:/var/volume1,readonly:true'"
```

该格式旨在与数据流 DSL 解析器兼容。通常，`cloudfoundry.services`部署属性接受以逗号分隔的值。由于逗号也用于分离配置参数，并避免空格问题，因此包括配置参数在内的任何项目都必须用单引号括起来。有效的价值包括：

```
rabbitmq,'nfs_service_instance uid:1000,gid:1000,mount:/var/volume1,readonly:true',mysql,'my-google-bigquery-example role:bigquery.user'
```

|   |单引号中允许空格，并且可以使用`=`代替`:`来分隔键值对。|
|---|--------------------------------------------------------------------------------------------------------|

### 11.9.用户提供的服务

除了市场服务，Cloud Foundry 还支持[用户提供的服务](https://docs.cloudfoundry.org/devguide/services/user-provided.html)。在整个参考手册中，都提到了常规服务，但也没有排除使用用户提供的服务，无论是作为消息传递中间件使用（例如，如果你想要使用外部的 Apache Kafka 安装）或某些流应用程序（例如，Oracle 数据库）使用。

现在，我们回顾一个从 UPS 提取和提供连接凭据的示例。

下面的示例显示了 Apache Kafka 的 UPS 设置示例：

```
cf create-user-provided-service kafkacups -p '{”brokers":"HOST:PORT","zkNodes":"HOST:PORT"}'
```

UPS 凭据被包装在`VCAP_SERVICES`中，并且可以直接在流定义中提供它们，如下例所示。

```
stream create fooz --definition "time | log"
stream deploy fooz --properties "app.time.spring.cloud.stream.kafka.binder.brokers=${vcap.services.kafkacups.credentials.brokers},app.time.spring.cloud.stream.kafka.binder.zkNodes=${vcap.services.kafkacups.credentials.zkNodes},app.log.spring.cloud.stream.kafka.binder.brokers=${vcap.services.kafkacups.credentials.brokers},app.log.spring.cloud.stream.kafka.binder.zkNodes=${vcap.services.kafkacups.credentials.zkNodes}"
```

### 11.10.数据库连接池

在 DataFlow2.0 中， Spring Cloud Connector 库不再用于创建数据源。现在使用的库[java-cfenv](https://github.com/pivotal-cf/java-cfenv)允许你设置[Spring Boot properties](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-connect-to-production-database)来配置连接池。

### 11.11.最大磁盘配额

默认情况下，Cloud Foundry 中的每个应用程序都从 1G 磁盘配额开始，并且可以将其调整为默认的最大 2G。通过使用 Pivotal Cloud Foundry 的 Ops Manager GUI，默认的最大值也可以覆盖到 10G。

这种配置与 Spring 云数据流相关，因为每个任务部署都由应用程序（通常是 Spring 引导 UBER-JAR 的）组成，并且这些应用程序是从远程 Maven 存储库解析的。在解析之后，应用程序工件被下载到本地 Maven 存储库以进行缓存和重用。当这种情况发生在后台时，默认磁盘配额（1G）可以迅速填充，特别是当我们实验由独特的应用程序组成的流时。为了克服这种磁盘限制，并根据你的缩放要求，你可能希望将默认的最大值从 2G 更改为 10G。让我们回顾一下更改默认的最大磁盘配额分配的步骤。

#### 11.11.1.PCF 的运营经理

从 PCF 的 Ops Manager 中，选择“Pivotal Elastic Runtime”贴片并导航到“Application Developer Control”选项卡。将“每个应用程序的最大磁盘配额”设置从 2048（2G）更改为 10240（10G）。保存磁盘配额更新，并单击“应用更改”以完成配置覆盖。

### 11.12.规模应用

一旦成功应用了磁盘配额更改，并假设你有[正在运行的应用程序](#running-on-cloudfoundry)，就可以通过 CF CLI 使用新的`disk_limit`扩展应用程序，如下例所示：

```
→ cf scale dataflow-server -k 10GB

Scaling app dataflow-server in org ORG / space SPACE as user...
OK

....
....
....
....

     state     since                    cpu      memory           disk           details
#0   running   2016-10-31 03:07:23 PM   1.8%     497.9M of 1.1G   193.9M of 10G
```

然后，你可以列出应用程序并查看新的最大磁盘空间，如下例所示：

```
→ cf apps
Getting apps in org ORG / space SPACE as user...
OK

name              requested state   instances   memory   disk   urls
dataflow-server   started           1/1         1.1G     10G    dataflow-server.apps.io
```

### 11.13.管理磁盘使用

即使在配置数据流服务器使用 10G 空间时，也有可能耗尽本地磁盘上的可用空间。为了防止这种情况，从外部来源下载的`jar`工件，即注册为`http`或`maven`资源的应用程序，在部署应用程序时会自动删除，无论部署请求是否成功。对于容器运行时稳定性比部署期间产生的 I/O 延迟更关键的生产环境，这种行为是最优的。在开发环境中，部署发生得更频繁。此外，`jar`工件（或打火机`metadata`JAR）包含描述应用程序配置属性的元数据，这些元数据用于与应用程序配置相关的各种操作，这些操作在生产前活动期间更频繁地执行（有关详细信息，请参见[应用程序元数据](https://dataflow.spring.io/docs/applications/application-metadata)）。为了提供响应更快的交互式开发人员体验，而不是在预生产环境中使用更多的磁盘，你可以将 CloudFoundry 部署程序属性`autoDeleteMavenArtifacts`设置为`false`。

如果使用默认的`port`健康检查类型部署数据流服务器，则必须显式地监视服务器上的磁盘空间，以避免耗尽空间。如果你使用`http`健康检查类型来部署服务器（请参见下一个示例），那么如果磁盘空间较低，则将重新启动数据流服务器。这是由于 Spring boot 的[磁盘空间健康指示器](https://github.com/spring-projects/spring-boot/blob/v1.5.14.RELEASE/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/DiskSpaceHealthIndicator.java)。通过使用带有`management.health.diskspace`前缀的属性，可以[configure](https://docs.spring.io/spring-boot/docs/1.5.14.RELEASE/reference/htmlsingle/#common-application-properties)磁盘空间健康指示器的设置。

对于版本 1.7，我们正在研究使用[批量服务](https://docs.cloudfoundry.org/devguide/services/using-vol-services.html)来使数据流服务器存储`.jar`工件，然后将它们推送到 Cloud Foundry。

下面的示例展示了如何将`http`健康检查类型部署到一个名为`/management/health`的端点：

```
---
  ...
  health-check-type: http
  health-check-http-endpoint: /management/health
```

### 11.14.应用解决方案

尽管我们建议对应用程序[注册一个流应用程序](#spring-cloud-dataflow-register-stream-apps)使用 Maven artifactory，但在某些情况下，以下一种替代方法可能是有意义的。

* 我们定制并维护了一个[SCDF 应用程序工具](https://github.com/spring-cloud-stream-app-starters/scdf-app-tool)，它可以在 Cloud Foundry 中作为常规 Spring 启动应用程序运行，但它将在运行时为 SCDF 托管和服务应用程序 JAR。

* 在 Spring Boot 的帮助下，我们可以在 Cloud Foundry 中服务[静态内容](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-static-content)。 Spring 一个简单的引导应用程序可以捆绑所有所需的流和任务应用程序。通过让它在 Cloud Foundry 上运行，静态应用程序就可以为 über-Jar 服务了。例如，可以通过使用`--uri=http://<Route-To-StaticApp>/http-source.jar`在 shell 中注册名称为`http-source.jar`的应用程序。

* über-Jar 可以托管在任何可以通过 HTTP 访问的外部服务器上。它们也可以从原始的 GitHub URI 中解决。从 shell 中，你可以使用`--uri=http://<Raw_GitHub_URI>/http-source.jar`注册名称为`http-source.jar`的应用程序。

* [静态构建包](https://docs.cloudfoundry.org/buildpacks/staticfile/index.html)支持 Cloud Foundry 是另一种选择。类似的 HTTP 解析也适用于此模型。

* [批量服务](https://docs.cloudfoundry.org/devguide/services/using-vol-services.html)是另一个不错的选择。所需的 über-jars 可以托管在外部文件系统中。例如，在卷服务的帮助下，你可以使用`--uri=file://<Path-To-FileSystem>/http-source.jar`以`http-source.jar`的名称注册应用程序。

### 11.15.安全

默认情况下，数据流服务器是不安全的，并在未加密的 HTTP 连接上运行。你可以通过启用 HTTPS 并要求客户机进行身份验证来保护 REST 端点（以及数据流仪表板）。有关保护 REST 端点和配置以针对 OAuth 后端（运行在 Cloud Foundry 上的 UAA 和 SSO）进行身份验证的更多详细信息，请参见核心[[configuration-local-security]]（#configuration-local-security）中的安全部分。你可以在`dataflow-server.yml`中配置安全细节，或者通过`cf set-env`命令将它们作为环境变量传递。

#### 11.15.1.认证

Spring 云数据流既可以与 Pivotal 单点登录服务（例如，在 PWS 上）集成，也可以与 Cloud Foundry 用户帐户和认证（UAA）服务器集成。

##### 关键的单点登录服务

Spring 将云数据流部署到 Cloud Foundry 时，可以将应用程序绑定到关键的单点登录服务。通过这样做， Spring 云数据流利用了[Java CFEnv](https://github.com/pivotal-cf/java-cfenv)，后者为 OAuth2.0 提供了特定于云 Foundry 的自动配置支持。

为此，将 Pivotal 单点登录服务绑定到数据流服务器应用程序，并提供以下属性：

```
SPRING_CLOUD_DATAFLOW_SECURITY_CFUSEUAA: false                                                 (1)
SECURITY_OAUTH2_CLIENT_CLIENTID: "${security.oauth2.client.clientId}"
SECURITY_OAUTH2_CLIENT_CLIENTSECRET: "${security.oauth2.client.clientSecret}"
SECURITY_OAUTH2_CLIENT_ACCESSTOKENURI: "${security.oauth2.client.accessTokenUri}"
SECURITY_OAUTH2_CLIENT_USERAUTHORIZATIONURI: "${security.oauth2.client.userAuthorizationUri}"
SECURITY_OAUTH2_RESOURCE_USERINFOURI: "${security.oauth2.resource.userInfoUri}"
```

|**1**|重要的是，将属性`spring.cloud.dataflow.security.cf-use-uaa`设置为`false`|
|-----|-----------------------------------------------------------------------------------------------|

对于非云计算代工安全场景，也同样支持授权。请参阅核心数据流[[configuration-local-security]]中的安全部分。

由于角色的配置在不同的环境中可能会有很大的差异，因此默认情况下，我们将所有 Spring 云数据流角色分配给用户。

你可以通过提供自己的[`AuthoritiesExtractor`]（https://DOCS. Spring.io/ Spring-boot/DOCS/current/api/org/springframework/boot/autofigure/security/oAututh2/resource/authoritiesextractor.html）来定制这种行为。

下面的示例显示了一种可能的方法，用于在`UserInfoTokenServices`上设置自定义`AuthoritiesExtractor`:

```
public class MyUserInfoTokenServicesPostProcessor
	implements BeanPostProcessor {

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
		if (bean instanceof UserInfoTokenServices) {
			final UserInfoTokenServices userInfoTokenServices == (UserInfoTokenServices) bean;
			userInfoTokenServices.setAuthoritiesExtractor(ctx.getBean(AuthoritiesExtractor.class));
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) {
		return bean;
	}
}
```

然后，你可以在你的配置类中声明它，如下所示：

```
@Bean
public BeanPostProcessor myUserInfoTokenServicesPostProcessor() {
	BeanPostProcessor postProcessor == new MyUserInfoTokenServicesPostProcessor();
	return postProcessor;
}
```

##### Cloud Foundry UAA

Cloud Foundry 用户帐户和身份验证（UAA）的可用性取决于 Cloud Foundry 环境。为了提供 UAA 集成，你必须提供必要的 OAuth2 配置属性（例如，通过设置`SPRING_APPLICATION_JSON`属性）。

下面的 JSON 示例展示了如何创建安全配置：

```
{
  "security.oauth2.client.client-id": "scdf",
  "security.oauth2.client.client-secret": "scdf-secret",
  "security.oauth2.client.access-token-uri": "https://login.cf.myhost.com/oauth/token",
  "security.oauth2.client.user-authorization-uri": "https://login.cf.myhost.com/oauth/authorize",
  "security.oauth2.resource.user-info-uri": "https://login.cf.myhost.com/userinfo"
}
```

默认情况下，`spring.cloud.dataflow.security.cf-use-uaa`属性设置为`true`。此属性激活一个特殊的[`AuthoritiesExtractor`]（https://DOCS. Spring.io/ Spring-boot/DOCS/current/api/org/springframework/boot/autofigure/security/oauth2/resource/authoritiesextractor.html），该函数称为`CloudFoundryDataflowAuthoritiesExtractor`。

如果不使用 CloudFoundry UAA，则应将`spring.cloud.dataflow.security.cf-use-uaa`设置为`false`。

在这种情况下，`spring.cloud.dataflow.security.cf-use-uaa`会调用[Cloud Foundry Apps API](https://apidocs.cloudfoundry.org/253/apps/retrieving_permissions_on_a_app.html)，并确保用户实际上是空间开发人员。

如果已验证的用户被验证为空间开发人员，则将分配所有角色。

### 11.16.配置参考

你必须提供几个配置部分。这些是 Spring boot`@ConfigurationProperties`，因此你可以将它们设置为环境变量，或者通过 Spring boot 支持的任何其他方式。下面的列表是环境变量格式，因为这是在 Cloud Foundry 中开始配置引导应用程序的一种简单方法。请注意，在将来，你将能够将任务部署到多个平台，但是对于 2.0.0.m1，你只能部署到一个平台，并且名称必须是`spring.cloud.dataflow.security.cf-use-uaa`。

```
# Default values appear after the equal signs.
# Example values, typical for Pivotal Web Services, are included as comments.

# URL of the CF API (used when using cf login -a for example) - for example, https://api.run.pivotal.io
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_URL=

# The name of the organization that owns the space above - for example, youruser-org
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_ORG=

# The name of the space into which modules will be deployed - for example, development
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SPACE=

# The root domain to use when mapping routes - for example, cfapps.io
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_DOMAIN=

# The user name and password of the user to use to create applications
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_USERNAME=
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_PASSWORD

# The identity provider to be used when accessing the Cloud Foundry API (optional).
# The passed string has to be a URL-Encoded JSON Object, containing the field origin with value as origin_key of an identity provider - for example, {"origin":"uaa"}
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_LOGIN_HINT=

# Whether to allow self-signed certificates during SSL validation (you should NOT do so in production)
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SKIP_SSL_VALIDATION

# A comma-separated set of service instance names to bind to every deployed task application.
# Among other things, this should include an RDBMS service that is used
# for Spring Cloud Task execution reporting, such as my_postgres
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES
spring.cloud.deployer.cloudfoundry.task.services=

# Timeout, in seconds, to use when doing blocking API calls to Cloud Foundry
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_API_TIMEOUT=

# Timeout, in milliseconds, to use when querying the Cloud Foundry API to compute app status
SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_STATUS_TIMEOUT
```

请注意，你可以通过使用`spring.cloud.deployer.cloudfoundry.services`、`spring.cloud.deployer.cloudfoundry.buildpacks`或 Spring Cloud Deployer-Standard`spring.cloud.dataflow.security.cf-use-uaa`和`spring.cloud.deployer.disk`作为单个部署请求的一部分来使用`deployer.<app-name>`快捷方式，如下例所示：

```
stream create --name ticktock --definition "time | log"
stream deploy --name ticktock --properties "deployer.time.memory=2g"
```

前面示例中的命令部署的时间源内存为 2048MB，而日志接收器使用的是缺省的 1024MB。

部署流时，还可以将`JAVA_OPTS`作为部署属性传递，如下例所示：

```
stream deploy --name ticktock --properties "deployer.time.cloudfoundry.javaOpts=-Duser.timezone=America/New_York"
```

### 11.17.调试

如果你想更好地了解部署流和任务时发生了什么，你可能需要启用以下功能：

* 反应堆“堆栈轨迹”，显示在发生错误之前涉及哪些操作人员。这个特性是有帮助的，因为部署人员依赖于 Project Reactor，而常规的堆栈跟踪可能并不总是允许在错误发生之前理解流程。请注意，这会带来性能损失，因此默认情况下会禁用它。

```
spring.cloud.dataflow.server.cloudfoundry.debugReactor == true
```

* 部署人员和 Cloud Foundry 客户端库请求和响应日志。此功能允许查看数据流服务器和 Cloud Foundry 云控制器之间的详细对话。

```
logging.level.cloudfoundry-client == DEBUG
```

### 11.18. Spring 云配置服务器

你可以使用 Spring Cloud Config Server 来集中 Spring 引导应用程序的配置属性。同样， Spring 云数据流和 Spring 云数据流所编排的应用程序都可以与配置服务器集成以使用相同的能力。

#### 11.18.1.流、任务和 Spring 云配置服务器

Spring 与云数据流服务器类似，可以同时配置流和任务应用程序，以解决来自配置服务器的集中属性。为部署的应用程序设置`spring.cloud.config.uri`属性是绑定到配置服务器的一种常见方式。有关更多信息，请参见[Spring Cloud Config Client](https://cloud.spring.io/spring-cloud-config/spring-cloud-config.html#_spring_cloud_config_client)参考指南。由于此属性很可能在数据流服务器部署的所有应用程序中使用，数据流服务器用于流应用程序的`spring.cloud.dataflow.applicationProperties.stream`属性和用于任务应用程序的`spring.cloud.dataflow.applicationProperties.task`属性可用于将配置服务器的`uri`传递给每个部署的流或任务应用程序。有关更多信息，请参见[通用应用程序属性](#spring-cloud-dataflow-global-properties)一节。

请注意，如果你使用来自[应用程序启动程序项目](https://cloud.spring.io/spring-cloud-stream-app-starters/)的应用程序，这些应用程序已经嵌入了[通用应用程序属性](#spring-cloud-dataflow-global-properties)依赖项。如果你从头开始构建应用程序，并且希望添加对 Config Server 的客户端支持，那么可以向 Config Server 客户机库添加依赖项引用。下面的片段展示了一个 Maven 示例：

```
...
<dependency>
  <groupId>io.pivotal.spring.cloud</groupId>
  <artifactId>spring-cloud-services-starter-config-client</artifactId>
  <version>CONFIG_CLIENT_VERSION</version>
</dependency>
...
```

其中`CONFIG_CLIENT_VERSION`可以是用于 Pivotal Cloud Foundry 的[Spring Cloud Config Server](https://github.com/pivotal-cf/spring-cloud-services-connector/releases)客户端的最新版本。

|   |如果使用该库的应用程序在应用程序启动时以及每当访问`/health`端点时无法连接到配置<br/>服务器，你可能会看到`WARN`日志消息，<br/>如果你知道你没有使用 Config 服务器功能，可以通过将`SPRING_CLOUD_CONFIG_ENABLED`环境变量设置为`false`来禁用客户端库。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 11.18.2.样本清单模板

以下 SCDF 和 Skipper`manifest.yml`模板包括 Skipper 和 Spring Cloud Data Flow 服务器所需的环境变量以及部署的应用程序和任务，以在 Cloud Foundry 上成功运行，并在运行时从`my-config-server`自动解析集中的属性：

```
---
applications:
- name: data-flow-server
  host: data-flow-server
  memory: 2G
  disk_quota: 2G
  instances: 1
  path: {PATH TO SERVER UBER-JAR}
  env:
    SPRING_APPLICATION_NAME: data-flow-server
    MAVEN_REMOTE_REPOSITORIES_REPO1_URL: https://repo.spring.io/libs-snapshot
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_URL: https://api.sys.huron.cf-app.com
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_ORG: sabby20
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SPACE: sabby20
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_DOMAIN: apps.huron.cf-app.com
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_USERNAME: admin
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_PASSWORD: ***
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SKIP_SSL_VALIDATION: true
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES: mysql
    SPRING_CLOUD_SKIPPER_CLIENT_SERVER_URI: https://<skipper-host-name>/api
services:
- mysql
- my-config-server

---
applications:
- name: skipper-server
  host: skipper-server
  memory: 1G
  disk_quota: 1G
  instances: 1
  timeout: 180
  buildpack: java_buildpack
  path: <PATH TO THE DOWNLOADED SKIPPER SERVER UBER-JAR>
  env:
    SPRING_APPLICATION_NAME: skipper-server
    SPRING_CLOUD_SKIPPER_SERVER_ENABLE_LOCAL_PLATFORM: false
    SPRING_CLOUD_SKIPPER_SERVER_STRATEGIES_HEALTHCHECK_TIMEOUTINMILLIS: 300000
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_URL: https://api.local.pcfdev.io
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_ORG: pcfdev-org
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SPACE: pcfdev-space
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_DOMAIN: cfapps.io
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_USERNAME: admin
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_PASSWORD: admin
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SKIP_SSL_VALIDATION: false
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_DELETE_ROUTES: false
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES: rabbit, my-config-server
services:
- mysql
  my-config-server
```

其中`my-config-server`是运行在 Cloud Foundry 上的 Spring Cloud Config 服务实例的名称。

通过将服务分别绑定到 Spring 云数据流服务器、 Spring 云任务和通过 Skipper 绑定到所有 Spring 云流应用程序，我们现在可以解析由该服务支持的集中式属性。

#### 11.18.3.自签名 SSL 证书和 Spring 云配置服务器

通常，在开发环境中，我们可能没有有效的证书来支持客户端和后端服务之间的 SSL 通信。但是，Pivotal Cloud Foundry 的配置服务器使用 HTTPS 进行所有客户端到服务的通信，因此在没有有效证书的环境中，我们需要添加自签名的 SSL 证书。

通过使用上一节中为服务器列出的相同的`WARN`模板，我们可以通过设置`TRUST_CERTS: <API_ENDPOINT>`来提供自签名的 SSL 证书。

然而，已部署的应用程序还需要`TRUST_CERTS`作为一个平坦的环境变量（而不是包装在`SPRING_APPLICATION_JSON`中），因此我们必须用另一组令牌（`SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_USE_SPRING_APPLICATION_JSON: false`）来指示服务器执行任务。通过这种设置，应用程序将其应用程序属性作为常规的环境变量接收。

下面的清单显示了更新后的`manifest.yml`所需的更改。数据流服务器和已部署的应用程序都从`my-config-server`云配置服务器（部署为 Cloud Foundry 服务）获得它们的配置。

```
---
applications:
- name: test-server
  host: test-server
  memory: 1G
  disk_quota: 1G
  instances: 1
  path: spring-cloud-dataflow-server-VERSION.jar
  env:
    SPRING_APPLICATION_NAME: test-server
    MAVEN_REMOTE_REPOSITORIES_REPO1_URL: https://repo.spring.io/libs-snapshot
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_URL: https://api.sys.huron.cf-app.com
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_ORG: sabby20
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SPACE: sabby20
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_DOMAIN: apps.huron.cf-app.com
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_USERNAME: admin
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_PASSWORD: ***
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SKIP_SSL_VALIDATION: true
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES: mysql, config-server
    SPRING_CLOUD_SKIPPER_CLIENT_SERVER_URI: https://<skipper-host-name>/api
    TRUST_CERTS: <API_ENDPOINT> #this is for the server
    SPRING_CLOUD_DATAFLOW_APPLICATION_PROPERTIES_TASK_TRUST_CERTS: <API_ENDPOINT>   #this propagates to all tasks
services:
- mysql
- my-config-server #this is for the server
```

还可以将`my-config-server`服务添加到 Skipper 的清单环境中

```
---
applications:
- name: skipper-server
  host: skipper-server
  memory: 1G
  disk_quota: 1G
  instances: 1
  timeout: 180
  buildpack: java_buildpack
  path: <PATH TO THE DOWNLOADED SKIPPER SERVER UBER-JAR>
  env:
    SPRING_APPLICATION_NAME: skipper-server
    SPRING_CLOUD_SKIPPER_SERVER_ENABLE_LOCAL_PLATFORM: false
    SPRING_CLOUD_SKIPPER_SERVER_STRATEGIES_HEALTHCHECK_TIMEOUTINMILLIS: 300000
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_URL: <URL>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_ORG: <ORG>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SPACE: <SPACE>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_DOMAIN: <DOMAIN>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_USERNAME: <USER>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_PASSWORD: <PASSWORD>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES: rabbit, my-config-server #this is so all stream applications bind to my-config-server
services:
- mysql
  my-config-server
```

### 11.19.配置调度

本节讨论如何配置 Spring 云数据流以连接到[PCF 调度器](https://www.cloudfoundry.org/the-foundry/scheduler/)作为其代理来执行任务。

|   |在遵循这些指令之前，确保在你的 Cloud Foundry 空间中有一个运行 PCF-Scheduler 服务的实例。`TRUST_CERTS`要在你的空间中创建一个 PCF-Scheduler（假设它在你的市场中），请从 CF CLI 执行以下操作：`cf create-service scheduler-for-pcf standard <name of service>`。<br/>服务的名称稍后将用于绑定正在运行的应用程序在*PCF*中。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

为了进行调度，你必须在你的环境中添加（或更新）以下环境变量：

* 通过将`spring.cloud.dataflow.features.schedules-enabled`设置为`true`，启用对 Spring 云数据流的调度。

* 通过将 PCF-Scheduler 服务名添加到`SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES`环境变量，将任务部署程序绑定到你的 PCF-Scheduler 实例。

* 通过设置`SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_SCHEDULER_SCHEDULER_URL`环境变量，建立到 PCF-Scheduler 的 URL。

|   |在创建了前面的配置之后，你必须创建任何需要调度的任务定义。|
|---|------------------------------------------------------------------------------------------------------------|

下面的示例清单显示了配置的两个环境属性（假设你有一个名为`myscheduler`的 PCF-Scheduler 服务可用）：

```
---
applications:
- name: data-flow-server
  host: data-flow-server
  memory: 2G
  disk_quota: 2G
  instances: 1
  path: {PATH TO SERVER UBER-JAR}
  env:
    SPRING_APPLICATION_NAME: data-flow-server
    SPRING_CLOUD_SKIPPER_SERVER_ENABLE_LOCAL_PLATFORM: false
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_URL: <URL>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_ORG: <ORG>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_SPACE: <SPACE>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_DOMAIN: <DOMAIN>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_USERNAME: <USER>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_CONNECTION_PASSWORD: <PASSWORD>
    SPRING_CLOUD_SKIPPER_SERVER_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_DEPLOYMENT_SERVICES: rabbit, myscheduler
    SPRING_CLOUD_DATAFLOW_FEATURES_SCHEDULES_ENABLED: true
    SPRING_CLOUD_SKIPPER_CLIENT_SERVER_URI: https://<skipper-host-name>/api
    SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]_SCHEDULER_SCHEDULER_URL: https://scheduler.local.pcfdev.io
services:
- mysql
```

其中`SPRING_CLOUD_DATAFLOW_TASK_PLATFORM_CLOUDFOUNDRY_ACCOUNTS[default]*SCHEDULER_SCHEDULER_URL*`* 具有以下格式：`scheduler.<Domain-Name>`（例如，`[scheduler.local.pcfdev.io](https://scheduler.local.pcfdev.io)`）。检查你的 \_PCF* 环境中的实际地址。

|   |在[dataflow.spring.io](http://dataflow.spring.io)上的[对任务的多平台支持](https://dataflow.spring.io/docs/recipes/multi-platform-deployment/)一节中提供了跨多个平台启动和调度任务的详细示例。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## 12. 配置-Kubernetes

本节描述了如何配置 Spring 云数据流特性，例如部署人员属性、任务以及使用哪个关系数据库。

### 12.1.功能切换

Data Flow Server 提供了一组特定的特性，可以在启动时启用或禁用这些特性。这些特性包括用于以下方面的所有生命周期操作、REST 端点（服务器和客户端实现，包括 shell 和 UI）：

* 溪流

* Tasks

* 时间表

在启动数据流服务器时，可以通过设置以下布尔环境变量来启用或禁用这些特性：

* `SPRING_CLOUD_DATAFLOW_FEATURES_STREAMS_ENABLED`

* `SPRING_CLOUD_DATAFLOW_FEATURES_TASKS_ENABLED`

* `SPRING_CLOUD_DATAFLOW_FEATURES_SCHEDULES_ENABLED`

默认情况下，所有的功能都是启用的。

`/features`REST 端点提供有关已启用和禁用的特性的信息。

### 12.2.部署人员属性

你可以使用以下配置属性`jar`来自定义如何部署流和任务。在部署数据流壳时，可以使用语法`deployer.<appName>.kubernetes.<deployerPropertyName>`。这些属性还用于在数据流服务器中配置[Kubernetes 任务平台](#configuration-kubernetes-tasks)，以及在 Skipper 中配置用于部署流的 Kubernetes 平台。

|             Deployer Property Name             |说明|                         Default Value                          |
|------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------|
|                   namespace                    |要使用的名称空间|environment variable `KUBERNETES_NAMESPACE`, otherwise `default`|
|            deployment.nodeSelector             |以`key:value`格式应用于部署的节点选择器。多个节点选择器是用逗号分隔的。|                            \<none\>                            |
|                imagePullSecret                 |秘密为一个访问一个私人注册中心的图片拉.|                            \<none\>                            |
|                imagePullPolicy                 |当拖动图像时要应用的图像拖动策略。有效的选项是`Always`、`IfNotPresent`和`Never`。|                          IfNotPresent                          |
|               livenessProbeDelay               |当应用程序容器的 Kubernetes 活性检查应开始检查其健康状态时，以秒为单位的延迟。|                               10                               |
|              livenessProbePeriod               |执行应用程序容器的 Kubernetes 活性检查的周期（以秒为单位）。|                               60                               |
|              livenessProbeTimeout              |应用程序容器的 Kubernetes 活性检查的超时（以秒为单位）。如果健康检查返回的时间超过这个值，则假定它为“不可用”。|                               2                                |
|               livenessProbePath                |应用程序容器必须响应的路径，以进行活性检查。|                            \<none\>                            |
|               livenessProbePort                |应用程序容器必须响应的端口，以进行活性检查。|                            \<none\>                            |
|              readinessProbeDelay               |当应用程序容器的准备状态检查应该开始检查模块是否完全启动并运行时，以秒为单位的延迟。|                               10                               |
|              readinessProbePeriod              |以秒为单位执行应用程序容器的准备状态检查。|                               10                               |
|             readinessProbeTimeout              |在准备就绪检查期间，应用程序容器必须响应其健康状态的超时（以秒为单位）。|                               2                                |
|               readinessProbePath               |应用程序容器必须响应的路径，以进行准备检查。|                            \<none\>                            |
|               readinessProbePort               |应用程序容器必须响应的端口，以进行准备检查。|                            \<none\>                            |
|             probeCredentialsSecret             |包含访问安全探测端点时要使用的凭据的秘密名称。|                            \<none\>                            |
|                 limits.memory                  |内存限制，分配 POD 所需的最大值，默认单元为 mebibytes，支持“M”和“G”后缀|                            \<none\>                            |
|                   limits.cpu                   |CPU 限制，分配 POD 所需的最大值|                            \<none\>                            |
|                requests.memory                 |内存请求，保证分配 POD 所需的值.|                            \<none\>                            |
|                  requests.cpu                  |的 CPU 请求，保证分配 POD 所需的值.|                            \<none\>                            |
|statefulSet.volumeClaimTemplate.storageClassName|有状态集的存储类的名称|                            \<none\>                            |
|    statefulSet.volumeClaimTemplate.storage     |存储数量。默认单元是 mebibytes，支持“m”和“g”后缀|                            \<none\>                            |
|              environmentVariables              |为任何已部署的应用程序容器设置的环境变量列表|                            \<none\>                            |
|                entryPointStyle                 |用于 Docker 映像的入口点样式。用于确定如何传入属性。可以是`exec`，`shell`，和`boot`|                             `exec`                             |
|               createLoadBalancer               |为每个应用程序创建的服务创建一个“loadbalancer”。这便于将外部 IP 分配给应用程序。|                             false                              |
|               serviceAnnotations               |为每个应用程序创建的服务设置的服务注释。格式`annotation1:value1,annotation2:value2`的字符串|                            \<none\>                            |
|                 podAnnotations                 |为每个部署创建的 POD 设置的 POD 注释。格式`annotation1:value1,annotation2:value2`的字符串|                            \<none\>                            |
|                 jobAnnotations                 |为 POD 或为作业创建的作业设置的作业注释。格式`annotation1:value1,annotation2:value2`的字符串|                            \<none\>                            |
|          minutesToWaitForLoadBalancer          |在尝试删除服务之前，等待负载均衡器可用的时间（以分钟为单位）。|                               5                                |
|           maxTerminatedErrorRestarts           |对于由于错误或过度使用资源而失败的应用程序，最大允许重启。|                               2                                |
|          maxCrashLoopBackOffRestarts           |在崩溃循环中的应用程序的最大允许重启。值为`Always`，`IfNotPresent`，`Never`|                         `IfNotPresent`                         |
|                  volumeMounts                  |以 YAML 格式表示的卷挂载。例如``[{name: 'testhostpath', mountPath: '/test/hostPath'}, {name: 'testpvc', mountPath: '/test/pvc'}, {name: 'testnfs', mountPath: '/test/nfs'}]``|                            \<none\>                            |
|                    volumes                     |Kubernetes 实例支持的卷以 YAML 格式提供。例如``[{name: testhostpath, hostPath: { path: '/test/override/hostPath' }},{name: 'testpvc', persistentVolumeClaim: { claimName: 'testClaim', readOnly: 'true' }}, {name: 'testnfs', nfs: { server: '10.0.0.1:111', path: '/test/nfs' }}]``|                            \<none\>                            |
|                  hostNetwork                   |部署的 hostnetwork 设置，请参见[Kubernetes.io/DOCS/api-reference/v1/definitions/#\_v1\_podspec](https://kubernetes.io/docs/api-reference/v1/definitions/#_v1_podspec)|                             false                              |
|                createDeployment                |用“复制集”而不是“复制控制器”创建“部署”。|                              true                              |
|                   createJob                    |在启动任务时，创建一个“工作”，而不只是一个“pod”。|                             false                              |
|                containerCommand                |使用提供的命令和参数重写缺省的 Entry Point 命令。|                            \<none\>                            |
|                 containerPorts                 |添加要在容器上公开的其他端口。|                            \<none\>                            |
|                 createNodePort                 |当`NodePort`时要使用的显式端口是`Service`类型。|                            \<none\>                            |
|          deploymentServiceAccountName          |应用程序部署中使用的服务帐户名称。注意：用于应用程序部署的服务帐户名称来自数据流服务器部署。|                            \<none\>                            |
|                deploymentLabels                |以`key:value`格式添加到部署中的附加标签。多个标签是用逗号分隔的。|                            \<none\>                            |
|                bootMajorVersion                |Spring 主要使用的引导版本。当前仅用于自动配置 Spring 启动版本特定的探测路径。有效的选项是`1`或`2`。|                               2                                |
|                tolerations.key                 |要用到的关键是容忍度。|                            \<none\>                            |
|               tolerations.effect               |宽容的效果。有关有效选项，请参见[Kubernetes.io/DOCS/概念/配置/污点容忍](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration)。|                            \<none\>                            |
|              tolerations.operator              |宽容的操作者。有关有效选项，请参见[Kubernetes.io/DOCS/概念/配置/污点容忍/](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)。|                            \<none\>                            |
|         tolerations.tolerationSeconds          |定义 POD 在添加污点后将与节点绑定多长时间的秒数。|                            \<none\>                            |
|               tolerations.value                |适用的容忍值，与`operator`结合使用，以选择适当的`effect`。|                            \<none\>                            |
|                   secretRefs                   |将整个数据内容加载到各个环境变量中的秘密的名称。多个秘密可以用逗号分隔。|                            \<none\>                            |
|            secretKeyRefs.envVarName            |保存秘密数据的环境变量名|                            \<none\>                            |
|            secretKeyRefs.secretName            |要访问的秘密名称|                            \<none\>                            |
|             secretKeyRefs.dataKey              |从其中获取秘密数据的键名|                            \<none\>                            |
|                 configMapRefs                  |用于将整个数据内容加载到各个环境变量中的配置图的名称。多个配置映射以逗号分隔。|                            \<none\>                            |
|          configMapKeyRefs.envVarName           |保存配置图数据的环境变量名|                            \<none\>                            |
|         configMapKeyRefs.configMapName         |要访问的配置图名称|                            \<none\>                            |
|            configMapKeyRefs.dataKey            |从其中获取配置图数据的键名|                            \<none\>                            |
|             maximumConcurrentTasks             |此平台实例允许的最大并发任务。|                               20                               |
|          podSecurityContext.runAsUser          |用于运行 POD 容器进程的数字用户 ID|                            \<none\>                            |
|           podSecurityContext.fsGroup           |用于运行 POD 容器进程的数字组 ID|                            \<none\>                            |
|             affinity.nodeAffinity              |以 YAML 格式表示的节点亲和力。例如``{ requiredDuringSchedulingIgnoredDuringExecution: { nodeSelectorTerms: [ { matchExpressions: [ { key: 'kubernetes.io/e2e-az-name', operator: 'In', values: [ 'e2e-az1', 'e2e-az2']}]}]}, preferredDuringSchedulingIgnoredDuringExecution: [ { weight: 1, preference: { matchExpressions: [ { key: 'another-node-label-key', operator: 'In', values: [ 'another-node-label-value' ]}]}}]}``|                            \<none\>                            |
|              affinity.podAffinity              |以 YAML 格式表示的 POD 亲和力。例如``{ requiredDuringSchedulingIgnoredDuringExecution: { labelSelector: [ { matchExpressions: [ { key: 'app', operator: 'In', values: [ 'store']}]}], topologyKey: 'kubernetes.io/hostnam'}, preferredDuringSchedulingIgnoredDuringExecution: [ { weight: 1, podAffinityTerm: { labelSelector: { matchExpressions: [ { key: 'security', operator: 'In', values: [ 'S2' ]}]}, topologyKey: 'failure-domain.beta.kubernetes.io/zone'}}]}``|                            \<none\>                            |
|            affinity.podAntiAffinity            |以 YAML 格式表示的 POD 反亲和力。例如``{ requiredDuringSchedulingIgnoredDuringExecution: { labelSelector: { matchExpressions: [ { key: 'app', operator: 'In', values: [ 'store']}]}], topologyKey: 'kubernetes.io/hostname'}, preferredDuringSchedulingIgnoredDuringExecution: [ { weight: 1, podAffinityTerm: { labelSelector: { matchExpressions: [ { key: 'security', operator: 'In', values: [ 'S2' ]}]}, topologyKey: 'failure-domain.beta.kubernetes.io/zone'}}]}``|                            \<none\>                            |
|       statefulSetInitContainerImageName        |用于 statefulset init 容器的自定义映像名|                            \<none\>                            |
|                 initContainer                  |以 YAML 格式表示的 init 容器，应用于 POD。例如``{containerName: 'test', imageName: 'busybox:latest', commands: ['sh', '-c', 'echo hello']}``|                            \<none\>                            |
|              additionalContainers              |以 YAML 格式表示的附加容器将应用于 POD。例如``[{name: 'c1', image: 'busybox:latest', command: ['sh', '-c', 'echo hello1'], volumeMounts: [{name: 'test-volume', mountPath: '/tmp', readOnly: true}]}, {name: 'c2', image: 'busybox:1.26.1', command: ['sh', '-c', 'echo hello2']}]``|                            \<none\>                            |

### 12.3.任务

数据流服务器负责部署任务。数据流启动的任务将其状态写入数据流服务器使用的同一数据库。对于 Spring 批处理作业的任务，作业和步骤执行数据也存储在该数据库中。与 Skipper 一样，任务可以在多个平台上启动。当数据流在 Kubernetes 上运行时，必须定义一个任务平台。要配置以 Kubernetes 为目标的新平台帐户，请在你的`application.yaml`文件中的`spring.cloud.dataflow.task.platform.kubernetes`节下通过另一种 Spring 启动支持的机制提供一个条目。在下面的示例中，创建了两个名为`dev`和`qa`的 Kubernetes 平台帐户。如`memory`和`disk`的键是[Cloud Foundry Deployer 属性](#configuration-cloudfoundry-deployer)。

```
spring:
  cloud:
    dataflow:
      task:
        platform:
          kubernetes:
            accounts:
              dev:
                namespace: devNamespace
                imagePullPolicy: Always
                entryPointStyle: exec
                limits:
                  cpu: 4
              qa:
                namespace: qaNamespace
                imagePullPolicy: IfNotPresent
                entryPointStyle: boot
                limits:
                  memory: 2048m
```

|   |通过将一个平台定义为`default`，你可以跳过使用`default`，否则将需要使用该平台。|
|---|------------------------------------------------------------------------------------------------------------------------|

启动任务时，使用 Task Launch 选项`--platformName`传递平台帐户名的值`platformName`如果不传递`platformName`的值，则将使用该值`default`。

|   |当将任务部署到多个平台时，任务的配置需要连接到与数据流服务器相同的数据库。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------|

你可以将 Kubernetes 上的数据流服务器配置为将任务部署到 Cloud Foundry 和 Kubernetes。有关更多信息，请参见[Cloud Foundry 任务平台配置](#configuration-cloudfoundry-tasks)一节。

关于跨多个平台启动和调度任务的详细示例，可在[对任务的多平台支持](https://dataflow.spring.io/docs/recipes/multi-platform-deployment/)上的[dataflow.spring.io](http://dataflow.spring.io)一节中获得。

### 12.4.一般配置

Spring 用于 Kubernetes 的云数据流服务器使用[`spring-cloud-kubernetes`]（https://github.com/fabric8io/ Spring-cloud-kubernetes）模块来处理安装在`/etc/secrets`下的秘密。配置映射必须在由 Spring 引导处理的`/config`目录中挂载为`application.yaml`。为了避免访问 Kubernetes API 服务器，将`SPRING_CLOUD_KUBERNETES_CONFIG_ENABLE_API`和`/etc/secrets`设置为`false`。

#### 12.4.1.使用配置图和秘密

可以通过使用 Kubernetes[ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configmap/)和[secrets](https://kubernetes.io/docs/concepts/configuration/secret/)将配置属性传递给数据流服务器。

下面的示例展示了一种可能的配置，该配置启用了 MySQL 并设置了内存限制：

```
apiVersion: v1
kind: ConfigMap
metadata:
  name: scdf-server
  labels:
    app: scdf-server
data:
  application.yaml: |-
    spring:
      cloud:
        dataflow:
          task:
            platform:
              kubernetes:
                accounts:
                  default:
                    limits:
                      memory: 1024Mi
      datasource:
        url: jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT}/mysql
        username: root
        password: ${mysql-root-password}
        driverClassName: org.mariadb.jdbc.Driver
        testOnBorrow: true
        validationQuery: "SELECT 1"
```

前面的示例假定 MySQL 是以`mysql`作为服务名称部署的。Kubernetes 将这些服务的主机值和端口值作为环境变量发布，我们可以在配置部署的应用程序时使用这些环境变量。

我们更喜欢在机密文件中提供 MySQL 连接密码，如下例所示：

```
apiVersion: v1
kind: Secret
metadata:
  name: mysql
  labels:
    app: mysql
data:
  mysql-root-password: eW91cnBhc3N3b3Jk
```

密码是以 64 为基础编码的值。

### 12.5.数据库配置

Spring 云数据流为 H2、HSQLDB、MySQL、Oracle、PostgreSQL、DB2 和 SQL Server 提供了模式。如果 Classpath 中有正确的数据库驱动程序和适当的凭据，则在服务器启动时会自动创建适当的模式。

MySQL 的 JDBC 驱动程序（通过 MariaDB 驱动程序）、HSQLDB、PostgreSQL 和嵌入式 H2 都是开箱即用的。如果使用任何其他数据库，则需要在服务器的 Classpath 上放置相应的 JDBC 驱动程序 JAR。

例如，如果除了在机密文件中使用密码外，还使用 MySQL，则可以在配置图中提供以下属性：

```
data:
  application.yaml: |-
    spring:
      datasource:
        url: jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT}/mysql
        username: root
        password: ${mysql-root-password}
        driverClassName: org.mariadb.jdbc.Driver
        url: jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT}/test
        driverClassName: org.mariadb.jdbc.Driver
```

对于 PostgreSQL，你可以使用以下配置：

```
data:
  application.yaml: |-
    spring:
      datasource:
        url: jdbc:postgresql://${PGSQL_SERVICE_HOST}:${PGSQL_SERVICE_PORT}/database
        username: root
        password: ${postgres-password}
        driverClassName: org.postgresql.Driver
```

对于 HSQLDB，你可以使用以下配置：

```
data:
  application.yaml: |-
    spring:
      datasource:
        url: jdbc:hsqldb:hsql://${HSQLDB_SERVICE_HOST}:${HSQLDB_SERVICE_PORT}/database
        username: sa
        driverClassName: org.hsqldb.jdbc.JDBCDriver
```

部署中的以下 YAML 片段是挂载配置图的示例作为`application.yaml``/config`下的`/config`，其中 Spring boot 将处理它加上一个安装在`/etc/secrets`下的秘密，由于环境变量`SPRING_CLOUD_KUBERNETES_SECRETS_PATHS`被设置为`/etc/secrets`，它将被 Spring-cloud-kubernetes 库选中。

```
...
      containers:
      - name: scdf-server
        image: springcloud/spring-cloud-dataflow-server:2.5.0.BUILD-SNAPSHOT
        imagePullPolicy: Always
        volumeMounts:
          - name: config
            mountPath: /config
            readOnly: true
          - name: database
            mountPath: /etc/secrets/database
            readOnly: true
        ports:
...
      volumes:
        - name: config
          configMap:
            name: scdf-server
            items:
            - key: application.yaml
              path: application.yaml
        - name: database
          secret:
            secretName: mysql
```

你可以在[spring-cloud-task](https://github.com/spring-cloud/spring-cloud-task/tree/master/spring-cloud-task-core/src/main/resources/org/springframework/cloud/task/migration)repo 中找到特定数据库类型的迁移脚本。

### 12.6.监测和管理

我们建议使用`kubectl`命令对流和任务进行故障排除。

你可以使用以下命令列出所使用的所有工件和资源：

```
kubectl get all,cm,secrets,pvc
```

通过使用标签来选择资源，你可以列出特定应用程序或服务使用的所有资源。下面的命令列出了`mysql`服务使用的所有资源：

```
kubectl get all -l app=mysql
```

你可以通过发出以下命令来获取特定 pod 的日志：

```
kubectl logs pod <pod-name>
```

如果持续重启 POD，可以添加`-p`作为查看上一个日志的选项，如下所示：

```
kubectl logs -p <pod-name>
```

你还可以通过添加`-f`选项跟踪或跟踪日志，如下所示：

```
kubectl logs -f <pod-name>
```

一个有用的命令是使用`describe`命令，以帮助解决问题（例如，一个容器在启动时出现致命错误），如下例所示：

```
kubectl describe pod ticktock-log-0-qnk72
```

#### 12.6.1.检查服务器日志

你可以使用以下命令访问服务器日志：

```
kubectl get pod -l app=scdf=server
kubectl logs <scdf-server-pod-name>
```

#### 12.6.2.溪流

部署流应用程序时，流名后面跟着应用程序的名称。对于处理器和接收器，还附加了一个实例索引。

要查看 Spring 云数据流服务器部署的所有 pod，你可以指定`role=spring-app`标签，如下所示：

```
kubectl get pod -l role=spring-app
```

要查看特定应用程序部署的详细信息，你可以使用以下命令：

```
kubectl describe pod <app-pod-name>
```

要查看应用程序日志，你可以使用以下命令：

```
kubectl logs <app-pod-name>
```

如果你想跟踪日志，可以使用以下命令：

```
kubectl logs -f <app-pod-name>
```

#### 12.6.3.任务

任务以裸荚形式启动，无需复制控制器。任务完成后，这些吊舱仍将保留，这为你提供了查看日志的机会。

要查看特定任务的所有吊舱，请使用以下命令：

```
kubectl get pod -l task-name=<task-name>
```

要查看任务日志，请使用以下命令：

```
kubectl logs <task-pod-name>
```

你有两个选项来删除已完成的 pod。一旦不再需要它们，你可以手动删除它们，或者可以使用数据流 shell`task execution cleanup`命令删除已完成的 pod 以执行任务。

要手动删除 Task Pod，请使用以下命令：

```
kubectl delete pod <task-pod-name>
```

要使用`task execution cleanup`命令，你必须首先确定用于执行任务的`ID`。要做到这一点，请使用`task execution list`命令，如下例（带输出）所示：

```
dataflow:>task execution list
╔═════════╤══╤════════════════════════════╤════════════════════════════╤═════════╗
║Task Name│ID│         Start Time         │          End Time          │Exit Code║
╠═════════╪══╪════════════════════════════╪════════════════════════════╪═════════╣
║task1    │1 │Fri May 05 18:12:05 EDT 2017│Fri May 05 18:12:05 EDT 2017│0        ║
╚═════════╧══╧════════════════════════════╧════════════════════════════╧═════════╝
```

有了 ID 之后，就可以发出命令来清理执行工件（已完成的 POD），如下例所示：

```
dataflow:>task execution cleanup --id 1
Request to clean up resources for task execution 1 has been submitted
```

##### 任务的数据库凭据

Spring 默认情况下，云数据流在任务启动时将数据库凭据作为属性传递给 POD。如果使用`exec`或`shell`入口点样式，如果用户在任务的 POD 上执行`kubectl describe`，则可以查看 DB 凭据。要配置 Spring 云数据流以使用 Kubernetes Secrets:将`spring.cloud.dataflow.task.use.kubernetes.secrets.for.db.credentials`属性设置为`true`。如果使用 Spring 云数据流提供的 YAML 文件更新 `SRC/Kubernetes/服务器/服务器-deployment.yaml’，以添加以下环境变量：

```
- name: SPRING_CLOUD_DATAFLOW_TASK_USE_KUBERNETES_SECRETS_FOR_DB_CREDENTIALS
  value: 'true'
```

如果从以前的 SCDF 版本升级，请务必验证`spring.datasource.username`和`myscheduler`环境变量是否存在于服务器-config.yaml 中的`secretKeyRefs`中。如果没有，请按下面的示例添加：

```
...
  task:
    platform:
      kubernetes:
        accounts:
          default:
            secretKeyRefs:
              - envVarName: "spring.datasource.password"
                secretName: mysql
                dataKey: mysql-root-password
              - envVarName: "spring.datasource.username"
                  secretName: mysql
                  dataKey: mysql-root-username
...
```

还要验证相关的秘密（数据密钥）在秘密中也是可用的。SCDF 在这里为 MySQL 提供了这样的示例：`src/kubernetes/mysql/mysql-svc.yaml`。

|   |默认情况下，通过属性传递 DB 凭据是为了保持向后兼容性。这一功能将会在未来的发布中被移除。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------|

### 12.7.调度

本节介绍如何配置计划任务的定制。 Spring 云数据流 Kubernetes 服务器中的任务调度在默认情况下是启用的。属性用于影响计划任务的设置，并且可以在全局或每个计划的基础上进行配置。

|   |除非注意，否则按计划设置的属性总是优先于作为服务器配置设置的属性。这种安排允许为特定的计划覆盖全局服务器级别的属性。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

参见[`KubernetesSchedulerProperties`]（https://github.com/ Spring-cloud/ Spring-cloud-scheduler-kubernetes/blob/master/SRC/main/java/org/springframework/cloud/scheduler/spi/kubernetes/kuberneteslerproperties.java）以获得更多支持的选项。

#### 12.7.1.入口点样式

入口点样式会影响如何将应用程序属性传递给要部署的任务容器。目前，支持三种样式：

* `exec`:（默认）将所有应用程序属性作为命令行参数传递。

* `shell`:将所有应用程序属性作为环境变量传递。

* `boot`:创建一个名为`SPRING_APPLICATION_JSON`的环境变量，该变量包含所有应用程序属性的 JSON 表示。

你可以按以下方式配置入口点样式：

```
deployer.kubernetes.entryPointStyle=<Entry Point Style>
```

用所需的入口点样式替换`<Entry Point Style>`。

还可以在部署 YAML 的容器`env`部分中在服务器级别配置入口点样式，如下例所示：

```
env:
- name: SPRING_CLOUD_SCHEDULER_KUBERNETES_ENTRY_POINT_STYLE
  value: entryPointStyle
```

用所需的入口点样式替换`entryPointStyle`。

你应该选择`exec`或`shell`的入口点样式，以对应于在容器的`ENTRYPOINT`中定义`Dockerfile`语法的方式。有关`exec`vs`shell`的更多信息和用例，请参见 Docker 文档的[ENTRYPOINT](https://docs.docker.com/engine/reference/builder/#entrypoint)部分。

使用`boot`入口点样式对应于使用`exec`样式`ENTRYPOINT`。来自部署请求的命令行参数将传递给容器，并添加映射到`SPRING_APPLICATION_JSON`环境变量中的应用程序属性，而不是命令行参数。

#### 12.7.2.环境变量

要影响给定应用程序的环境设置，可以利用`spring.cloud.deployer.kubernetes.environmentVariables`属性。例如，生产设置中的一个常见要求是影响 JVM 内存参数。你可以通过使用`JAVA_TOOL_OPTIONS`环境变量来实现这一点，如下例所示：

```
deployer.kubernetes.environmentVariables=JAVA_TOOL_OPTIONS=-Xmx1024m
```

|   |当部署流应用程序或启动某些属性可能包含敏感信息的任务应用程序时，请使用`shell`或`boot`作为`entryPointStyle`。这是因为`exec`（默认）将所有属性转换为命令行参数，因此在某些环境中可能不安全。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

此外，你还可以在部署 YAML 的容器`env`部分中在服务器级别上配置环境变量，如下例所示：

|   |当在服务器配置中指定环境变量时，并在每个计划的基础上，环境变量将被合并。这允许在服务器配置中设置公共环境变量，并在特定的计划级别上设置更具体的环境变量。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

```
env:
- name: SPRING_CLOUD_SCHEDULER_KUBERNETES_ENVIRONMENT_VARIABLES
  value: myVar=myVal
```

用所需的环境变量替换`myVar=myVal`。

#### 12.7.3.图像拉取策略

图像拉取策略定义了何时应该将 Docker 图像拉到本地注册中心。目前，有三项政策得到支持：

* `IfNotPresent`:（默认）如果图像已经存在，请不要拉它。

* `Always`:无论图像是否已经存在，都要拉它。

* `Never`:永远不要拉图像。只使用已经存在的图像。

下面的示例展示了如何单独配置容器：

```
deployer.kubernetes.imagePullPolicy=Always
```

用所需的图像拉出策略替换`Always`。

你可以在部署 YAML 的容器`env`部分中，在服务器级别上配置图像拉取策略，如下例所示：

```
env:
- name: SPRING_CLOUD_SCHEDULER_KUBERNETES_IMAGE_PULL_POLICY
  value: Always
```

用所需的图像拉出策略替换`Always`。

#### 12.7.4.专用 Docker 注册表

私密且需要身份验证的 Docker 映像可以通过配置秘密来提取。首先，你必须在集群中创建一个秘密。按照[从私有注册表中提取图像](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/)指南创建秘密。

创建了秘密之后，使用`imagePullSecret`属性设置要使用的秘密，如下例所示：

```
deployer.kubernetes.imagePullSecret=mysecret
```

用前面创建的秘密的名称替换`mysecret`。

你还可以在部署 YAML 的容器`env`部分中，在服务器级别上配置图像提取秘密，如下例所示：

```
env:
- name: SPRING_CLOUD_SCHEDULER_KUBERNETES_IMAGE_PULL_SECRET
  value: mysecret
```

用前面创建的秘密的名称替换`mysecret`。

#### 12.7.5.名称空间

默认情况下，用于调度任务的命名空间是`default`。可以在部署 YAML 的容器`env`部分的服务器级配置中设置该值，如下例所示：

```
env:
- name: SPRING_CLOUD_SCHEDULER_KUBERNETES_NAMESPACE
  value: mynamespace
```

#### 12.7.6.服务帐户

你可以通过属性为计划的任务配置自定义服务帐户。可以使用现有的服务帐户，也可以创建新的服务帐户。创建服务帐户的一种方法是使用`kubectl`，如下例所示：

```
$ kubectl create serviceaccount myserviceaccountname
serviceaccount "myserviceaccountname" created
```

然后，你可以将服务帐户配置为按计划使用，如下所示：

```
deployer.kubernetes.taskServiceAccountName=myserviceaccountname
```

将`myserviceaccountname`替换为你的服务帐户名称。

还可以在部署 YAML 的容器`env`部分中在服务器级别配置服务帐户名称，如下例所示：

```
env:
- name: SPRING_CLOUD_SCHEDULER_KUBERNETES_TASK_SERVICE_ACCOUNT_NAME
  value: myserviceaccountname
```

将`myserviceaccountname`替换为要应用于所有部署的服务帐户名称。

有关调度任务的详细信息，请参见[调度任务](#spring-cloud-dataflow-schedule-launch-tasks)。

### 12.8.调试支持

Spring 通过[Java 调试连接协议（JDWP）](https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/jdwp-spec.html)支持调试 Kubernetes 服务器的云数据流和包含的组件（例如[Spring Cloud Kubernetes Deployer](https://github.com/spring-cloud/spring-cloud-deployer-kubernetes)）。本节概述了一种手动启用调试的方法，以及另一种使用 Spring Cloud Data Flow Server Kubernetes 提供的配置文件来“修补”正在运行的部署的方法。

|   |JDWP 本身不使用任何身份验证。本节假定调试是在本地开发环境（如 MiniKube）上完成的，因此没有提供关于确保调试端口安全的指导。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 12.8.1.手动启用调试

要手动启用 JDWP，首先编辑`src/kubernetes/server/server-deployment.yaml`，并在`spec.template.spec.containers.ports`下添加一个额外的`containerPort`条目，其值为`5005`。另外，在`JAVA_TOOL_OPTIONS`下添加[`JAVA_TOOL_OPTIONS`]（https://DOCS.oracle.com/javase/8/DOCS/platform/jvmti/jvmti.html#tooloptions）环境变量，如以下示例所示：

```
spec:
  ...
  template:
    ...
    spec:
      containers:
      - name: scdf-server
        ...
        ports:
        ...
		- containerPort: 5005
        env:
        - name: JAVA_TOOL_OPTIONS
          value: '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005'
```

|   |前面的示例使用端口 5005，但它可以是与另一个端口不冲突的任何数字。对于添加的`containerPort`值和`address`标志的参数`JAVA_TOOL_OPTIONS``-agentlib`，所选择的端口号也必须相同，如前面的示例所示。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

现在可以启动 Spring 云数据流 Kubernetes 服务器。一旦服务器启动，你就可以验证`scdf-server`部署上的配置更改，如下例（带输出）所示：

```
kubectl describe deployment/scdf-server
...
...
Pod Template:
  ...
  Containers:
   scdf-server:
    ...
    Ports:       80/TCP, 5005/TCP
    ...
    Environment:
      JAVA_TOOL_OPTIONS:  -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
	  ...
```

在启动服务器并启用 JDWP 之后，你需要配置对端口的访问。在这个示例中，我们使用`port-forward`]（https://kubernetes.io/DOCS/tasks/access-application-cluster/port-forward-access-application-cluster/）子命令`kubectl`。下面的示例（带输出）展示了如何使用`port-forward`将本地端口公开到调试目标：

```
$ kubectl get pod -l app=scdf-server
NAME                           READY     STATUS    RESTARTS   AGE
scdf-server-5b7cfd86f7-d8mj4   1/1       Running   0          10m
$ kubectl port-forward scdf-server-5b7cfd86f7-d8mj4 5005:5005
Forwarding from 127.0.0.1:5005 -> 5005
Forwarding from [::1]:5005 -> 5005
```

现在可以通过将调试器指向`127.0.0.1`作为主机，将`5005`作为端口来附加调试器。`port-forward`子命令运行到停止为止（例如，按`CTRL+c`）。

你可以通过将更改恢复到`src/kubernetes/server/server-deployment.yaml`来删除调试支持。在下一次部署 Spring 云数据流 Kubernetes 服务器时，将拾取恢复的更改。当每次部署服务器时默认应该启用调试时，手动将调试支持添加到配置中是有用的。

#### 12.8.2.启用带修补程序的调试

与手动更改`server-deployment.yaml`不同，Kubernetes 对象可以在适当的位置进行“修补”。为了方便起见，包含了提供与手动方法相同配置的修补程序文件。要通过修补程序启用调试，请使用以下命令：

```
kubectl patch deployment scdf-server -p "$(cat src/kubernetes/server/server-deployment-debug.yaml)"
```

运行前面的命令会自动添加`containerPort`属性和`JAVA_TOOL_OPTIONS`环境变量。下面的示例（带输出）展示了如何验证对`scdf-server`部署的更改：

```
$ kubectl describe deployment/scdf-server
...
...
Pod Template:
  ...
  Containers:
   scdf-server:
    ...
    Ports:       5005/TCP, 80/TCP
    ...
    Environment:
      JAVA_TOOL_OPTIONS:  -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
	  ...
```

要启用对调试端口的访问，而不是使用`kubectl`的`port-forward`子命令，你可以修补`scdf-server`Kubernetes 服务对象。你必须首先确保`scdf-server`Kubernetes 服务对象具有正确的配置。下面的示例（带输出）展示了如何这样做：

```
kubectl describe service/scdf-server
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30784/TCP
```

如果输出包含`<unset>`，则必须对服务进行修补以添加此端口的名称。下面的示例展示了如何做到这一点：

```
$ kubectl patch service scdf-server -p "$(cat src/kubernetes/server/server-svc.yaml)"
```

|   |只有在添加调试功能之前创建了目标群集时，才应该缺少端口号。由于向`scdf-server`Kubernetes 服务对象添加了多个端口，因此每个端口都需要有自己的名称。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

现在你可以添加调试端口了，如下例所示：

```
kubectl patch service scdf-server -p "$(cat src/kubernetes/server/server-svc-debug.yaml)"
```

下面的示例（带输出）展示了如何验证映射：

```
$ kubectl describe service scdf-server
Name:                     scdf-server
...
...
Port:                     scdf-server-jdwp  5005/TCP
TargetPort:               5005/TCP
NodePort:                 scdf-server-jdwp  31339/TCP
...
...
Port:                     scdf-server  80/TCP
TargetPort:               80/TCP
NodePort:                 scdf-server  30883/TCP
...
...
```

输出结果表明，5005 集装箱港口已被映射到 31339 的节点。下面的示例（带输出）展示了如何获得 MiniKube 节点的 IP 地址：

```
$ minikube ip
192.168.99.100
```

有了这些信息，你可以使用 192.168.99.100 的主机和 31339 的端口来创建调试连接。

下面的示例展示了如何禁用 JDWP:

```
$ kubectl rollout undo deployment/scdf-server
$ kubectl patch service scdf-server --type json -p='[{"op": "remove", "path": "/spec/ports/0"}]'
```

Kubernetes 部署对象在进行修补之前会回滚到其状态。然后用`remove`操作修补 Kubernetes 服务对象，以从`containerPorts`列表中删除端口 5005.

|   |`kubectl rollout undo`强制 pod 重新启动。修补 Kubernetes 服务对象不会重新创建服务，并且到`scdf-server`部署的端口映射保持不变。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

有关部署回滚（包括管理历史记录）和[使用 Kubectl 补丁更新 API 对象](https://kubernetes.io/docs/tasks/run-application/update-api-object-kubectl-patch/)的更多信息，请参见[撤回部署](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#rolling-back-a-deployment)。

# Shell

本节介绍了启动 shell 的选项，以及与 shell 如何处理空格、引号和 SPEL 表达式的解释有关的更高级功能。对于最常用的 shell 命令，[Stream DSL](#spring-cloud-dataflow-stream-intro)和[组合任务 DSL](#spring-cloud-dataflow-composed-tasks)的介绍性章节是很好的起点。

## 13. 外壳选项

shell 是在[Spring Shell](https://projects.spring.io/spring-shell/)项目的基础上构建的。有些命令行选项来自 Spring shell，有些则是特定于数据流的。shell 接受以下命令行选项：

```
unix:>java -jar spring-cloud-dataflow-shell-2.9.2.jar --help
Data Flow Options:
  --dataflow.uri=                              Address of the Data Flow Server [default: http://localhost:9393].
  --dataflow.username=                        Username of the Data Flow Server [no default].
  --dataflow.password=                    Password of the Data Flow Server [no default].
  --dataflow.credentials-provider-command= Executes an external command which must return an
                                                    OAuth Bearer Token (Access Token prefixed with 'Bearer '),
                                                    e.g. 'Bearer 12345'), [no default].
  --dataflow.skip-ssl-validation=       Accept any SSL certificate (even self-signed) [default: no].
  --dataflow.proxy.uri=                  Address of an optional proxy server to use [no default].
  --dataflow.proxy.username=        Username of the proxy server (if required by proxy server) [no default].
  --dataflow.proxy.password=        Password of the proxy server (if required by proxy server) [no default].
  --spring.shell.historySize=                 Default size of the shell log file [default: 3000].
  --spring.shell.commandFile=                 Data Flow Shell executes commands read from the file(s) and then exits.
  --help                                            This message.
```

你可以使用`spring.shell.commandFile`选项指向一个现有的文件，该文件包含用于部署一个或多个相关流和任务的所有 shell 命令。还支持运行多个文件。它们应该以逗号分隔的字符串传递：

`--spring.shell.commandFile=file1.txt,file2.txt`

当创建一些脚本以帮助自动化部署时，此选项非常有用。

此外，下面的 shell 命令有助于将复杂的脚本模块化为多个独立的文件：

`dataflow:>script --file <YOUR_AWESOME_SCRIPT>`

## 14. 列出可用的命令

在命令提示符下输入`help`，将显示所有可用命令的列表。大多数命令用于数据流功能，但也有一些是通用的。下面的清单显示了`help`命令的输出：

```
! - Allows execution of operating system (OS) commands
clear - Clears the console
cls - Clears the console
date - Displays the local date and time
exit - Exits the shell
http get - Make GET request to http endpoint
http post - POST data to http endpoint
quit - Exits the shell
system properties - Shows the shells properties {JB - restore the apostrophe}
version - Displays shell version
```

将命令名添加到`help`将显示有关如何调用该命令的其他信息：

```
dataflow:>help stream create
Keyword:                   stream create
Description:               Create a new stream definition
 Keyword:                  ** default **
 Keyword:                  name
   Help:                   the name to give to the stream
   Mandatory:              true
   Default if specified:   '__NULL__'
   Default if unspecified: '__NULL__'

 Keyword:                  definition
   Help:                   a stream definition, using the DSL (e.g. "http --port=9000 | hdfs")
   Mandatory:              true
   Default if specified:   '__NULL__'
   Default if unspecified: '__NULL__'

 Keyword:                  deploy
   Help:                   whether to deploy the stream immediately
   Mandatory:              false
   Default if specified:   'true'
   Default if unspecified: 'false'
```

## 15. 制表符补全

你可以在 shell 中完成 shell 命令选项，方法是在前导`--`之后按`TAB`键。例如，在`stream create --`之后按`TAB`会产生以下一对建议：

```
dataflow:>stream create --
stream create --definition    stream create --name
```

如果键入`--de`，然后按 Tab，`--definition`展开。

制表符补全也可以在流或用于应用程序或任务属性的组合任务 DSL 表达式中使用。你还可以使用`TAB`在流 DSL 表达式中获得你可以使用的可用源、处理器或接收器的提示。

## 16. 空白和引用规则

只有当参数值包含空格或`|`字符时，才需要引用参数值。下面的示例将一个 SPEL 表达式（应用于它遇到的任何数据）传递给转换处理器：

```
transform --expression='new StringBuilder(payload).reverse()'
```

如果参数值需要嵌入单个引号，请使用两个单引号，如下所示：

```
// Query is: Select * from /Customers where name='Smith'
scan --query='Select * from /Customers where name=''Smith'''
```

### 16.1.引号和逃逸

Spring 有一个基于外壳的客户机，它与数据流服务器进行对话，并负责**解析**DSL。反过来，应用程序可能具有依赖于嵌入式语言的应用程序属性，例如**Spring Expression Language**。

shell、数据流 DSL 解析器和 SPEL 都有关于它们如何处理引号以及语法转义如何工作的规则。当结合在一起时，可能会出现混乱。本节解释了应用的规则，并提供了当涉及所有三个组件时可能遇到的最复杂情况的示例。

|   |并不总是那么复杂<br/><br/>如果不使用数据流 shell（例如，如果直接使用 RESTAPI）或如果应用程序属性不是 SPEL 表达式，则转义规则更简单。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 16.1.1.外壳规则

可以说，就报价而言，最复杂的组件是 shell。不过，规则可以很简单地列出：

* shell 命令由键（`--something`）和相应的值组成。不过，还有一种特殊的无键映射，将在后面进行描述。

* 值通常不能包含空格，因为空格是命令的默认分隔符。

* 但是，可以通过用引号（单引号（`'`）或双引号（`"`）包围该值来添加空格。

* 传递到部署属性内部的值（例如，`deployment <stream-name> --properties " …​"`）不应再次引用。

* 如果周围有引号，则值可以通过用反斜杠（`\`）作为前缀来嵌入相同类型的文字引号。

* 其他的转义也可以使用，例如`\t`，`\n`，`\r`，`\f`以及`\uxxxx`形式的 Unicode 转义。

* 无键映射是以一种特殊的方式处理的，因此它不需要引用来包含空格。

例如，shell 支持`!`命令来执行本机 shell 命令。`!`接受单个无键参数。这就是为什么下面的示例有效的原因：

```
dataflow:>! rm something
```

这里的参数是整个`rm something`字符串，该字符串按原样传递给底层 shell。

再举一个例子，下面的命令是严格等价的，参数值是`something`（不带引号）：

```
dataflow:>stream destroy something
dataflow:>stream destroy --name something
dataflow:>stream destroy "something"
dataflow:>stream destroy --name "something"
```

#### 16.1.2.属性文件规则

当从文件中加载属性时，规则会放松。

* 属性文件（包括 Java 和 YAML）中使用的特殊字符需要转义。例如`\`应由`\\`代替，`\t`应由`\\t`代替，等等。

* 对于 Java 属性文件（`--propertiesFile <FILE_PATH>.properties`），属性值不应被引号包围。即使它们有空位，也不需要。

  ```
  filter.expression=payload > 5
  ```

* 但是，对于 YAML 属性文件（`--propertiesFile <FILE_PATH>.yaml`），值需要用双引号包围。

  ```
  app:
      filter:
          filter:
              expression: "payload > 5"
  ```

#### 16.1.3.DSL 解析规则

在解析器级别（即在流或任务定义的主体内部），规则如下：

* 选项值通常被解析到第一个空格字符。

* 不过，它们也可以由文字字符串组成，用单引号或双引号环绕。

* 要嵌入这样的引号，请使用所需类型的两个连续引号。

因此，在以下示例中，过滤器应用程序的`--expression`选项的值在语义上是等效的：

```
filter --expression=payload>5
filter --expression="payload>5"
filter --expression='payload>5'
filter --expression='payload > 5'
```

可以说，最后一条更具可读性。多亏了周围的引语，这才成为可能。实际表达式是`payload > 5`。

现在，假设我们要针对字符串消息进行测试。如果我们想将有效负载与 spel 文字字符串`"something"`进行比较，我们可以使用以下方法：

```
filter --expression=payload=='something'           (1)
filter --expression='payload == ''something'''     (2)
filter --expression='payload == "something"'       (3)
```

|**1**|这是可行的，因为没有空间。不过，它不是很清晰。|
|-----|-----------------------------------------------------------------------------------------------------------------------|
|**2**|这使用单引号来保护整个论证。因此，实际的单个报价需要加倍。|
|**3**|SPEL 识别带有单引号或双引号的字符串文字，因此最后一个方法可以说是可读性最强的。|

请注意，前面的示例是在 shell 之外考虑的（例如，当直接调用 RESTAPI 时）。当在 shell 中输入时，很可能整个流定义本身就在双引号中，这将需要转义。整个例子如下：

```
dataflow:>stream create something --definition "http | filter --expression=payload='something' | log"

dataflow:>stream create something --definition "http | filter --expression='payload == ''something''' | log"

dataflow:>stream create something --definition "http | filter --expression='payload == \"something\"' | log"
```

#### 16.1.4.SPEL 语法和 SPEL 文字

最后一块拼图是关于 spel 表达式的。许多应用程序接受将被解释为 SPEL 表达式的选项，并且，正如前面所看到的，字符串文字也以一种特殊的方式处理。规则如下：

* 文字可以用单引号或双引号括起来。

* 引号需要加倍才能嵌入文字引号。双引号内的单引号不需要特殊处理，反之亦然。

作为最后一个示例，假设你希望使用[转换处理器]($https://docs.spring.io/spring-cloud-stream-app-starters/docs/current/reference/html/spring-cloud-stream-modules-processors.html#spring-clound-stream-modules-transform-processor)。此处理器接受`expression`选项，这是一个 SPEL 表达式。它将根据传入的消息进行评估，缺省值为`payload`（未更改消息有效负载的转发）。

重要的是要理解以下陈述是等效的：

```
transform --expression=payload
transform --expression='payload'
```

然而，它们不同于以下（以及它们的变体）：

```
transform --expression="'payload'"
transform --expression='''payload'''
```

第一个系列计算的是消息有效负载，而后面的示例计算的是文字字符串`payload`。

#### 16.1.5.把这一切放在一起

作为最后一个完整的示例，请考虑如何通过在数据流壳层的上下文中创建一个流来强制将所有消息转换为字符串文字`hello world`:

```
dataflow:>stream create something --definition "http | transform --expression='''hello world''' | log" (1)

dataflow:>stream create something --definition "http | transform --expression='\"hello world\"' | log" (2)

dataflow:>stream create something --definition "http | transform --expression=\"'hello world'\" | log" (2)
```

|**1**|在第一行中，字符串周围是单引号（在数据流解析器级别），但它们需要加倍，因为它们位于字符串文本中（由等号之后的第一个引号开始）。|
|-----|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|第二行和第三行分别使用单引号和双引号，以在数据流解析器级别上包含整个字符串。因此，另一种报价可以在字符串中使用。不过，整个过程都在 shell 的`--definition`参数中，该参数使用了双引号。因此，双引号被转义（在 shell 级别）。|

# 溪流

本节将更详细地介绍如何创建流，这些流是[Spring Cloud Stream](https://cloud.spring.io/spring-cloud-stream/)应用程序的集合。它涵盖了创建和部署流等主题。

如果你刚开始使用 Spring 云数据流，那么在深入了解本节之前，你可能应该阅读[开始](#getting-started)指南。

## 17. 导言

流是长期存在的[Spring Cloud Stream](https://cloud.spring.io/spring-cloud-stream/)应用程序的集合，它们通过消息传递中间件彼此通信。基于文本的 DSL 定义了应用程序之间的配置和数据流。虽然为你提供了许多应用程序来实现通用用例，但你通常会创建一个自定义 Spring 云流应用程序来实现自定义业务逻辑。

流的一般生命周期是：

1. 登记申请。

2. 创建一个流定义。

3. 部署流。

4. 取消部署或破坏该流。

5. 升级或回滚流中的应用程序。

对于部署流，数据流服务器必须被配置为将部署委托给 Spring 云生态系统中名为[Skipper](https://cloud.spring.io/spring-cloud-skipper/)的新服务器。

此外，你可以将 Skipper 配置为将应用程序部署到一个或多个 Cloud Foundry ORGS 和空间、Kubernetes 集群上的一个或多个名称空间，或者本地机器。在数据流中部署流时，可以指定在部署时使用哪个平台。Skipper 还为数据流提供了对部署的流执行更新的功能。可以通过多种方式更新流中的应用程序，但最常见的示例之一是使用新的自定义业务逻辑升级处理器应用程序，同时不使用现有的源和接收器应用程序。

### 17.1.流管道 DSL

流是通过使用受 Unix 启发的[管道语法](https://en.wikipedia.org/wiki/Pipeline_(Unix)）来定义的。该语法使用垂直条形（称为“管道”）来连接多个命令。UNIX 中的命令`ls -l | grep key | less`获取`ls -l`进程的输出，并将其输送到`grep key`进程的输入。然后，将`grep`的输出发送到`less`进程的输入。每个`|`符号将左侧命令的标准输出与右侧命令的标准输入连接起来。数据从左到右在管道中流动。

在 Data Flow 中，UNIX 命令被[Spring Cloud Stream](https://cloud.spring.io/spring-cloud-stream/)应用程序代替，每个管道符号表示通过消息传递中间件（例如 RabbitMQ 或 Apache Kafka）连接应用程序的输入和输出。

每个 Spring Cloud Stream 应用程序都以一个简单的名称注册。注册过程指定可以在哪里获得应用程序（例如，在 Maven 存储库或 Docker 注册表中）。你可以在[section](#spring-cloud-dataflow-register-stream-apps)中找到有关如何注册 Spring 云流应用程序的更多信息。在数据流中，我们将 Spring 云应用程序分为源、处理器或接收器。

作为一个简单的示例，考虑从 HTTP 源收集数据并将其写入文件接收器。使用 DSL，流描述为：

`http | file`

涉及某些处理的流将表示为：

`http | filter | transform | file`

可以使用 shell 的`stream create`命令创建流定义，如下例所示：

`dataflow:> stream create --name httpIngest --definition "http | file"`

流 DSL 被传递到`--definition`命令选项。

流定义的部署是通过 shell 的`stream deploy`命令完成的，如下所示：

`dataflow:> stream deploy --name ticktock`

[开始](#getting-started)部分向你展示了如何启动服务器以及如何启动和使用 Spring 云数据流壳层。

请注意，shell 调用了数据流服务器的 REST API。有关直接向服务器发出 HTTP 请求的详细信息，请参见[REST API 指南](#api-guide)。

|   |在命名流定义时，请记住，流中的每个应用程序都将在平台上创建，其名称的格式为`<stream name>-<app name>`。因此，生成的应用程序名称的总长度不能超过 58 个字符。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 17.2.流应用程序 DSL

可以使用流应用程序 DSL 为 Spring 云流应用程序中的每个定义自定义绑定属性。有关更多信息，请参见 microSite 的[流应用程序 DSL](https://dataflow.spring.io/docs/feature-guides/streams/stream-application-dsl/)部分。

考虑下面的 Java 接口，它定义了一个输入方法和两个输出方法：

```
public interface Barista {

    @Input
    SubscribableChannel orders();

    @Output
    MessageChannel hotDrinks();

    @Output
    MessageChannel coldDrinks();
}
```

进一步考虑以下 Java 接口，这是创建 Kafka Streams 应用程序的典型方法：

```
interface KStreamKTableBinding {

    @Input
    KStream<?, ?> inputStream();

    @Input
    KTable<?, ?> inputTable();
}
```

在这些具有多个输入和输出绑定的情况下，数据流不能对从一个应用程序到另一个应用程序的数据流进行任何假设。因此，你需要将绑定属性设置为“连接”应用程序。**流应用程序 DSL**使用“双管道”，而不是“管道符号”，以表示数据流不应配置应用程序的绑定属性。想一想`||`的意思是“平行”。下面的示例展示了这样一个“并行”定义：

```
dataflow:> stream create --definition "orderGeneratorApp || baristaApp || hotDrinkDeliveryApp || coldDrinkDeliveryApp" --name myCafeStream
```

|   |打破零钱！SCDF Local、Cloud Foundry1.7.0 至 1.7.2 和 SCDF Kubernetes1.7.0 至 1.7.1 的版本使用`comma`字符作为应用程序之间的分隔符。这引起了传统的流 DSL 的突破性变化。虽然并不理想，但更改分隔符字符被认为是对现有用户影响最小的最佳解决方案。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

这个流有四个应用程序。`baristaApp`有两个输出目的地，`hotDrinks`和`coldDrinks`，分别被`hotDrinkDeliveryApp`和`coldDrinkDeliveryApp`使用。部署此流时，需要设置绑定属性，以便`baristaApp`将热饮消息发送到`hotDrinkDeliveryApp`目的地，并将冷饮消息发送到`coldDrinkDeliveryApp`目的地。下面的清单就是这样做的：

```
app.baristaApp.spring.cloud.stream.bindings.hotDrinks.destination=hotDrinksDest
app.baristaApp.spring.cloud.stream.bindings.coldDrinks.destination=coldDrinksDest
app.hotDrinkDeliveryApp.spring.cloud.stream.bindings.input.destination=hotDrinksDest
app.coldDrinkDeliveryApp.spring.cloud.stream.bindings.input.destination=coldDrinksDest
```

如果希望使用消费者组，则需要分别在生产者应用程序和消费者应用程序上设置 Spring 云流应用程序属性`spring.cloud.stream.bindings.<channelName>.producer.requiredGroups`和`spring.cloud.stream.bindings.<channelName>.group`。

流应用程序 DSL 的另一个常见用例是部署一个 HTTP 网关应用程序，该应用程序向 Kafka 或 RabbitMQ 应用程序发送同步请求或回复消息。在这种情况下，HTTP 网关应用程序和 Kafka 或 RabbitMQ 应用程序都可以是 Spring 不利用 Spring 云流库的集成应用程序。

也可以使用流应用程序 DSL 仅部署单个应用程序。

### 17.3.应用程序属性

每个应用程序都获取属性来定制其行为。例如，`http`源模块公开了一个`port`设置，该设置允许从默认值更改数据摄入端口：

```
dataflow:> stream create --definition "http --port=8090 | log" --name myhttpstream
```

这个`port`属性实际上与标准的 Spring boot`server.port`属性相同。数据流增加了使用简写形式`port`而不是`server.port`的能力。你还可以指定 longhand 版本：

```
dataflow:> stream create --definition "http --server.port=8000 | log" --name myhttpstream
```

在[流应用程序属性](#spring-cloud-dataflow-application-properties)一节中将更多地讨论这种简写行为。如果你有[已注册的应用程序属性元数据](https://dataflow.spring.io/docs/applications/application-metadata/#using-application-metadata)，则可以在输入`--`后在 shell 中使用 tab 补全来获得候选属性名列表。

shell 为应用程序属性提供了制表符补全功能。`app info --name <appName> --type <appType>`shell 命令为所有受支持的属性提供了额外的文档。

|   |支持的流`<appType>`的可能性有：`source`，`processor`，和`sink`。|
|---|----------------------------------------------------------------------------------|

## 18. 流生命周期

流的生命周期经历了以下几个阶段：

1. [注册一个流应用程序](#spring-cloud-dataflow-register-stream-apps)

2. [创建一个流](#spring-cloud-dataflow-create-stream)

3. [部署流](#spring-cloud-dataflow-deploy-stream)

4. [破坏一条小溪](#spring-cloud-dataflow-destroy-stream)或[取消部署流](#spring-cloud-dataflow-undeploy-stream)

5. [Upgrade](#spring-cloud-dataflow-streams-upgrading)或[roll back](#spring-cloud-dataflow-streams-rollback)流中的应用程序。

[Skipper](https://cloud.spring.io/spring-cloud-skipper/)是一种服务器，它允许你在多个云平台上发现 Spring 引导应用程序并管理它们的生命周期。

Skipper 中的应用程序作为包捆绑在一起，包中包含应用程序的资源位置、应用程序属性和部署属性。你可以认为 Skipper 包类似于工具中的包，如`apt-get`或`brew`。

当数据流部署流时，它会生成一个包并上载到 Skipper，该包表示流中的应用程序。在流中升级或回滚应用程序的后续命令将传递给 Skipper。此外，流定义是从包中反向工程的，流的状态也被委托给 Skipper。

### 18.1.注册一个流应用程序

你可以使用`app register`命令注册版本流应用程序。你必须提供唯一的名称、应用程序类型和可以解析为应用程序工件的 URI。对于类型，请指定`source`、`processor`或`sink`。该版本是从 URI 解析的。以下是几个例子：

```
dataflow:>app register --name mysource --type source --uri maven://com.example:mysource:0.0.1
dataflow:>app register --name mysource --type source --uri maven://com.example:mysource:0.0.2
dataflow:>app register --name mysource --type source --uri maven://com.example:mysource:0.0.3

dataflow:>app list --id source:mysource
╔═══╤══════════════════╤═════════╤════╤════╗
║app│      source      │processor│sink│task║
╠═══╪══════════════════╪═════════╪════╪════╣
║   │> mysource-0.0.1 <│         │    │    ║
║   │mysource-0.0.2    │         │    │    ║
║   │mysource-0.0.3    │         │    │    ║
╚═══╧══════════════════╧═════════╧════╧════╝

dataflow:>app register --name myprocessor --type processor --uri file:///Users/example/myprocessor-1.2.3.jar

dataflow:>app register --name mysink --type sink --uri https://example.com/mysink-2.0.1.jar
```

应用程序 URI 应该符合以下模式格式：

* Maven 模式：

  ```
  maven://<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>
  ```

* HTTP 架构：

  ```
  http://<web-path>/<artifactName>-<version>.jar
  ```

* 文件架构：

  ```
  file:///<local-path>/<artifactName>-<version>.jar
  ```

* Docker 模式：

  ```
  docker:<docker-image-path>/<imageName>:<version>
  ```

|   |对于版本控制的流应用程序，URI`<version>`部分是必需的。<br/>Skipper 使用多版本控制的流应用程序允许通过使用部署属性在运行时升级或回滚这些应用程序。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

如果你想注册使用 RabbitMQ 活页夹构建的`http`和`log`应用程序的快照版本，可以执行以下操作：

```
dataflow:>app register --name http --type source --uri maven://org.springframework.cloud.stream.app:http-source-rabbit:1.2.1.BUILD-SNAPSHOT
dataflow:>app register --name log --type sink --uri maven://org.springframework.cloud.stream.app:log-sink-rabbit:1.2.1.BUILD-SNAPSHOT
```

如果希望一次注册多个应用程序，可以将它们存储在一个属性文件中，其中键的格式为`<type>.<name>`，值是 URI。

例如，要注册使用 RabbitMQ 绑定程序构建的`http`和`log`应用程序的快照版本，可以在属性文件中包含以下内容（例如，`stream-apps.properties`）：

```
source.http=maven://org.springframework.cloud.stream.app:http-source-rabbit:1.2.1.BUILD-SNAPSHOT
sink.log=maven://org.springframework.cloud.stream.app:log-sink-rabbit:1.2.1.BUILD-SNAPSHOT
```

然后，要大量导入应用程序，请使用`app import`命令，并通过`--uri`开关提供属性文件的位置，如下所示：

```
dataflow:>app import --uri file:///<YOUR_FILE_LOCATION>/stream-apps.properties
```

使用`--type app`注册应用程序与注册`source`、`processor`或`sink`相同。类型`app`的应用程序只能在流应用程序 DSL 中使用（它在 DSL 中使用双管道`||`而不是单管道`|`），并指示数据流不要配置应用程序的 Spring 云流绑定属性。使用`--type app`注册的应用程序不必是 Spring 云流应用程序。它可以是任何 Spring 启动应用程序。有关使用此应用程序类型的更多信息，请参见[流应用 DSL 介绍](#spring-cloud-dataflow-stream-app-dsl)。

你可以注册相同应用程序的多个版本（例如，相同的名称和类型），但只能将其中一个设置为默认值。默认版本用于部署流。

第一次注册应用程序时，它被标记为默认值。可以使用`app default`命令更改默认的应用程序版本：

```
dataflow:>app default --id source:mysource --version 0.0.2
dataflow:>app list --id source:mysource
╔═══╤══════════════════╤═════════╤════╤════╗
║app│      source      │processor│sink│task║
╠═══╪══════════════════╪═════════╪════╪════╣
║   │mysource-0.0.1    │         │    │    ║
║   │> mysource-0.0.2 <│         │    │    ║
║   │mysource-0.0.3    │         │    │    ║
╚═══╧══════════════════╧═════════╧════╧════╝
```

`app list --id <type:name>`命令列出了给定流应用程序的所有版本。

`app unregister`命令有一个可选的`--version`参数，用于指定要取消注册的应用程序版本：

```
dataflow:>app unregister --name mysource --type source --version 0.0.1
dataflow:>app list --id source:mysource
╔═══╤══════════════════╤═════════╤════╤════╗
║app│      source      │processor│sink│task║
╠═══╪══════════════════╪═════════╪════╪════╣
║   │> mysource-0.0.2 <│         │    │    ║
║   │mysource-0.0.3    │         │    │    ║
╚═══╧══════════════════╧═════════╧════╧════╝
```

如果`--version`未指定，则默认版本未注册。

|   |流中的所有应用程序都应该为要部署的流设置一个默认版本。<br/>否则，在部署过程中它们将被视为未注册的应用程序。<br/>使用`app default`命令设置默认值。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

```
app default --id source:mysource --version 0.0.3
dataflow:>app list --id source:mysource
╔═══╤══════════════════╤═════════╤════╤════╗
║app│      source      │processor│sink│task║
╠═══╪══════════════════╪═════════╪════╪════╣
║   │mysource-0.0.2    │         │    │    ║
║   │> mysource-0.0.3 <│         │    │    ║
╚═══╧══════════════════╧═════════╧════╧════╝
```

`stream deploy`需要设置默认的应用程序版本。不过，`stream update`和`stream rollback`命令可以使用所有（默认和非默认）注册的应用程序版本。

下面的命令创建了一个使用默认 MySource 版本（0.0.3）的流：

```
dataflow:>stream create foo --definition "mysource | log"
```

然后我们可以将版本更新到 0.0.2:

```
dataflow:>stream update foo --properties version.mysource=0.0.2
```

|   |只有预先注册的应用程序才能用于`deploy`、`update`或`rollback`a 流。|
|---|-------------------------------------------------------------------------------------------|

将`mysource`更新为`0.0.1`（未注册）的尝试失败。

#### 18.1.1.注册支持的应用程序和任务

为了方便起见，我们为所有开箱即用流和任务或批处理应用程序启动器提供了带有应用程序 URI（用于 Maven 和 Docker）的静态文件。你可以指向这个文件并大量导入所有的应用程序 URI。否则，正如前面所解释的，你可以单独注册它们，或者拥有自己的自定义属性文件，其中只包含所需的应用程序 URI。但是，我们建议在自定义属性文件中设置一个所需的应用程序 URI 的“集中”列表。

##### Spring Cloud Stream 应用程序初学者

下表包括指向基于 Spring Cloud Stream2.1.x 和 Spring Boot2.1.x 的可用流应用程序启动器的`dataflow.spring.io`链接：

|    Artifact Type    |                                        Stable Release                                        |快照发布|
|---------------------|----------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|  RabbitMQ + Maven   | [dataflow.spring.io/rabbitmq-maven-latest](https://dataflow.spring.io/rabbitmq-maven-latest) |[dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-rabbit-maven](https://dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-rabbit-maven)|
|  RabbitMQ + Docker  |[dataflow.spring.io/rabbitmq-docker-latest](https://dataflow.spring.io/rabbitmq-docker-latest)|[dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-rabbit-docker](https://dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-rabbit-docker)|
|Apache Kafka + Maven |    [dataflow.spring.io/kafka-maven-latest](https://dataflow.spring.io/kafka-maven-latest)    |[dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-kafka-maven](https://dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-kafka-maven)|
|Apache Kafka + Docker|   [dataflow.spring.io/kafka-docker-latest](https://dataflow.spring.io/kafka-docker-latest)   |[dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-kafka-docker](https://dataflow.spring.io/Einstein-BUILD-SNAPSHOT-stream-applications-kafka-docker)|

|   |默认情况下，应用程序启动执行器端点是安全的。你可以通过使用`app.*.spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration`属性部署流来禁用安全性。<br/>在 Kubernetes 上，请参阅[活性和准备状态探查](#getting-started-kubernetes-probes)部分，以了解如何为执行器端点配置<br/>安全性。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |从 Spring Cloud Stream2.1GA 版本开始，我们现在与 Spring Cloud 函数<br/>编程模型具有强大的互操作性。在此基础上，使用 Einstein Release-Train，现在可以选择几个流应用<br/>启动器，并使用函数式编程模型将它们组合成一个应用程序。查看["Composed Function Support in<br/>Spring Cloud Data Flow"](https://spring.io/blog/2019/01/09/composed-function-support-in-spring-cloud-data-flow)博客，以了解有关开发人员和业务流程的更多信息，并提供示例。|
|---| |

##### Spring 云任务应用程序初学者

下表包括基于 Spring Cloud Task2.1.x 和 Spring Boot2.1.x 的可用任务应用程序启动器：

|Artifact Type|                                    Stable Release                                    |快照发布|
|-------------|--------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
|    Maven    | [dataflow.spring.io/task-maven-latest](https://dataflow.spring.io/task-maven-latest) |[dataflow.spring.io/Elston-BUILD-SNAPSHOT-task-applications-maven](https://dataflow.spring.io/Elston-BUILD-SNAPSHOT-task-applications-maven)|
|   Docker    |[dataflow.spring.io/task-docker-latest](https://dataflow.spring.io/task-docker-latest)|[dataflow.spring.io/Elston-BUILD-SNAPSHOT-task-applications-docker](https://dataflow.spring.io/Elston-BUILD-SNAPSHOT-task-applications-docker)|

你可以在[任务应用程序启动项目页面](https://cloud.spring.io/spring-cloud-task-app-starters/)和相关参考文档中找到有关可用任务启动器的更多信息。有关可用的流启动器的更多信息，请查看[流应用程序启动项目页面](https://cloud.spring.io/spring-cloud-stream-app-starters/)和相关的参考文档。

例如，如果你想注册所有使用 Kafka Binder 构建的批量开箱即用流应用程序，可以使用以下命令：

```
$ dataflow:>app import --uri https://dataflow.spring.io/kafka-maven-latest
```

或者，你可以使用 Rabbit Binder 注册所有的流应用程序，如下所示：

```
$ dataflow:>app import --uri https://dataflow.spring.io/rabbitmq-maven-latest
```

你还可以传递`--local`选项（默认情况下是`true`），以指示是否应该在 shell 进程本身中解析属性文件位置。如果应该从数据流服务器进程解析位置，请指定`--local false`。

|   |当使用`app register`或`app import`时，如果应用程序已经用<br/>提供的名称、类型和版本注册，则默认情况下不会覆盖该应用程序。如果你想重写<br/>预先存在的应用程序`uri`或`metadata-uri`坐标，请包括`--force`选项。<br/>但是，注意，一旦下载，应用程序可能会基于资源<br/>位置在数据流服务器上本地缓存。如果资源位置没有改变（即使实际资源*字节*可能是不同的），它<br/>也不会被重新下载。另一方面，当使用`maven://`资源时，使用一个固定的位置仍然可以绕过<br/>缓存（如果使用`-SNAPSHOT`版本）。此外，如果流已经部署并使用了某个注册应用程序的某个版本，然后（强制）重新注册一个<br/>不同的应用程序没有任何效果，直到流再次部署。|
|---| |

|   |在某些情况下，资源是在服务器端解析的。在其他情况下，<br/>URI 被传递给一个运行时容器实例，并在其中进行解析。有关更多详细信息，请参见<br/>每个数据流服务器的具体文档。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 18.1.2.创建自定义应用程序

虽然数据流包括源程序、处理器程序和接收程序，但你可以扩展这些程序或编写自定义[Spring Cloud Stream](https://github.com/spring-cloud/spring-cloud-stream)应用程序。

Spring 使用[Spring Initializr](https://start.spring.io/)创建 Spring 云流应用程序的过程在 Spring 云流[文件](https://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/index.html#_getting_started)中进行了详细说明。你可以将多个绑定程序包含到一个应用程序。如果你这样做了，请参阅[[passing\_producer\_consumer\_properties]]中的说明，以了解如何配置它们。

为了支持允许的属性，在 Spring 云数据流中运行的 Spring 云流应用程序可以将 Spring boot`configuration-processor`作为可选依赖项，如以下示例所示：

```
<dependencies>
  <!-- other dependencies -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
  </dependency>
</dependencies>
```

注意：确保`spring-boot-maven-plugin`包含在 POM 中。该插件对于创建注册在 Spring 云数据流中的可执行 JAR 是必需的。 Spring InitialZR 包括在所生成的插件中的 POM。

一旦创建了自定义应用程序，就可以注册它，如[注册一个流应用程序](#spring-cloud-dataflow-register-stream-apps)中所述。

### 18.2.创建一个流

Spring 云数据流服务器公开了用于管理流定义的生命周期的完整 RESTful API，但最简单的使用方法是通过 Spring 云数据流壳层。[开始](#getting-started)部分描述了如何启动 shell。

新的流是在流定义的帮助下创建的。这些定义是从一个简单的 DSL 构建的。例如，考虑一下如果我们运行以下 shell 命令会发生什么：

```
dataflow:> stream create --definition "time | log" --name ticktock
```

这定义了一个名为`ticktock`的流，该流基于 DSL 表达式`time | log`。DSL 使用“pipe”符号（`|`）将源连接到接收器。

`stream info`命令显示了有关流的有用信息，如下例所示（与其输出一起）：

```
dataflow:>stream info ticktock
╔═══════════╤═════════════════╤══════════╗
║Stream Name│Stream Definition│  Status  ║
╠═══════════╪═════════════════╪══════════╣
║ticktock   │time | log       │undeployed║
╚═══════════╧═════════════════╧══════════╝
```

#### 18.2.1.流应用程序属性

应用程序属性是与流中的每个应用程序相关联的属性。在部署应用程序时，应用程序属性将通过命令行参数或环境变量应用于应用程序，这取决于底层的部署实现。

以下流可以具有在创建流时定义的应用程序属性：

```
dataflow:> stream create --definition "time | log" --name ticktock
```

`app info --name <appName> --type <appType>`shell 命令显示应用程序的公开应用程序属性。有关暴露的属性的更多信息，请参见[应用程序元数据](https://dataflow.spring.io/docs/applications/application-metadata)。

下面的清单显示了`time`应用程序的公开属性：

```
dataflow:> app info --name time --type source
╔══════════════════════════════╤══════════════════════════════╤══════════════════════════════╤══════════════════════════════╗
║         Option Name          │         Description          │           Default            │             Type             ║
╠══════════════════════════════╪══════════════════════════════╪══════════════════════════════╪══════════════════════════════╣
║trigger.time-unit             │The TimeUnit to apply to delay│<none>                        │java.util.concurrent.TimeUnit ║
║                              │values.                       │                              │                              ║
║trigger.fixed-delay           │Fixed delay for periodic      │1                             │java.lang.Integer             ║
║                              │triggers.                     │                              │                              ║
║trigger.cron                  │Cron expression value for the │<none>                        │java.lang.String              ║
║                              │Cron Trigger.                 │                              │                              ║
║trigger.initial-delay         │Initial delay for periodic    │0                             │java.lang.Integer             ║
║                              │triggers.                     │                              │                              ║
║trigger.max-messages          │Maximum messages per poll, -1 │1                             │java.lang.Long                ║
║                              │means infinity.               │                              │                              ║
║trigger.date-format           │Format for the date value.    │<none>                        │java.lang.String              ║
╚══════════════════════════════╧══════════════════════════════╧══════════════════════════════╧══════════════════════════════╝
```

下面的清单显示了`log`应用程序的公开属性：

```
dataflow:> app info --name log --type sink
╔══════════════════════════════╤══════════════════════════════╤══════════════════════════════╤══════════════════════════════╗
║         Option Name          │         Description          │           Default            │             Type             ║
╠══════════════════════════════╪══════════════════════════════╪══════════════════════════════╪══════════════════════════════╣
║log.name                      │The name of the logger to use.│<none>                        │java.lang.String              ║
║log.level                     │The level at which to log     │<none>                        │org.springframework.integratio║
║                              │messages.                     │                              │n.handler.LoggingHandler$Level║
║log.expression                │A SpEL expression (against the│payload                       │java.lang.String              ║
║                              │incoming message) to evaluate │                              │                              ║
║                              │as the logged message.        │                              │                              ║
╚══════════════════════════════╧══════════════════════════════╧══════════════════════════════╧══════════════════════════════╝
```

你可以在创建`stream`时为`time`和`log`应用程序指定应用程序属性，如下所示：

```
dataflow:> stream create --definition "time --fixed-delay=5 | log --level=WARN" --name ticktock
```

注意，在前面的示例中，为`fixed-delay`和`level`应用程序定义的`time`属性是 shell 补全提供的“简式”属性名称。这些“简式”属性名称仅适用于公开的属性。在所有其他情况下，你应该只使用完全限定的属性名。

#### 18.2.2.通用应用程序属性

Spring 除了通过 DSL 进行配置外，云数据流还提供了一种机制，用于设置由其启动的所有流应用程序的公共属性。这可以通过在启动服务器时添加带`spring.cloud.dataflow.applicationProperties.stream`前缀的属性来完成。这样做时，服务器将所有属性（不带前缀）传递给它启动的实例。

例如，通过使用以下选项启动数据流服务器，可以将所有启动的应用程序配置为使用特定的 Kafka 代理：

```
--spring.cloud.dataflow.applicationProperties.stream.spring.cloud.stream.kafka.binder.brokers=192.168.1.100:9092
--spring.cloud.dataflow.applicationProperties.stream.spring.cloud.stream.kafka.binder.zkNodes=192.168.1.100:2181
```

这样做会将`spring.cloud.stream.kafka.binder.brokers`和`spring.cloud.stream.kafka.binder.zkNodes`属性传递给所有启动的应用程序。

|   |使用此机制配置的属性的优先级低于流部署属性。<br/>如果在流部署时指定了具有相同键的属性（例如，`app.http.spring.cloud.stream.kafka.binder.brokers`重写公共属性），则会重写这些属性。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 18.3.部署流

本节描述了当 Spring 云数据流服务器负责部署流时如何部署流。它涵盖了通过使用 Skipper 服务部署和升级流。关于如何设置部署属性的描述适用于两种流部署方法。

考虑`ticktock`流定义：

```
dataflow:> stream create --definition "time | log" --name ticktock
```

要部署流，请使用以下 shell 命令：

```
dataflow:> stream deploy --name ticktock
```

数据流服务器委托跳过`time`和`log`应用程序的解析和部署。

`stream info`命令显示了有关流的有用信息，包括部署属性：

```
dataflow:>stream info --name ticktock
╔═══════════╤═════════════════╤═════════╗
║Stream Name│Stream Definition│  Status ║
╠═══════════╪═════════════════╪═════════╣
║ticktock   │time | log       │deploying║
╚═══════════╧═════════════════╧═════════╝

Stream Deployment properties: {
  "log" : {
    "resource" : "maven://org.springframework.cloud.stream.app:log-sink-rabbit",
    "spring.cloud.deployer.group" : "ticktock",
    "version" : "2.0.1.RELEASE"
  },
  "time" : {
    "resource" : "maven://org.springframework.cloud.stream.app:time-source-rabbit",
    "spring.cloud.deployer.group" : "ticktock",
    "version" : "2.0.1.RELEASE"
  }
}
```

对于`stream deploy`命令，有一个重要的可选命令参数（称为`--platformName`）。可以将 Skipper 配置为部署到多个平台。Skipper 预先配置了一个名为`default`的平台，该平台将应用程序部署到运行 Skipper 的本地机器上。`--platformName`命令行参数的默认值是`default`。如果通常部署到一个平台，则在安装 Skipper 时，可以重写`default`平台的配置。否则，将`platformName`指定为`stream platform-list`命令返回的值之一。

在前面的示例中，时间源每秒发送当前时间作为消息，而日志接收器通过使用日志框架输出它。你可以跟踪`stdout`日志（它有一个`<instance>`后缀）。日志文件位于数据流服务器的日志输出中显示的目录中，如以下清单所示：

```
$ tail -f /var/folders/wn/8jxm_tbd1vj28c8vj37n900m0000gn/T/spring-cloud-dataflow-912434582726479179/ticktock-1464788481708/ticktock.log/stdout_0.log
2016-06-01 09:45:11.250  INFO 79194 --- [  kafka-binder-] log.sink    : 06/01/16 09:45:11
2016-06-01 09:45:12.250  INFO 79194 --- [  kafka-binder-] log.sink    : 06/01/16 09:45:12
2016-06-01 09:45:13.251  INFO 79194 --- [  kafka-binder-] log.sink    : 06/01/16 09:45:13
```

你还可以在创建流时通过传递`--deploy`标志一步创建和部署流，如下所示：

```
dataflow:> stream create --definition "time | log" --name ticktock --deploy
```

然而，在现实世界的用例中，在一步中创建和部署流并不常见。原因是，当你使用`stream deploy`命令时，你可以传入定义如何将应用程序映射到平台上的属性（例如，要使用的容器的内存大小是多少，要运行的每个应用程序的数量是多少，以及是否启用数据分区功能）。属性还可以覆盖在创建流时设置的应用程序属性。下一节将详细介绍此功能。

#### 18.3.1.部署属性

部署流时，可以指定可以控制应用程序部署和配置方式的属性。有关更多信息，请参见微型网站的[部署属性](https://dataflow.spring.io/docs/feature-guides/streams/deployment-properties/)部分。

### 18.4.破坏一条小溪

你可以通过从 shell 中发出`stream destroy`命令来删除流，如下所示：

```
dataflow:> stream destroy --name ticktock
```

如果流已部署，则在删除流定义之前未部署流。

### 18.5.取消部署流

通常，你希望停止流，但保留名称和定义以备将来使用。在这种情况下，你可以`undeploy`流的名称：

```
dataflow:> stream undeploy --name ticktock
dataflow:> stream deploy --name ticktock
```

你可以稍后发出`deploy`命令重新启动它：

```
dataflow:> stream deploy --name ticktock
```

### 18.6.验证流

有时，流定义中包含的应用程序在其注册中包含无效的 URI。这可能是由于在应用程序注册时输入了无效的 URI，或者应用程序从要从中提取它的存储库中删除而导致的。要验证流中包含的所有应用程序都是可解析的，用户可以使用`validate`命令：

```
dataflow:>stream validate ticktock
╔═══════════╤═════════════════╗
║Stream Name│Stream Definition║
╠═══════════╪═════════════════╣
║ticktock   │time | log       ║
╚═══════════╧═════════════════╝

ticktock is a valid stream.
╔═══════════╤═════════════════╗
║ App Name  │Validation Status║
╠═══════════╪═════════════════╣
║source:time│valid            ║
║sink:log   │valid            ║
╚═══════════╧═════════════════╝
```

在前面的示例中，用户验证了他们的 ticktock 流。`source:time`和`sink:log`都是有效的。现在，我们可以看到，如果我们有一个流定义，其中注册的应用程序具有无效的 URI，会发生什么情况：

```
dataflow:>stream validate bad-ticktock
╔════════════╤═════════════════╗
║Stream Name │Stream Definition║
╠════════════╪═════════════════╣
║bad-ticktock│bad-time | log   ║
╚════════════╧═════════════════╝

bad-ticktock is an invalid stream.
╔═══════════════╤═════════════════╗
║   App Name    │Validation Status║
╠═══════════════╪═════════════════╣
║source:bad-time│invalid          ║
║sink:log       │valid            ║
╚═══════════════╧═════════════════╝
```

在这种情况下， Spring 云数据流声明该流是无效的，因为`source:bad-time`具有无效的 URI。

### 18.7.更新数据流

要更新流，请使用`stream update`命令，该命令将`--properties`或`--propertiesFile`作为命令参数。Skipper 有一个重要的新顶级前缀：`version`。以下命令部署`http | log`流（以及在部署时注册的`log`的版本`1.1.0.RELEASE`）：

```
dataflow:> stream create --name httptest --definition "http --server.port=9000 | log"
dataflow:> stream deploy --name httptest
dataflow:>stream info httptest
╔══════════════════════════════╤══════════════════════════════╤════════════════════════════╗
║             Name             │             DSL              │          Status            ║
╠══════════════════════════════╪══════════════════════════════╪════════════════════════════╣
║httptest                      │http --server.port=9000 | log │deploying                   ║
╚══════════════════════════════╧══════════════════════════════╧════════════════════════════╝

Stream Deployment properties: {
  "log" : {
    "spring.cloud.deployer.indexed" : "true",
    "spring.cloud.deployer.group" : "httptest",
    "maven://org.springframework.cloud.stream.app:log-sink-rabbit" : "1.1.0.RELEASE"
  },
  "http" : {
    "spring.cloud.deployer.group" : "httptest",
    "maven://org.springframework.cloud.stream.app:http-source-rabbit" : "1.1.0.RELEASE"
  }
}
```

然后，下面的命令更新流以使用日志应用程序的`1.2.0.RELEASE`版本。在使用应用程序的特定版本更新流之前，我们需要确保应用程序已注册到该版本：

```
dataflow:>app register --name log --type sink --uri maven://org.springframework.cloud.stream.app:log-sink-rabbit:1.2.0.RELEASE
Successfully registered application 'sink:log'
```

然后我们可以更新应用程序：

```
dataflow:>stream update --name httptest --properties version.log=1.2.0.RELEASE
```

|   |你只能使用预先注册的应用程序版本`deploy`、`update`或`rollback`a 流。|
|---|---------------------------------------------------------------------------------------------------|

为了验证部署属性和更新的版本，我们可以使用`stream info`，如下例所示（其输出）：

```
dataflow:>stream info httptest
╔══════════════════════════════╤══════════════════════════════╤════════════════════════════╗
║             Name             │             DSL              │          Status            ║
╠══════════════════════════════╪══════════════════════════════╪════════════════════════════╣
║httptest                      │http --server.port=9000 | log │deploying                   ║
╚══════════════════════════════╧══════════════════════════════╧════════════════════════════╝

Stream Deployment properties: {
  "log" : {
    "spring.cloud.deployer.indexed" : "true",
    "spring.cloud.deployer.count" : "1",
    "spring.cloud.deployer.group" : "httptest",
    "maven://org.springframework.cloud.stream.app:log-sink-rabbit" : "1.2.0.RELEASE"
  },
  "http" : {
    "spring.cloud.deployer.group" : "httptest",
    "maven://org.springframework.cloud.stream.app:http-source-rabbit" : "1.1.0.RELEASE"
  }
}
```

### 18.8.强制更新流

在升级流时，你可以使用`--force`选项来部署当前部署的应用程序的新实例，即使没有应用程序或部署属性发生变化。当应用程序本身在启动时（例如，从 Spring Cloud Config Server）获得配置信息时，需要这种行为。你可以使用`--app-names`选项指定强制升级的应用程序。如果没有指定任何应用程序名称，则所有应用程序都将被迫升级。你可以指定`--force`和`--app-names`选项以及`--properties`或`--propertiesFile`选项。

### 18.9.流版本

Skipper 保留部署的流的历史记录。在更新一个流之后，会有第二个流的版本。你可以使用`stream history --name <name-of-stream>`命令查询版本的历史记录：

```
dataflow:>stream history --name httptest
╔═══════╤════════════════════════════╤════════╤════════════╤═══════════════╤════════════════╗
║Version│        Last updated        │ Status │Package Name│Package Version│  Description   ║
╠═══════╪════════════════════════════╪════════╪════════════╪═══════════════╪════════════════╣
║2      │Mon Nov 27 22:41:16 EST 2017│DEPLOYED│httptest    │1.0.0          │Upgrade complete║
║1      │Mon Nov 27 22:40:41 EST 2017│DELETED │httptest    │1.0.0          │Delete complete ║
╚═══════╧════════════════════════════╧════════╧════════════╧═══════════════╧════════════════╝
```

### 18.10.流清单

Skipper 保留所有应用程序、它们的应用程序属性以及它们的部署属性的“清单”，在所有值都被替换之后。这代表了部署到平台上的内容的最终状态。你可以使用以下命令查看流的任何版本的清单：

```
stream manifest --name <name-of-stream> --releaseVersion <optional-version>
```

如果没有指定`--releaseVersion`，则返回最后一个版本的清单。

下面的示例展示了清单的使用：

```
dataflow:>stream manifest --name httptest
```

使用该命令会产生以下输出：

```
# Source: log.yml
apiVersion: skipper.spring.io/v1
kind: SpringCloudDeployerApplication
metadata:
  name: log
spec:
  resource: maven://org.springframework.cloud.stream.app:log-sink-rabbit
  version: 1.2.0.RELEASE
  applicationProperties:
    spring.cloud.dataflow.stream.app.label: log
    spring.cloud.stream.bindings.input.group: httptest
    spring.cloud.dataflow.stream.name: httptest
    spring.cloud.dataflow.stream.app.type: sink
    spring.cloud.stream.bindings.input.destination: httptest.http
  deploymentProperties:
    spring.cloud.deployer.indexed: true
    spring.cloud.deployer.group: httptest
    spring.cloud.deployer.count: 1

---
# Source: http.yml
apiVersion: skipper.spring.io/v1
kind: SpringCloudDeployerApplication
metadata:
  name: http
spec:
  resource: maven://org.springframework.cloud.stream.app:http-source-rabbit
  version: 1.2.0.RELEASE
  applicationProperties:
    spring.cloud.dataflow.stream.app.label: http
    spring.cloud.stream.bindings.output.producer.requiredGroups: httptest
    server.port: 9000
    spring.cloud.stream.bindings.output.destination: httptest.http
    spring.cloud.dataflow.stream.name: httptest
    spring.cloud.dataflow.stream.app.type: source
  deploymentProperties:
    spring.cloud.deployer.group: httptest
```

大多数部署和应用程序属性都是由数据流设置的，以使应用程序能够相互对话，并发送带有标识标签的应用程序指标。

### 18.11.回滚一条流

你可以使用`stream rollback`命令回滚到流的上一个版本：

```
dataflow:>stream rollback --name httptest
```

可选的`--releaseVersion`命令参数添加了流的版本。如果未指定，则回滚操作将转到上一个流版本。

### 18.12.应用程序数量

应用程序计数是用于指定应用程序实例数量的系统的动态属性。有关更多信息，请参见微型网站的[应用程序数量](https://dataflow.spring.io/docs/feature-guides/streams/application-count/)部分。

### 18.13.Skipper 的升级策略

Skipper 有一个简单的“红/黑”升级策略。它使用与当前运行的版本一样多的实例来部署应用程序的新版本，并检查应用程序的`/health`端点。如果新应用程序的健康状况良好，则取消部署上一个应用程序。如果新应用程序的健康状况不佳，则所有新应用程序都未被部署，并且升级被认为是不成功的。

升级策略不是滚动升级，因此，如果应用程序的五个实例正在运行，那么，在阳光明媚的情况下，在未部署旧版本之前，也有五个新应用程序正在运行。

## 19. 流 DSL

本节将介绍[流 DSL 介绍](#spring-cloud-dataflow-stream-intro-dsl)中未涉及的流 DSL 的其他特性。

### 19.1.点击一条小溪

可以在流中的各个生产者端点创建分接头。有关更多信息，请参见微型网站的[敲击溪流](https://dataflow.spring.io/docs/feature-guides/streams/taps/)部分。

### 19.2.在流中使用标签

当一个流是由具有相同名称的多个应用程序组成时，它们必须用标签进行限定。有关更多信息，请参见微型网站的[标记应用程序](https://dataflow.spring.io/docs/feature-guides/streams/labels/)部分。

### 19.3.已命名的目的地

你可以使用指定的目的地，而不是引用源程序或接收器应用程序。有关更多信息，请参见微型网站的[已命名的目的地](https://dataflow.spring.io/docs/feature-guides/streams/named-destinations/)部分。

### 19.4.扇入扇出

通过使用指定的目的地，你可以支持扇入和扇出用例。有关更多信息，请参见微型网站的[扇入扇出](https://dataflow.spring.io/docs/feature-guides/streams/fanin-fanout/)部分。

## 20. 流 Java DSL

你可以使用`spring-cloud-dataflow-rest-client`模块提供的基于 Java 的 DSL，而不是使用 shell 来创建和部署流。有关更多信息，请参见微型网站的[Java DSL](https://dataflow.spring.io/docs/feature-guides/streams/java-dsl/)部分。

## 21. 具有多个绑定配置的流应用程序

在某些情况下，当需要连接到不同的消息传递中间件配置时，流可以将其应用程序绑定到多个 Spring 云流绑定程序。在这些情况下，你应该确保应用程序的绑定配置是适当的。例如，支持 Kafka 和 Rabbit Binder 的多绑定转换器是以下流中的处理器：

```
http | multibindertransform --expression=payload.toUpperCase() | log
```

|   |在前面的示例中，你将编写自己的`multibindertransform`应用程序。|
|---|--------------------------------------------------------------------------------------|

在这个流中，每个应用程序都以以下方式连接到消息传递中间件：

1. HTTP 源将事件发送到 RabbitMQ（`rabbit1`）。

2. 多绑定转换处理器接收来自 RabbitMQ（`rabbit1`）的事件，并将处理后的事件发送到 Kafka（`kafka1`）。

3. 日志接收器接收来自 Kafka（`kafka1`）的事件。

在这里，`rabbit1`和`kafka1`是 Spring Cloud Stream 应用程序属性中给出的绑定程序名称。基于这种设置，应用程序在其类空间中具有以下具有适当配置的绑定器：

* http:rabbit binder

* 转换：Kafka 和 Rabbit Binder

* 日志：Kafka Binder

`spring-cloud-stream``binder`配置属性可以在应用程序本身中设置。如果不是，则可以在部署流时通过`deployment`属性传递它们：

```
dataflow:>stream create --definition "http | multibindertransform --expression=payload.toUpperCase() | log" --name mystream

dataflow:>stream deploy mystream --properties "app.http.spring.cloud.stream.bindings.output.binder=rabbit1,app.multibindertransform.spring.cloud.stream.bindings.input.binder=rabbit1,
app.multibindertransform.spring.cloud.stream.bindings.output.binder=kafka1,app.log.spring.cloud.stream.bindings.input.binder=kafka1"
```

你可以通过部署属性指定任何绑定程序配置属性来覆盖它们。

## 22. 函数组成

函数组合允许你动态地将功能逻辑附加到现有的事件流应用程序。有关更多详细信息，请参见 microSite 的[函数组成](https://dataflow.spring.io/docs/feature-guides/streams/function-composition/)部分。

## 23. 功能应用程序

通过 Spring Cloud Stream3.x 添加[功能支持](https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/current/reference/html/spring-cloud-stream.html#spring-cloud-stream-overview-producing-consuming-messages)，你可以分别通过实现 Java util 的`Source`、`Sink`和`Processor`接口来构建`Supplier`应用程序。有关此功能的更多信息，请参见 SCDF 站点的[功能应用程序配方](https://dataflow.spring.io/docs/recipes/functional-apps/)。

## 24. 例子

本章包括以下几个例子：

* [简单的流处理](#spring-cloud-dataflow-simple-stream)

* [有状态流处理](#spring-cloud-dataflow-stream-partitions)

* [其他源和汇应用程序类型](#spring-cloud-dataflow-stream-app-types)

你可以在“[Samples](#dataflow-samples)”一章中找到更多示例的链接。

### 24.1.简单的流处理

作为一个简单处理步骤的示例，我们可以使用以下流定义将 HTTP 发布的数据的有效负载转换为大写：

```
http | transform --expression=payload.toUpperCase() | log
```

要创建这个流，请在 shell 中输入以下命令：

```
dataflow:> stream create --definition "http --server.port=9000 | transform --expression=payload.toUpperCase() | log" --name mystream --deploy
```

下面的示例使用 shell 命令来发布一些数据：

```
dataflow:> http post --target http://localhost:9000 --data "hello"
```

前面的示例在日志中生成大写`HELLO`，如下所示：

```
2016-06-01 09:54:37.749  INFO 80083 --- [  kafka-binder-] log.sink    : HELLO
```

### 24.2.有状态流处理

为了演示数据分区功能，下面的清单部署了一个以 Kafka 为绑定器的流：

```
dataflow:>stream create --name words --definition "http --server.port=9900 | splitter --expression=payload.split(' ') | log"
Created new stream 'words'

dataflow:>stream deploy words --properties "app.splitter.producer.partitionKeyExpression=payload,deployer.log.count=2"
Deployed stream 'words'

dataflow:>http post --target http://localhost:9900 --data "How much wood would a woodchuck chuck if a woodchuck could chuck wood"
> POST (text/plain;Charset=UTF-8) http://localhost:9900 How much wood would a woodchuck chuck if a woodchuck could chuck wood
> 202 ACCEPTED

dataflow:>runtime apps
╔════════════════════╤═══════════╤═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║App Id / Instance Id│Unit Status│                                                               No. of Instances / Attributes                                                               ║
╠════════════════════╪═══════════╪═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║words.log-v1        │ deployed  │                                                                             2                                                                             ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╢
║                    │           │       guid = 24166                                                                                                                                        ║
║                    │           │        pid = 33097                                                                                                                                        ║
║                    │           │       port = 24166                                                                                                                                        ║
║words.log-v1-0      │ deployed  │     stderr = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461063/words.log-v1/stderr_0.log     ║
║                    │           │     stdout = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461063/words.log-v1/stdout_0.log     ║
║                    │           │        url = https://192.168.0.102:24166                                                                                                                   ║
║                    │           │working.dir = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461063/words.log-v1                  ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╢
║                    │           │       guid = 41269                                                                                                                                        ║
║                    │           │        pid = 33098                                                                                                                                        ║
║                    │           │       port = 41269                                                                                                                                        ║
║words.log-v1-1      │ deployed  │     stderr = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461063/words.log-v1/stderr_1.log     ║
║                    │           │     stdout = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461063/words.log-v1/stdout_1.log     ║
║                    │           │        url = https://192.168.0.102:41269                                                                                                                   ║
║                    │           │working.dir = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461063/words.log-v1                  ║
╟────────────────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢
║words.http-v1       │ deployed  │                                                                             1                                                                             ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╢
║                    │           │       guid = 9900                                                                                                                                         ║
║                    │           │        pid = 33094                                                                                                                                        ║
║                    │           │       port = 9900                                                                                                                                         ║
║words.http-v1-0     │ deployed  │     stderr = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461054/words.http-v1/stderr_0.log    ║
║                    │           │     stdout = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461054/words.http-v1/stdout_0.log    ║
║                    │           │        url = https://192.168.0.102:9900                                                                                                                    ║
║                    │           │working.dir = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803461054/words.http-v1                 ║
╟────────────────────┼───────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢
║words.splitter-v1   │ deployed  │                                                                             1                                                                             ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╢
║                    │           │       guid = 33963                                                                                                                                        ║
║                    │           │        pid = 33093                                                                                                                                        ║
║                    │           │       port = 33963                                                                                                                                        ║
║words.splitter-v1-0 │ deployed  │     stderr = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803437542/words.splitter-v1/stderr_0.log║
║                    │           │     stdout = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803437542/words.splitter-v1/stdout_0.log║
║                    │           │        url = https://192.168.0.102:33963                                                                                                                   ║
║                    │           │working.dir = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/words-1542803437542/words.splitter-v1             ║
╚════════════════════╧═══════════╧═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
```

当你查看`words.log-v1-0`日志时，你应该会看到以下内容：

```
2016-06-05 18:35:47.047  INFO 58638 --- [  kafka-binder-] log.sink                                 : How
2016-06-05 18:35:47.066  INFO 58638 --- [  kafka-binder-] log.sink                                 : chuck
2016-06-05 18:35:47.066  INFO 58638 --- [  kafka-binder-] log.sink                                 : chuck
```

当你查看`words.log-v1-1`日志时，你应该会看到以下内容：

```
2016-06-05 18:35:47.047  INFO 58639 --- [  kafka-binder-] log.sink                                 : much
2016-06-05 18:35:47.066  INFO 58639 --- [  kafka-binder-] log.sink                                 : wood
2016-06-05 18:35:47.066  INFO 58639 --- [  kafka-binder-] log.sink                                 : would
2016-06-05 18:35:47.066  INFO 58639 --- [  kafka-binder-] log.sink                                 : a
2016-06-05 18:35:47.066  INFO 58639 --- [  kafka-binder-] log.sink                                 : woodchuck
2016-06-05 18:35:47.067  INFO 58639 --- [  kafka-binder-] log.sink                                 : if
2016-06-05 18:35:47.067  INFO 58639 --- [  kafka-binder-] log.sink                                 : a
2016-06-05 18:35:47.067  INFO 58639 --- [  kafka-binder-] log.sink                                 : woodchuck
2016-06-05 18:35:47.067  INFO 58639 --- [  kafka-binder-] log.sink                                 : could
2016-06-05 18:35:47.067  INFO 58639 --- [  kafka-binder-] log.sink                                 : wood
```

这个示例表明，包含相同单词的有效负载分割被路由到相同的应用程序实例。

### 24.3.其他源和汇应用程序类型

这个例子展示了一些更复杂的东西：将`time`源转换成其他的东西。另一种受支持的源类型是`http`，它接受通过 HTTP POST 请求来摄取数据。请注意，`http`源接受来自数据流服务器（缺省 8080）的不同端口上的数据。默认情况下，端口是随机分配的。

要创建一个使用`http`源但仍使用相同`log`接收器的流，我们将[简单的流处理](#spring-cloud-dataflow-simple-stream)示例中的原始命令更改为以下内容：

```
dataflow:> stream create --definition "http | log" --name myhttpstream --deploy
```

请注意，这一次，在实际发布一些数据（通过使用 shell 命令）之前，我们不会看到任何其他输出。要查看`http`源正在监听的随机分配的端口，请运行以下命令：

```
dataflow:>runtime apps

╔══════════════════════╤═══════════╤═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╗
║ App Id / Instance Id │Unit Status│                                                                    No. of Instances / Attributes                                                                    ║
╠══════════════════════╪═══════════╪═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╣
║myhttpstream.log-v1   │ deploying │                                                                                  1                                                                                  ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╢
║                      │           │       guid = 39628                                                                                                                                                  ║
║                      │           │        pid = 34403                                                                                                                                                  ║
║                      │           │       port = 39628                                                                                                                                                  ║
║myhttpstream.log-v1-0 │ deploying │     stderr = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/myhttpstream-1542803867070/myhttpstream.log-v1/stderr_0.log ║
║                      │           │     stdout = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/myhttpstream-1542803867070/myhttpstream.log-v1/stdout_0.log ║
║                      │           │        url = https://192.168.0.102:39628                                                                                                                             ║
║                      │           │working.dir = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/myhttpstream-1542803867070/myhttpstream.log-v1              ║
╟──────────────────────┼───────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╢
║myhttpstream.http-v1  │ deploying │                                                                                  1                                                                                  ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈╢
║                      │           │       guid = 52143                                                                                                                                                  ║
║                      │           │        pid = 34401                                                                                                                                                  ║
║                      │           │       port = 52143                                                                                                                                                  ║
║myhttpstream.http-v1-0│ deploying │     stderr = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/myhttpstream-1542803866800/myhttpstream.http-v1/stderr_0.log║
║                      │           │     stdout = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/myhttpstream-1542803866800/myhttpstream.http-v1/stdout_0.log║
║                      │           │        url = https://192.168.0.102:52143                                                                                                                             ║
║                      │           │working.dir = /var/folders/js/7b_pn0t575l790x7j61slyxc0000gn/T/spring-cloud-deployer-6467595568759190742/myhttpstream-1542803866800/myhttpstream.http-v1             ║
╚══════════════════════╧═══════════╧═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝
```

你应该看到对应的`http`源具有`url`属性，该属性包含它正在监听的主机和端口信息。现在你已经准备好发布到该 URL，如下面的示例所示：

```
dataflow:> http post --target http://localhost:1234 --data "hello"
dataflow:> http post --target http://localhost:1234 --data "goodbye"
```

然后，流将来自`http`源的数据漏斗到由`log`汇实现的输出日志，产生类似于以下内容的输出：

```
2016-06-01 09:50:22.121  INFO 79654 --- [  kafka-binder-] log.sink    : hello
2016-06-01 09:50:26.810  INFO 79654 --- [  kafka-binder-] log.sink    : goodbye
```

我们还可以更改 sink 实现。你可以通过管道将输出发送到文件（`file`）、Hadoop（`hdfs`）或任何其他可用的接收器应用程序。你还可以定义自己的应用程序。

# 流开发人员指南

有关如何在本地计算机上创建、测试和运行 Spring 云流应用程序的更多信息，请参见 microSite 上的[流开发人员指南](https://dataflow.spring.io/docs/stream-developer-guides/)。

# 流监控

有关如何监视作为流的一部分部署的应用程序的更多信息，请参见 microSite 上的[溪流监测指南](https://dataflow.spring.io/docs/feature-guides/streams/monitoring/)。

# 任务

本节将详细介绍如何在 Spring 云数据流上编排[Spring Cloud Task](https://cloud.spring.io/spring-cloud-task/)应用程序。

如果你刚开始使用 Spring 云数据流，那么在深入了解本节之前，你可能应该阅读“[Local](#getting-started-local)”、“[Cloud Foundry](#getting-started-cloudfoundry)”或“[Kubernetes](#getting-started-kubernetes)”的入门指南。

## 25. 导言

任务应用程序是短命的，这意味着它会故意停止运行，并且可以按需运行或在以后进行计划。一种用例可能是抓取网页并写入数据库。

[Spring Cloud Task](https://cloud.spring.io/spring-cloud-task/)框架基于 Spring 引导，并增加了引导应用程序记录短期应用程序的生命周期事件的能力，例如启动时间、结束时间和退出状态。[`TaskExecution`]（https://DOCS. Spring.io/ Spring-cloud-task/DOCS/2.0.0.release/reference/htmlsingle/#features-task-execution-details）文档显示了哪些信息存储在数据库中。在 Spring 云任务应用程序中执行代码的入口点通常是 Boot 的`CommandLineRunner`接口的实现，如下面的[example](https://docs.spring.io/spring-cloud-task/docs/2.0.0.RELEASE/reference/htmlsingle/#getting-started-writing-the-code)所示。

Spring 批处理项目可能是 Spring 编写短期应用程序的开发人员想到的。 Spring 批处理提供了一组比 Spring 云任务丰富得多的功能，并且在处理大量数据时是推荐的。一种用例可能是读取许多 CSV 文件，转换每一行数据，并将每个转换后的行写入数据库。 Spring Batch 提供了它自己的数据库模式，该模式具有关于 Spring 批处理作业的执行的更丰富的[一组信息](https://docs.spring.io/spring-batch/4.1.x/reference/html/schema-appendix.html#metaDataSchema)。 Spring 云任务是与 Spring 批处理集成的，这样，如果 Spring 云任务应用程序定义了 Spring 批处理，则在 Spring 云任务和 Spring 云批处理执行表之间创建链接。

在本地计算机上运行数据流时，任务将在单独的 JVM 中启动。在 Cloud Foundry 上运行时，将使用[Cloud Foundry 的任务](https://docs.cloudfoundry.org/devguide/using-tasks.html)功能启动任务。在 Kubernetes 上运行时，可以使用`Pod`或`Job`资源启动任务。

## 26. 任务的生命周期

在深入研究创建任务的细节之前，你应该了解 Spring 云数据流上下文中任务的典型生命周期：

1. [创建任务应用程序](#spring-cloud-dataflow-create-task-apps)

2. [注册任务应用程序](#spring-cloud-dataflow-register-task-apps)

3. [创建任务定义](#spring-cloud-dataflow-create-task-definition)

4. [启动一项任务](#spring-cloud-dataflow-task-launch)

5. [审查任务执行情况](#spring-cloud-dataflow-task-review-executions)

6. [销毁任务定义](#spring-cloud-dataflow-task-definition-destroying)

7. [持续部署](#spring-cloud-dataflow-task-cd)

### 26.1.创建任务应用程序

Spring 尽管云任务确实提供了许多开箱即用的应用程序（at[spring-cloud-task-app-starters](https://github.com/spring-cloud-task-app-starters)），但大多数任务应用程序都需要定制开发。要创建自定义任务应用程序：

1. 使用[Spring Initializer](https://start.spring.io)创建一个新项目，确保选择以下启动器：

   1. `Cloud Task`:这个依赖关系是`spring-cloud-starter-task`。

   2. `JDBC`:此依赖项是`spring-jdbc`启动器。

   3. 选择你的数据库依赖项：输入数据流当前使用的数据库依赖项。例如：`H2`。

2. 在你的新项目中，创建一个新类作为你的主类，如下所示：

   ```
   @EnableTask
   @SpringBootApplication
   public class MyTask {

       public static void main(String[] args) {
   		SpringApplication.run(MyTask.class, args);
   	}
   }
   ```

3. 在这个类中，你需要在应用程序中实现一个或多个`CommandLineRunner`或`ApplicationRunner`。你既可以实现自己的，也可以使用 Spring Boot 提供的功能（例如，有一个用于运行批处理作业的功能）。

4. 用 Spring boot 将应用程序打包到一个 über JAR 中，是通过标准[Spring Boot conventions](https://docs.spring.io/spring-boot/docs/2.1.1.RELEASE/reference/html/getting-started-first-application.html#getting-started-first-application-executable-jar)完成的。打包的应用程序可以注册和部署，如下所示。

#### 26.1.1.任务数据库配置

|   |在启动任务应用程序时，请确保 Spring 云数据流所使用的数据库驱动程序也是对任务应用程序的依赖关系，例如，如果你的 Spring 云数据流被设置为使用 PostgreSQL，确保任务应用程序也有 PostgreSQL 作为依赖项。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |当你在外部运行任务（即从命令行），并且希望 Spring 云数据流在其 UI 中显示任务执行情况时，请确保两者共享公共数据源设置，<br/>默认情况下， Spring 云任务使用本地 H2 实例，并将执行记录到数据库中所使用的 Spring 云数据流。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 26.2.注册任务应用程序

你可以使用 Spring cloud data flow shell`app register`命令在 app 注册表中注册一个任务应用程序。你必须提供一个唯一的名称和一个可以解析为应用程序工件的 URI。对于类型，请指定`task`。下面的清单展示了三个例子：

```
dataflow:>app register --name task1 --type task --uri maven://com.example:mytask:1.0.2

dataflow:>app register --name task2 --type task --uri file:///Users/example/mytask-1.0.2.jar

dataflow:>app register --name task3 --type task --uri https://example.com/mytask-1.0.2.jar
```

当提供带有`maven`方案的 URI 时，格式应符合以下要求：

```
maven://<groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>
```

如果希望一次注册多个应用程序，可以将它们存储在一个属性文件中，其中键的格式为`<type>.<name>`，值是 URI。例如，下面的列表将是一个有效的属性文件：

```
task.cat=file:///tmp/cat-1.2.1.BUILD-SNAPSHOT.jar
task.hat=file:///tmp/hat-1.2.1.BUILD-SNAPSHOT.jar
```

然后，你可以使用`app import`命令，并通过使用`--uri`选项提供属性文件的位置，如下所示：

```
app import --uri file:///tmp/task-apps.properties
```

例如，如果你希望在一次操作中注册所有附带数据流的任务应用程序，那么你可以使用以下命令进行注册：

```
dataflow:>app import --uri https://dataflow.spring.io/task-maven-latest
```

你还可以通过`--local`选项（默认情况下是`TRUE`）来指示是否应该在 shell 进程本身中解析属性文件位置。如果应该从数据流服务器进程解析位置，请指定`--local false`。

当使用`app register`或`app import`时，如果任务应用程序已经使用提供的名称和版本注册，则默认情况下不会覆盖该应用程序。如果你想用不同的`uri`或`uri-metadata`位置覆盖预先存在的任务应用程序，请包含`--force`选项。

|   |在某些情况下，资源是在服务器端解析的。<br/>在其他情况下，URI 被传递到一个运行时容器实例，在那里它被解析。<br/>有关更多详细信息，请参阅每个数据流服务器的特定文档。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 26.3.创建任务定义

你可以通过提供定义名称以及应用于任务执行的属性，从任务应用程序创建任务定义。你可以通过 RESTful API 或 shell 创建任务定义。要通过使用 shell 创建任务定义，请使用`task create`命令创建任务定义，如下例所示：

```
dataflow:>task create mytask --definition "timestamp --format=\"yyyy\""
Created new task 'mytask'
```

你可以通过 RESTful API 或 shell 获得当前任务定义的列表。要通过使用 shell 获得任务定义列表，请使用`task list`命令。

#### 26.3.1.最大任务定义名称长度

任务定义名称的最大字符长度取决于平台。

|   |有关资源命名的详细信息，请参阅平台文档。<br/>本地平台将任务定义名称存储在最大为 255 的数据库列中。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|Kubernetes 裸荚|Kubernetes Jobs|Cloud Foundry|Local|
|--------------------|---------------|-------------|-----|
|         63         |      52       |     63      | 255 |

#### 26.3.2.自动创建任务定义

从版本 2.3.0 开始，你可以通过将`spring.cloud.dataflow.task.autocreate-task-definitions`设置为`true`来配置数据流服务器以自动创建任务定义。这不是默认的行为，而是作为一种方便而提供的。启用此属性后，任务启动请求可以将已注册的任务应用程序名称指定为任务名称。如果注册了任务应用程序，服务器将创建一个基本的任务定义，该定义仅根据需要指定应用程序名。这消除了类似于以下操作的手动步骤：

```
dataflow:>task create mytask --definition "mytask"
```

你仍然可以为每个任务启动请求指定命令行参数和部署属性。

### 26.4.启动一项任务

可以通过 RESTful API 或 shell 启动一个临时任务。要通过 shell 启动一个特别任务，请使用`task launch`命令，如下面的示例所示：

```
dataflow:>task launch mytask
Launched task 'mytask'
```

当任务启动时，你可以将启动任务时需要作为命令行参数传递给任务应用程序的任何属性设置如下：

```
dataflow:>task launch mytask --arguments "--server.port=8080 --custom=value"
```

|   |参数需要以空格分隔的值传递。|
|---|----------------------------------------------------------|

你可以通过使用`--properties`选项来传递用于`TaskLauncher`本身的附加属性。这个选项的格式是一个逗号分隔的属性字符串，前缀是`app.<task definition name>.<property>`。属性作为应用程序属性传递到`TaskLauncher`。由实现来选择如何将这些信息传递到实际的任务应用程序中。如果该属性的前缀是`deployer`，而不是`app`，则将其作为部署属性传递给`TaskLauncher`，其含义可能是`TaskLauncher`实现特定的。

```
dataflow:>task launch mytask --properties "deployer.timestamp.custom1=value1,app.timestamp.custom2=value2"
```

#### 26.4.1.应用程序属性

每个应用程序都获取属性来定制其行为。例如，`timestamp`Task`format`设置建立了与默认值不同的输出格式。

```
dataflow:> task create --definition "timestamp --format=\"yyyy\"" --name printTimeStamp
```

这个`timestamp`属性实际上与时间戳应用程序指定的`timestamp.format`属性相同。数据流增加了使用简写形式`format`而不是`timestamp.format`的能力。你还可以指定 Longhand 版本，如下面的示例所示：

```
dataflow:> task create --definition "timestamp --timestamp.format=\"yyyy\"" --name printTimeStamp
```

在[流应用程序属性](#spring-cloud-dataflow-application-properties)一节中对这种简写行为进行了更多的讨论。如果有[已注册的应用程序属性元数据](https://dataflow.spring.io/docs/applications/application-metadata/#using-application-metadata)，则可以在输入`--`后在 shell 中使用 tab 补全来获得候选属性名列表。

shell 为应用程序属性提供了制表符补全功能。`app info --name <appName> --type <appType>`shell 命令为所有受支持的属性提供了额外的文档。支持的任务`<appType>`是`task`。

|   |在 Kubernetes 上重新启动 Spring 批处理作业时，必须使用`shell`或`boot`的入口点。|
|---|---------------------------------------------------------------------------------------------------|

##### Kubernetes 上具有敏感信息的应用程序属性

当启动某些属性可能包含敏感信息的任务应用程序时，使用`shell`或`boot`作为`entryPointStyle`。这是因为`exec`（默认）将所有属性转换为命令行参数，因此在某些环境中可能不安全。

#### 26.4.2.通用应用程序属性

Spring 除了通过 DSL 进行配置外，云数据流还提供了一种机制，用于设置由其启动的所有任务应用程序所共有的属性。可以通过在启动服务器时添加带`spring.cloud.dataflow.applicationProperties.task`前缀的属性来实现此目的。然后，服务器将不带前缀的所有属性传递给它启动的实例。

例如，通过使用以下选项启动数据流服务器，可以配置所有启动的应用程序使用`prop1`和`prop2`属性：

```
--spring.cloud.dataflow.applicationProperties.task.prop1=value1
--spring.cloud.dataflow.applicationProperties.task.prop2=value2
```

这会将`prop1=value1`和`prop2=value2`属性传递给所有启动的应用程序。

|   |使用此机制配置的属性的优先级低于任务部署属性。<br/>如果在任务启动时指定了具有相同键的属性（例如，`app.trigger.prop2`重写公共属性），则会重写这些属性。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 26.5.限制并发任务启动的数量

Spring 云数据流允许用户限制每个配置平台的并发运行任务的最大数量，以防止 IaaS 或硬件资源的饱和。默认情况下，对于所有受支持的平台，限制设置为`20`。如果平台实例上并发运行的任务数量大于或等于限制，则下一个任务启动请求失败，并通过 RESTful API、shell 或 UI 返回错误消息。可以通过设置相应的部署人员属性`spring.cloud.dataflow.task.platform.<platform-type>.accounts[<account-name>].maximumConcurrentTasks`为平台实例配置此限制，其中`<account-name>`是已配置平台帐户的名称（如果没有显式配置帐户，`default`）。`<platform-type>`是指当前支持的部署程序之一：`local`或`kubernetes`。对于`cloudfoundry`，该属性是`spring.cloud.dataflow.task.platform.<platform-type>.accounts[<account-name>].deployment.maximumConcurrentTasks`。（区别在于`deployment`已被添加到路径中）。

如果可能的话，每个受支持平台的`TaskLauncher`实现通过查询底层平台的运行时状态来确定当前正在运行的任务的数量。用于识别`task`的方法因平台的不同而不同。例如，在本地主机上启动任务使用`LocalTaskLauncher`。`LocalTaskLauncher`为每个启动请求运行一个进程，并在内存中跟踪这些进程。在这种情况下，我们不查询底层 OS，因为以这种方式识别任务是不切实际的。对于 Cloud Foundry，任务是其部署模型支持的核心概念。（所有任务的状态）可直接通过 API 获得。这意味着帐户的组织和空间中的每个正在运行的任务容器都包含在正在运行的执行计数中，无论它是否通过使用 Spring 云数据流或直接调用`CloudFoundryTaskLauncher`来启动。对于 Kubernetes 来说，通过`KubernetesTaskLauncher`启动一个任务，如果成功，将导致一个正在运行的 pod，我们希望它最终完成或失败。在这种环境下，通常没有简单的方法来识别与任务相对应的 pod。因此，我们只计算`KubernetesTaskLauncher`发射的吊舱。由于 Task Launcher 在 POD 的元数据中提供了`task-name`标签，因此我们通过该标签的存在来过滤所有运行中的 POD。

### 26.6.审查任务执行情况

一旦任务启动，任务的状态就存储在关系数据库中。国家包括：

* 任务名称

* 开始时间

* 结束时间

* 退出代码

* 退出消息

* 上次更新时间

* 参数

你可以通过 RESTful API 或 shell 检查任务执行的状态。要通过 shell 显示最新的任务执行情况，请使用`task execution list`命令。

要获得只针对一个任务定义的任务执行列表，请添加`--name`和任务定义名称——例如，`task execution list --name foo`。要获取任务执行的全部详细信息，请使用带有任务执行 ID 的`task execution status`命令，例如`task execution status --id 549`。

### 26.7.销毁任务定义

销毁任务定义会从定义库中删除该定义。这可以通过 RESTful API 或 shell 来完成。要通过 shell 销毁任务，请使用`task destroy`命令，如以下示例所示：

```
dataflow:>task destroy mytask
Destroyed task 'mytask'
```

`task destroy`命令还具有`cleanup`被销毁任务的任务执行的选项，如以下示例所示：

```
dataflow:>task destroy mytask --cleanup
Destroyed task 'mytask'
```

默认情况下，`cleanup`选项被设置为`false`（也就是说，默认情况下，任务执行不会在任务被销毁时被清理）。

要通过 shell 销毁所有任务，请使用`task all destroy`命令，如下例所示：

```
dataflow:>task all destroy
Really destroy all tasks? [y, n]: y
All tasks destroyed
```

如果需要，你可以使用力开关：

```
dataflow:>task all destroy --force
All tasks destroyed
```

定义中先前启动的任务的任务执行信息保留在任务存储库中。

|   |这不会停止此定义的任何当前正在运行的任务。相反，它将从数据库中删除任务定义。|
|---|------------------------------------------------------------------------------------------------------------------------------|

|   |`task destroy <task-name>`只删除定义，而不删除部署在 Cloud Foundry 上的任务。<br/>完成删除任务的唯一方法是通过 CLI 分两步：<br/><br/>\+<br/>。使用`cf apps`命令获取应用程序列表。<br/>。确定要删除的任务应用程序并运行`cf delete <task-name>`命令。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 26.8.验证任务

有时，包含在任务定义中的应用程序在其注册中具有无效的 URI。这可能是由于在应用程序注册时输入了无效的 URI，或者应用程序从要从中提取它的存储库中删除而导致的。要验证任务中包含的所有应用程序都是可解析的，请使用`validate`命令，如下所示：

```
dataflow:>task validate time-stamp
╔══════════╤═══════════════╗
║Task Name │Task Definition║
╠══════════╪═══════════════╣
║time-stamp│timestamp      ║
╚══════════╧═══════════════╝

time-stamp is a valid task.
╔═══════════════╤═════════════════╗
║   App Name    │Validation Status║
╠═══════════════╪═════════════════╣
║task:timestamp │valid            ║
╚═══════════════╧═════════════════╝
```

在前面的示例中，用户验证了他们的时间戳任务。`task:timestamp`应用程序是有效的。现在我们可以看到，如果我们有一个流定义，而注册的应用程序具有无效的 URI，会发生什么情况：

```
dataflow:>task validate bad-timestamp
╔═════════════╤═══════════════╗
║  Task Name  │Task Definition║
╠═════════════╪═══════════════╣
║bad-timestamp│badtimestamp   ║
╚═════════════╧═══════════════╝

bad-timestamp is an invalid task.
╔══════════════════╤═════════════════╗
║     App Name     │Validation Status║
╠══════════════════╪═════════════════╣
║task:badtimestamp │invalid          ║
╚══════════════════╧═════════════════╝
```

在这种情况下， Spring 云数据流声明该任务是无效的，因为`task:badtimestamp`具有无效的 URI。

### 26.9.停止任务执行

在某些情况下，在平台上运行的任务可能不会因为平台或应用程序业务逻辑本身的问题而停止。 Spring 对于这样的情况，云数据流提供了向平台发送请求以结束任务的能力。要做到这一点，请为给定的一组任务执行提交`task execution stop`，如下所示：

```
task execution stop --ids 5

Request to stop the task execution with id(s): 5 has been submitted
```

使用前面的命令，将停止执行`id=5`的触发器提交给底层部署程序实现。结果，操作将停止该任务。当我们查看任务执行的结果时，我们看到任务执行是用 0 退出代码完成的：

```
dataflow:>task execution list
╔══════════╤══╤════════════════════════════╤════════════════════════════╤═════════╗
║Task Name │ID│         Start Time         │          End Time          │Exit Code║
╠══════════╪══╪════════════════════════════╪════════════════════════════╪═════════╣
║batch-demo│5 │Mon Jul 15 13:58:41 EDT 2019│Mon Jul 15 13:58:55 EDT 2019│0        ║
║timestamp │1 │Mon Jul 15 09:26:41 EDT 2019│Mon Jul 15 09:26:41 EDT 2019│0        ║
╚══════════╧══╧════════════════════════════╧════════════════════════════╧═════════╝
```

如果你为一个任务执行提交了一个停止，而该任务执行中有与之相关联的子任务执行，例如一个组合任务，那么将为每个子任务执行发送一个停止请求。

|   |当停止具有正在运行的 Spring 批处理作业的任务执行时，该作业的批处理状态为`STARTED`。<br/>当请求停止时，每个受支持的平台都向任务应用程序发送一个 SIG-INT。这使得 Spring Cloud Task 能够捕获应用程序的状态。然而， Spring 批处理不处理 SIG-INT，因此，作业停止，但仍处于开始状态。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |在启动远程分区 Spring 批处理任务应用程序时， Spring 云数据流支持为 Cloud Foundry 和 Kubernetes 平台直接停止工作分区任务。本地平台不支持停止工作分区任务。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 26.9.1.停止在 Spring 云数据流之外启动的任务执行

你可能希望停止已在 Spring 云数据流之外启动的任务。这方面的一个例子是由远程批处理分区应用程序启动的工作应用程序。在这种情况下，远程批处理分区应用程序为每个工作者应用程序存储`external-execution-id`。但是，没有存储平台信息。因此，当 Spring 云数据流必须停止远程批处理分区的应用程序及其工作应用程序时，需要指定平台名称，如下所示：

```
dataflow:>task execution stop --ids 1 --platform myplatform
Request to stop the task execution with id(s): 1 for platform myplatform has been submitted
```

## 27. 订阅任务和批处理事件

你还可以在任务启动时利用各种任务和批处理事件。如果任务被启用来生成任务或批处理事件（带有`spring-cloud-task-stream`的附加依赖关系，并且在以 Kafka 为绑定器的情况下，`spring-cloud-stream-binder-kafka`），则这些事件将在任务生命周期期间发布。默认情况下，代理上发布的事件（Rabbit、Kafka 和其他事件）的目标名称是事件名称本身（例如：`task-events`，`job-execution-events`，等等）。

```
dataflow:>task create myTask --definition "myBatchJob"
dataflow:>stream create task-event-subscriber1 --definition ":task-events > log" --deploy
dataflow:>task launch myTask
```

通过在启动任务时指定显式名称，你可以控制这些事件的目标名称，如下所示：

```
dataflow:>stream create task-event-subscriber2 --definition ":myTaskEvents > log" --deploy
dataflow:>task launch myTask --properties "app.myBatchJob.spring.cloud.stream.bindings.task-events.destination=myTaskEvents"
```

下表列出了代理上的默认任务、批处理事件和目标名称：

|      **Event**      |**目的地**|
|---------------------|-----------------------|
|     Task events     |`task-events`|
|Job Execution events |`job-execution-events`|
|Step Execution events|`step-execution-events`|
|  Item Read events   |`item-read-events`|
| Item Process events |`item-process-events`|
|  Item Write events  |`item-write-events`|
|     Skip events     |`skip-events`|

## 28. 组合任务

Spring 云数据流允许你创建有向图，其中图的每个节点都是任务应用程序。这是通过对组合任务使用 DSL 来完成的。你可以通过 RESTful API、 Spring Cloud Data Flow Shell 或 Spring Cloud Data Flow UI 创建一个组合任务。

### 28.1.组成的任务运行器

复合任务通过一个名为复合任务运行器的任务应用程序运行。 Spring 云数据流服务器在启动合成任务时自动部署合成任务运行器。

#### 28.1.1.配置组合任务运行器

Composed Task Runner 应用程序有一个`dataflow-server-uri`属性，用于验证和启动子任务。这默认为`[localhost:9393](http://localhost:9393)`。如果运行分布式 Spring 云数据流服务器，就像在 Cloud Foundry 或 Kubernetes 上部署该服务器一样，你需要提供可用于访问该服务器的 URI。你可以通过在启动组合任务时为组合任务运行器应用程序设置`dataflow-server-uri`属性，或者在启动 Spring 云数据流服务器时为其设置`spring.cloud.dataflow.server.uri`属性来提供此功能。对于后一种情况，`dataflow-server-uri`Composed Task Runner 应用程序属性将在启动 Composed 任务时自动设置。

##### 配置选项

`ComposedTaskRunner`任务有以下选项：

* `composed-task-arguments`用于每个任务的命令行参数。（字符串，缺省：\<none\>）。

* `increment-instance-enabled`允许一个`ComposedTaskRunner`实例在不更改参数的情况下再次运行，方法是根据前一次执行中的`run.id`添加一个递增的数 job 参数。（布尔值，默认值：`true`）。ComposedTaskRunner 是使用[Spring Batch](https://github.com/spring-projects/spring-batch)构建的。因此，一旦成功执行，该批处理作业就被视为已完成。要多次启动相同的`ComposedTaskRunner`定义，你必须将`increment-instance-enabled`或`uuid-instance-enabled`属性设置为`true`，或者更改每次启动定义的参数。当使用此选项时，必须将其应用于所需应用程序的所有任务启动，包括第一次启动。

* `uuid-instance-enabled`通过向`ctr.id`作业参数添加 UUID，允许在不更改参数的情况下再次运行单个`ComposedTaskRunner`实例。（布尔值，默认值：`false`）。ComposedTaskRunner 是使用[Spring Batch](https://github.com/spring-projects/spring-batch)构建的。因此，一旦成功执行，该批处理作业就被视为已完成。要多次启动相同的`ComposedTaskRunner`定义，必须将`increment-instance-enabled`或`uuid-instance-enabled`属性设置为`true`，或者更改每次启动定义的参数。当使用此选项时，必须将其应用于所需应用程序的所有任务启动，包括第一次启动。当设置为 true 时，此选项将覆盖`increment-instance-id`的值。当同时运行同一组合任务定义的多个实例时，将此选项设置为`true`。

* `interval-time-between-checks``ComposedTaskRunner`在检查数据库以查看任务是否完成之间等待的时间，以毫秒为单位。（整数，缺省：`10000`）。`ComposedTaskRunner`使用数据存储来确定每个子任务的状态。这个间隔指示`ComposedTaskRunner`它应该检查其子任务状态的频率。

* `transaction-isolation-level`为组合任务运行器建立事务隔离级别。可以找到[here](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/TransactionDefinition.html)的可用事务隔离级别列表。默认值为`ISOLATION_REPEATABLE_READ`。

* `max-wait-time`在组合任务执行失败之前，单个步骤可以运行的最大时间（以毫秒为单位）（整数，缺省：0）。确定在 CTR 以失败结束之前允许每个子任务运行的最长时间。`0`的默认值表示没有超时。

* `split-thread-allow-core-thread-timeout`指定是否允许拆分核心线程超时。（Boolean，默认值：`false`）设置了管理核心线程是否超时的策略，如果在 Keep-Alive 时间内没有任务到达就会终止，如果新任务到达时需要替换核心线程。

* `split-thread-core-pool-size`Split 的核心池大小。（整数，缺省：`1`）拆分中包含的每个子任务都需要一个线程才能执行。因此，例如，像`<AAA || BBB || CCC> && <DDD || EEE>`这样的定义将需要`split-thread-core-pool-size`的`3`。这是因为最大的分割包含三个子任务。如果计算`2`，则意味着`AAA`和`BBB`将并行运行，但 CCC 将等待`AAA`或`BBB`完成才能运行。然后`DDD`和`EEE`将并行运行。

* `split-thread-keep-alive-seconds`Split 的线程保持活秒。（整数，缺省：`60`）如果池当前有超过`corePoolSize`的线程，如果多余的线程空闲时间超过`keepAliveTime`，则停止这些线程。

* `split-thread-max-pool-size`Split 的最大池大小。（整数，缺省：`Integer.MAX_VALUE`）。建立线程池允许的最大线程数.

* **分割线程队列容量**Split 的容量`BlockingQueue`。（整数，缺省：`Integer.MAX_VALUE`）

  * 如果运行的线程少于`corePoolSize`，则`Executor`总是更喜欢添加新线程，而不是排队。

  * 如果`corePoolSize`或更多线程正在运行，则`Executor`总是更喜欢对请求进行排队，而不是添加新线程。

  * 如果一个请求不能排队，则创建一个新线程，除非它超过`maximumPoolSize`。在这种情况下，任务将被拒绝。

* `split-thread-wait-for-tasks-to-complete-on-shutdown`是否在关机时等待计划任务完成，不中断运行任务并运行队列中的所有任务。（布尔值，默认值：`false`）

* `dataflow-server-uri`接收任务启动请求的数据流服务器的 URI。（字符串，默认值：`[localhost:9393](http://localhost:9393)`）

* `dataflow-server-username`接收任务启动请求的数据流服务器的可选用户名。用于通过使用基本身份验证访问数据流服务器。如果设置`dataflow-server-access-token`，则不使用。

* `dataflow-server-password`接收任务启动请求的数据流服务器的可选密码。用于通过使用基本身份验证访问数据流服务器。如果设置`dataflow-server-access-token`，则不使用。

* `dataflow-server-access-token`此属性设置一个可选的 OAuth2 访问令牌。通常，如果可用，则通过使用来自当前登录用户的令牌自动设置该值。但是，对于特殊的用例，也可以显式地设置该值。

当你想要使用当前登录用户的访问令牌并将其传播给组合的任务运行器时，存在一个特殊的布尔属性`dataflow-server-use-user-access-token`。 Spring 云数据流使用此属性，如果设置为`true`，则自动填充`dataflow-server-access-token`属性。当使用`dataflow-server-use-user-access-token`时，必须为每个任务执行传递它。在某些情况下，在默认情况下，用户的`dataflow-server-access-token`必须为每个组合任务启动传递。在这种情况下，将 Spring 云数据流`spring.cloud.dataflow.task.useUserAccessToken`属性设置为`true`。

要为组合任务运行器设置属性，你需要在该属性前加上`app.composed-task-runner.`。例如，要设置`dataflow-server-uri`属性，该属性将看起来像`app.composed-task-runner.dataflow-server-uri`。

### 28.2.组合任务的生命周期

组合任务的生命周期由三部分组成：

* [创建一个完整的任务](#spring-cloud-data-flow-composed-task-creating)

* [停止一项复杂的任务](#spring-cloud-data-flow-composed-task-stopping)

* [重新启动一个组合任务](#spring-cloud-data-flow-composed-task-restarting)

#### 28.2.1.创建一个完整的任务

在通过 Task Create 命令创建任务定义时，将使用组合任务的 DSL，如以下示例所示：

```
dataflow:> app register --name timestamp --type task --uri maven://org.springframework.cloud.task.app:timestamp-task:
dataflow:> app register --name mytaskapp --type task --uri file:///home/tasks/mytask.jar
dataflow:> task create my-composed-task --definition "mytaskapp && timestamp"
dataflow:> task launch my-composed-task
```

在前面的示例中，我们假设组合任务使用的应用程序尚未注册。因此，在前两个步骤中，我们注册了两个任务应用程序。然后，我们使用`task create`命令创建组合任务定义。前面示例中的组合任务 DSL 在启动时运行`mytaskapp`，然后运行时间戳应用程序。

但是在我们推出`my-composed-task`定义之前，我们可以查看 Spring 云数据流为我们生成了什么。这可以通过使用 Task List 命令来完成，如下例所示（包括其输出）：

```
dataflow:>task list
╔══════════════════════════╤══════════════════════╤═══════════╗
║        Task Name         │   Task Definition    │Task Status║
╠══════════════════════════╪══════════════════════╪═══════════╣
║my-composed-task          │mytaskapp && timestamp│unknown    ║
║my-composed-task-mytaskapp│mytaskapp             │unknown    ║
║my-composed-task-timestamp│timestamp             │unknown    ║
╚══════════════════════════╧══════════════════════╧═══════════╝
```

Spring 在该示例中，云数据流创建了三个任务定义，其中一个是针对组成我们的组合任务的应用程序（`my-composed-task-mytaskapp`和`my-composed-task-timestamp`）以及组成任务的定义（`my-composed-task`）。我们还看到，为子任务生成的每个名称都由组合任务的名称和应用程序的名称组成，用连字符`-`分隔（如*我的-沉着的-任务*`-`*MyTaskApp*）。

##### 任务应用程序参数

组成组合任务定义的任务应用程序也可以包含参数，如以下示例所示：

```
dataflow:> task create my-composed-task --definition "mytaskapp --displayMessage=hello && timestamp --format=YYYY"
```

#### 28.2.2.展开一项沉着的任务

发起一项组合任务的方式与发起一项独立任务的方式相同，如下所示：

```
task launch my-composed-task
```

一旦任务启动，并且假设所有任务都成功完成，当你运行`task execution list`时，你可以看到三个任务执行，如以下示例所示：

```
dataflow:>task execution list
╔══════════════════════════╤═══╤════════════════════════════╤════════════════════════════╤═════════╗
║        Task Name         │ID │         Start Time         │          End Time          │Exit Code║
╠══════════════════════════╪═══╪════════════════════════════╪════════════════════════════╪═════════╣
║my-composed-task-timestamp│713│Wed Apr 12 16:43:07 EDT 2017│Wed Apr 12 16:43:07 EDT 2017│0        ║
║my-composed-task-mytaskapp│712│Wed Apr 12 16:42:57 EDT 2017│Wed Apr 12 16:42:57 EDT 2017│0        ║
║my-composed-task          │711│Wed Apr 12 16:42:55 EDT 2017│Wed Apr 12 16:43:15 EDT 2017│0        ║
╚══════════════════════════╧═══╧════════════════════════════╧════════════════════════════╧═════════╝
```

在前面的示例中，我们看到`my-compose-task`启动了，并且其他任务也是按顺序启动的。它们都以`Exit Code`为`0`成功运行。

##### 将属性传递给子任务

要在任务启动时在组合任务图中设置子任务的属性，请使用以下格式：`app.<composed task definition name>.<child task app name>.<property>`。下面的清单显示了一个组合任务定义作为示例：

```
dataflow:> task create my-composed-task --definition "mytaskapp  && mytimestamp"
```

要使`mytaskapp`显示“hello”并将`mytimestamp`时间戳格式设置为`YYYY`用于组合任务定义，请使用以下任务启动格式：

```
task launch my-composed-task --properties "app.my-composed-task.mytaskapp.displayMessage=HELLO,app.my-composed-task.mytimestamp.timestamp.format=YYYY"
```

与应用程序属性类似，你也可以使用以下格式为子任务设置`deployer`属性：

```
task launch my-composed-task --properties "deployer.my-composed-task.mytaskapp.memory=2048m,app.my-composed-task.mytimestamp.timestamp.format=HH:mm:ss"
Launched task 'a1'
```

##### 将参数传递给组成的任务运行器

你可以使用`--arguments`选项为组合任务运行器传递命令行参数：

```
dataflow:>task create my-composed-task --definition "<aaa: timestamp || bbb: timestamp>"
Created new task 'my-composed-task'

dataflow:>task launch my-composed-task --arguments "--increment-instance-enabled=true --max-wait-time=50000 --split-thread-core-pool-size=4" --properties "app.my-composed-task.bbb.timestamp.format=dd/MM/yyyy HH:mm:ss"
Launched task 'my-composed-task'
```

##### 退出状态

下面的列表显示了在每个步骤执行之后，如何为组合任务中包含的每个步骤（任务）设置退出状态：

* 如果`TaskExecution`具有`ExitMessage`，则将其用作`ExitStatus`。

* 如果不存在`ExitMessage`，并且`ExitCode`设置为零，则该步骤的`ExitStatus`为`COMPLETED`。

* 如果不存在`ExitMessage`，并且`ExitCode`被设置为任何非零数，则该步骤的`ExitStatus`是`FAILED`。

#### 28.2.3.摧毁一项沉着的任务

用于销毁独立任务的命令与用于销毁组合任务的命令相同。唯一的区别是，破坏一个组合任务也会破坏与之相关的子任务。下面的示例显示了使用`destroy`命令之前和之后的任务列表：

```
dataflow:>task list
╔══════════════════════════╤══════════════════════╤═══════════╗
║        Task Name         │   Task Definition    │Task Status║
╠══════════════════════════╪══════════════════════╪═══════════╣
║my-composed-task          │mytaskapp && timestamp│COMPLETED  ║
║my-composed-task-mytaskapp│mytaskapp             │COMPLETED  ║
║my-composed-task-timestamp│timestamp             │COMPLETED  ║
╚══════════════════════════╧══════════════════════╧═══════════╝
...
dataflow:>task destroy my-composed-task
dataflow:>task list
╔═════════╤═══════════════╤═══════════╗
║Task Name│Task Definition│Task Status║
╚═════════╧═══════════════╧═══════════╝
```

#### 28.2.4.停止一项复杂的任务

在需要停止组合任务执行的情况下，你可以通过以下方式完成此操作：

* RESTful API

* Spring 云数据流仪表板

要通过仪表板停止组合任务，请选择**工作**选项卡，并单击要停止的作业执行旁边的 \*stop（）按钮。

当当前运行的子任务完成时，将停止组合任务运行。与停止组合任务时正在运行的子任务相关联的步骤标记为`STOPPED`，以及组合任务作业执行。

#### 28.2.5.重新启动一个组合任务

如果组合任务在执行过程中失败，并且组合任务的状态`FAILED`，则可以重新启动该任务。你可以通过以下方式实现此目的：

* RESTful API

* 贝壳

* Spring 云数据流仪表板

要通过 shell 重新启动组合任务，请使用相同的参数启动该任务。要通过仪表板重新启动组合任务，请选择**工作**选项卡，并单击要重新启动的作业执行旁边的**重新启动**按钮。

|   |重新启动已停止（通过 Spring Cloud Data Flow 仪表板或 RESTful API）的组合任务作业，重新启动`STOPPED`子任务，然后按照指定的顺序启动剩余的（未启动的）子任务。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## 29. 组合任务 DSL

组合任务可以通过三种方式运行：

* [有条件执行](#spring-cloud-data-flow-conditional-execution)

* [过渡性执行](#spring-cloud-data-flow-transitional-execution)

* [分割执行](#spring-cloud-data-flow-split-execution)

### 29.1.有条件执行

条件执行是通过使用双符号表示的（`&&`）。这使得序列中的每个任务只有在前一个任务成功完成时才能启动，如以下示例所示：

```
task create my-composed-task --definition "task1 && task2"
```

当发起名为`my-composed-task`的组合任务时，它启动名为`task1`的任务，如果`task1`成功完成，则启动名为`task2`的任务。如果`task1`失败，则`task2`不启动。

你还可以使用 Spring 云数据流仪表板来创建你的条件执行，方法是使用设计器拖放所需的应用程序，并将它们连接在一起以创建你的有向图，如下图所示：

![组合任务条件执行](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-conditional-execution.png)

图 2.有条件执行

前面的图是使用 Spring 云数据流仪表板创建的有向图的屏幕截图。你可以看到图中的四个组件组成了一个条件执行：

* **开始**图标：所有有向图都从这个符号开始。只有一个。

* **任务**图标：表示有向图中的每个任务。

* **结束**图标：表示有向图的结束。

* 实线箭头：表示以下之间的流条件执行流：

  * 两个应用程序。

  * 启动控制节点和应用程序.

  * 一个应用程序和终端控制节点.

* **结束**图标：所有有向图都以这个符号结尾。

|   |通过单击“定义”选项卡上的“组合任务定义”旁边的**细节**按钮，可以查看有向图的图表。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------|

### 29.2.过渡性执行

DSL 支持对有向图执行过程中的转换进行细粒度控制。转换是通过提供一个基于前一个任务的退出状态的相等条件来指定的。任务转换由以下符号`->`表示。

#### 29.2.1.基本过渡

一个基本的过渡将如下所示：

```
task create my-transition-composed-task --definition "foo 'FAILED' -> bar 'COMPLETED' -> baz"
```

在前面的示例中，`foo`将启动，并且，如果退出状态为`FAILED`，则`bar`任务将启动。如果`foo`的退出状态是`COMPLETED`，则`baz`将启动。由`cat`返回的所有其他状态都没有影响，任务将正常结束。

使用 Spring 云数据流仪表板创建相同的“基本转换”将类似于以下图像：

![组合任务基本转换](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-transition-basic.png)

图 3.基本过渡

前面的图表是在 Spring 云数据流仪表板中创建的有向图的屏幕截图。请注意，有两种不同类型的连接器：

* 虚线：表示从应用程序到可能的目标应用程序之一的转换。

* 实线：在条件执行中连接应用程序，或在应用程序和控制节点（开始或结束）之间连接应用程序。

要创建过渡连接器：

1. 在创建转换时，使用连接器将应用程序链接到每个可能的目标。

2. 完成后，转到每个连接，并通过单击它来选择它。

3. 一个螺栓图标出现。

4. 点击那个图标。

5. 输入该连接器所需的退出状态。

6. 该连接器的实线变为虚线。

#### 29.2.2.使用通配符的转换

对于 DSL 的转换，通配符是支持的，如下例所示：

```
task create my-transition-composed-task --definition "foo 'FAILED' -> bar '*' -> baz"
```

在前面的示例中，`foo`将启动，并且，如果退出状态为`FAILED`，则`bar`任务将启动。对于除`FAILED`以外的`cat`的任何退出状态，`baz`都将发射。

使用 Spring 云数据流仪表板创建相同的“通配符转换”将类似于以下图像：

![带有通配符的组合任务基本转换](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-transition-basic-wildcard.png)

图 4.带有通配符的基本转换

#### 29.2.3.使用以下条件执行的转换

只要不使用通配符，就可以在转换之后执行条件执行，如下例所示：

```
task create my-transition-conditional-execution-task --definition "foo 'FAILED' -> bar 'UNKNOWN' -> baz && qux && quux"
```

在前面的示例中，`foo`将启动，并且，如果它的退出状态为`FAILED`，则`bar`任务将启动。如果`foo`的退出状态为`UNKNOWN`，则将启动`baz`。对于除`foo`或`UNKNOWN`以外的`foo`的任何退出状态，将启动`qux`，成功完成后，将启动`quux`。

使用 Spring 云数据流仪表板创建相同的“带条件执行的转换”将类似于以下图像：

![有条件执行的组合任务转换](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-transition-conditional-execution.png)

图 5.带条件执行的转换

|   |在这个图中，虚线将`foo`应用程序与目标应用程序连接起来，但是一条实线将`foo`、`qux`和`quux`之间的条件执行连接起来。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 29.3.分割执行

拆分让一个组合任务中的多个任务并行运行。它是通过使用角括号（`<>`）对要并行运行的任务和流进行分组来表示的。这些任务和流由双管道`||`符号分隔，如以下示例所示：

```
task create my-split-task --definition "<foo || bar || baz>"
```

前面的示例并行地启动任务`foo`、`bar`和`baz`。

使用 Spring 云数据流仪表板创建相同的“分割执行”将类似于以下图像：

![组合任务拆分](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-split.png)

图 6.分裂

使用 Task DSL，你还可以连续运行多个分割组，如以下示例所示：

```
task create my-split-task --definition "<foo || bar || baz> && <qux || quux>"
```

在前面的示例中，`foo`、`bar`和`baz`任务是并行启动的。一旦它们全部完成，`qux`和`quux`任务将并行启动。一旦他们完成了，这项稳定的任务就结束了。但是，如果`foo`、`bar`或`baz`失败，则包含`qux`和`quux`的拆分不会启动。

使用 Spring 云数据流仪表板创建相同的“多组分割”将类似于以下图像：

![组合任务拆分](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-multiple-splits.png)

图 7.作为有条件执行的一部分拆分

请注意，在连接两个连续的拆分时，设计器会插入一个`SYNC`控制节点。

|   |拆分中使用的任务不应设置其`ExitMessage`。将`ExitMessage`设置为<br/>仅用于[过渡](#spring-cloud-data-flow-transitional-execution)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### 29.3.1.包含条件执行的拆分

拆分也可以在斜括号内有条件执行，如以下示例所示：

```
task create my-split-task --definition "<foo && bar || baz>"
```

在前面的示例中，我们看到`foo`和`baz`是并行发射的。但是，`bar`在`foo`成功完成之前不会启动。

使用 Spring 云数据流仪表板创建相同的“`split containing conditional execution`”类似于以下图像：

![有条件执行的组合任务分割](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-split-contains-conditional.png)

图 8.用条件执行拆分

#### 29.3.2.为拆分建立适当的线程计数

拆分中包含的每个子任务都需要一个线程才能运行。要正确地设置这一点，你需要查看你的图表，并找到子任务数量最多的分割。在此拆分中，子任务的数量是你需要的线程数量。要设置线程计数，请使用`split-thread-core-pool-size property`（默认为`1`）。因此，例如，像`<AAA || BBB || CCC> && <DDD || EEE>`这样的定义需要`split-thread-core-pool-size`的`3`。这是因为最大的分割包含三个子任务。计数为 2 意味着`AAA`和`BBB`将并行运行，但 CCC 将等待`AAA`或`BBB`中的任何一个完成才能运行。然后`DDD`和`EEE`将并行运行。

## 30. 从流启动任务

你可以通过使用[`task-launcher-dataflow`]（https://github.com/ Spring-cloud-stream-app-starters/tasklauncher-dataflow/blob/master/ Spring-cloud-starter-stream-sink-task-launcher-dataflow/readme.ADOC）接收器从流启动任务。接收器连接到数据流服务器，并使用其 REST API 来启动任何已定义的任务。接收器接受表示[JSON 有效载荷](https://github.com/spring-cloud-stream-app-starters/tasklauncher-dataflow/blob/master/spring-cloud-starter-stream-sink-task-launcher-dataflow/README.adoc#payload)的`task launch request`，它提供要启动的任务的名称，并可能包括命令行参数和部署属性。

[`app-starters-task-launch-request-common`]（https://github.com/ Spring-cloud-stream-app-starters/core/blob/master/common/app-starters-task-launch-request-common/readme.ADOC）组件，结合 Spring cloud stream[功能组合](https://docs.spring.io/spring-cloud-stream/docs/current-snapshot/reference/htmlsingle/#_functional_composition)，可以将任何源或处理器的输出转换为任务启动请求。

向`app-starters-task-launch-request-common`添加依赖项可自动配置`java.util.function.Function`实现，该实现通过[Spring Cloud Function](https://cloud.spring.io/spring-cloud-function/)注册为`task发射Request`。

例如，你可以从[time](https://github.com/spring-cloud-stream-app-starters/time/tree/master/spring-cloud-starter-stream-source-time)源开始，添加以下依赖项，构建它，并将其注册为自定义源。在这个示例中，我们将其称为`time-tlr`:

```
<dependency>
    <groupId>org.springframework.cloud.stream.app</groupId>
    <artifactId>app-starters-task-launch-request-common</artifactId>
</dependency>
```

|   |[Spring Cloud Stream Initializr](https://start-scs.cfapps.io/)为创建流应用程序提供了一个很好的起点。|
|---|--------------------------------------------------------------------------------------------------------------------------------|

接下来，[register](#applications)接收`task-launcher-dataflow`并创建一个任务（我们使用提供的时间戳任务）：

```
stream create --name task-every-minute --definition "time-tlr --trigger.fixed-delay=60 --spring.cloud.stream.function.definition=taskLaunchRequest --task.launch.request.task-name=timestamp-task | task-launcher-dataflow" --deploy
```

前面的流每分钟产生一个任务启动请求。请求提供要启动的任务的名称：`{"name":"timestamp-task"}`。

下面的流定义演示了命令行参数的使用。它生成诸如`{"args":["foo=bar","time=12/03/18 17:44:12"],"deploymentProps":{},"name":"timestamp-task"}`之类的消息，以便为任务提供命令行参数：

```
stream create --name task-every-second --definition "time-tlr --spring.cloud.stream.function.definition=taskLaunchRequest --task.launch.request.task-name=timestamp-task --task.launch.request.args=foo=bar --task.launch.request.arg-expressions=time=payload | task-launcher-dataflow" --deploy
```

请注意使用 SPEL 表达式将每个消息有效负载映射到`time`命令行参数，以及一个静态参数（`foo=bar`）。

然后，你可以使用 shell 命令`task execution list`查看任务执行列表，如下例所示（及其输出）：

```
dataflow:>task execution list
╔════════════════════╤══╤════════════════════════════╤════════════════════════════╤═════════╗
║     Task Name      │ID│         Start Time         │          End Time          │Exit Code║
╠════════════════════╪══╪════════════════════════════╪════════════════════════════╪═════════╣
║timestamp-task_26176│4 │Tue May 02 12:13:49 EDT 2017│Tue May 02 12:13:49 EDT 2017│0        ║
║timestamp-task_32996│3 │Tue May 02 12:12:49 EDT 2017│Tue May 02 12:12:49 EDT 2017│0        ║
║timestamp-task_58971│2 │Tue May 02 12:11:50 EDT 2017│Tue May 02 12:11:50 EDT 2017│0        ║
║timestamp-task_13467│1 │Tue May 02 12:10:50 EDT 2017│Tue May 02 12:10:50 EDT 2017│0        ║
╚════════════════════╧══╧════════════════════════════╧════════════════════════════╧═════════╝
```

在这个示例中，我们展示了如何使用`time`源以固定的速率启动任务。此模式可应用于任何源，以启动响应任何事件的任务。

### 30.1.从流中发起一项复杂的任务

可以使用`task-launcher-dataflow`接收器启动组合任务，如讨论的[here](#spring-cloud-dataflow-launch-tasks-from-stream)。由于我们直接使用`ComposedTaskRunner`，因此在创建组合任务启动流之前，我们需要为组合任务运行器本身以及组合任务设置任务定义。假设我们想要创建以下复合任务定义：`AAA && BBB`。第一步是创建任务定义，如下例所示：

```
task create composed-task-runner --definition "composed-task-runner"
task create AAA --definition "timestamp"
task create BBB --definition "timestamp"
```

|   |可以找到`ComposedTaskRunner`的版本[here](https://github.com/spring-cloud-task-app-starters/composed-task-runner/releases)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------|

现在，我们为组合任务定义所需的任务定义已经准备好了，我们需要创建一个启动`ComposedTaskRunner`的流。因此，在这种情况下，我们创建一个流，使用：

* `time`源自定义以发出任务启动请求，如图[earlier](#spring-cloud-dataflow-launch-tasks-from-stream)所示。

* 启动`task-launcher-dataflow`的`ComposedTaskRunner`接收器

该流应类似于以下内容：

```
stream create ctr-stream --definition "time --fixed-delay=30 --task.launch.request.task-name=composed-task-launcher --task.launch.request.args=--graph=AAA&&BBB,--increment-instance-enabled=true | task-launcher-dataflow"
```

目前，我们主要关注启动`ComposedTaskRunner`所需的配置：

* `graph`:这是将由`ComposedTaskRunner`执行的图形。在这种情况下，它是`AAA&&BBB`。

* `increment-instance-enabled`:这使得`ComposedTaskRunner`的每个执行都是唯一的。`ComposedTaskRunner`是通过使用[Spring Batch](https://projects.spring.io/spring-batch/)构建的。因此，我们希望为`ComposedTaskRunner`的每次启动都有一个新的作业实例。为此，我们将`increment-instance-enabled`设置为`true`。

## 31. 与任务共享云数据流的数据存储

正如[任务](#spring-cloud-dataflow-task)文档中所讨论的， Spring 云数据流允许你查看 Spring 云任务应用程序的执行情况。因此，在这一节中，我们将讨论任务应用程序和 Spring 云数据流共享任务执行信息所需的内容。

### 31.1.一个通用的数据存储依赖项

Spring 云数据流支持许多开箱即用的数据库，因此通常需要做的就是声明`spring_datasource_*`环境变量，以建立云数据流所需的数据存储。无论你决定将哪个数据库用于 Spring 云数据流，都要确保你的任务在其`pom.xml`或`gradle.build`文件中也包含该数据库依赖项。如果 Spring 云数据流使用的数据库依赖项在任务应用程序中不存在，则任务失败且任务执行不被记录。

### 31.2.公共数据存储

Spring 云数据流和你的任务应用程序必须访问相同的数据存储实例。这样， Spring 云数据流就可以读取由任务应用程序记录的任务执行情况，以便在 shell 和 dashboard 视图中列出它们。此外，任务应用程序必须具有对 Spring 云数据流使用的任务数据表的读写权限。

有了对任务应用程序和 Spring 云数据流之间的数据源依赖关系的这种理解，你现在就可以了解如何在各种任务编排场景中应用它们了。

#### 31.2.1.简单的任务启动

当从 Spring 云数据流启动任务时，数据流将其数据源属性（`spring.datasource.url`，`spring.datasource.driverClassName`，`spring.datasource.username`，`spring.datasource.密码`）添加到所启动任务的应用程序属性中。因此，任务应用程序将其任务执行信息记录到 Spring 云数据流库中。

#### 31.2.2.组合任务运行器

Spring 云数据流允许你创建一个有向图，其中该图的每个节点都是一个任务应用程序。这是通过[组合任务运行器](https://github.com/spring-cloud-task-app-starters/composed-task-runner/blob/master/spring-cloud-starter-task-composedtaskrunner/README.adoc)完成的。在这种情况下，适用于[简单的任务启动](#datasource-simple-task-launch)或任务启动器接收器的规则也适用于组合的任务运行器。所有子应用程序还必须具有对组合任务运行器正在使用的数据存储的访问权限。此外，所有的子应用程序必须具有与其`pom.xml`或`gradle.build`文件中枚举的组合任务运行器相同的数据库依赖关系。

#### 31.2.3.从 Spring 云数据流在外部启动任务

你可以通过使用另一种方法（例如调度器）来启动 Spring 云任务应用程序，但仍然可以跟踪 Spring 云数据流中的任务执行。如果任务应用程序遵守指定的规则[here](#a-common-datastore-dependency)和[here](#a-common-datastore)，就可以这样做。

|   |如果你想使用 Spring 云数据流来查看你的[Spring Batch](https://projects.spring.io/spring-batch/)作业，请确保<br/>你的批处理应用程序使用`@EnableTask`注释，并遵循枚举的规则[here](#a-common-datastore-dependency)和<br/>。<br/>可获得更多信息[here](https://github.com/spring-projects/spring-batch-admin/blob/master/MIGRATION.md)。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## 32. 调度任务

Spring 云数据流允许你使用`cron`表达式来调度任务的执行。你可以通过 RESTful API 或 Spring Cloud Data Flow UI 创建日程安排。

### 32.1.调度器

Spring 云数据流通过在云平台上可用的调度代理来调度其任务的执行。当使用 Cloud Foundry 平台时， Spring 云数据流使用[PCF 调度器](https://www.cloudfoundry.org/the-foundry/scheduler/)。当使用 Kubernetes 时，将使用[CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)。

|   |调度任务不实现持续部署功能。 Spring 云数据流中对任务定义的应用程序版本或属性的任何更改都不会影响计划的任务。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

![调度器架构概述](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-scheduling-architecture.png)

图 9.建筑概述

### 32.2.启用调度

默认情况下， Spring 云数据流使调度功能失效。要启用调度特性，请将以下特性属性设置为`true`:

* `spring.cloud.dataflow.features.schedules-enabled`

* `spring.cloud.dataflow.features.tasks-enabled`

### 32.3.计划的生命周期

时间表的生命周期包括三个部分：

* [调度任务执行](#spring-cloud-data-flow-schedule-scheduling)

* [删除日程安排](#spring-cloud-data-flow-schedule-unscheduling)

* [上市时间表](#spring-cloud-data-flow-schedule-list)

#### 32.3.1.调度任务执行

你可以通过以下方式安排任务执行：

* Spring 云数据流 shell

* Spring 云数据流仪表板

* Spring 云数据流 RESTful API

#### 32.3.2.安排任务

要使用 shell 调度任务，请使用`task schedule create`命令创建调度，如以下示例所示：

```
dataflow:>task schedule create --definitionName mytask --name mytaskschedule --expression '*/1 * * * *'
Created schedule 'mytaskschedule'
```

在前面的示例中，我们为名为`mytaskschedule`的任务定义创建了一个名为`mytask`的调度。这个时刻表每分钟启动一次`mytask`。

|   |如果使用 Cloud Foundry，上面的`cron`表达式将是：`*/1 * ? * *`。这是因为 Cloud Foundry 使用 Quartz`cron`表达式格式。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------|

##### 计划名称的最大长度

调度名称的最大字符长度取决于平台。

|Kubernetes|Cloud Foundry|Local|
|----------|-------------|-----|
|    52    |     63      | N/A |

#### 32.3.3.删除日程安排

你可以使用以下选项删除日程安排：

* Spring 云数据流 shell

* Spring 云数据流仪表板

* Spring 云数据流 RESTful API

要使用 shell 删除任务调度，请使用`task schedule destroy`命令，如以下示例所示：

```
dataflow:>task schedule destroy --name mytaskschedule
Deleted task schedule 'mytaskschedule'
```

#### 32.3.4.上市时间表

你可以使用以下选项查看可用的时间表：

* Spring 云数据流 shell

* Spring 云数据流仪表板

* Spring 云数据流 RESTful API

要从 shell 查看计划，请使用`task schedule list`命令，如以下示例所示：

```
dataflow:>task schedule list
╔══════════════════════════╤════════════════════╤════════════════════════════════════════════════════╗
║      Schedule Name       │Task Definition Name│                     Properties                     ║
╠══════════════════════════╪════════════════════╪════════════════════════════════════════════════════╣
║mytaskschedule            │mytask              │spring.cloud.scheduler.cron.expression = */1 * * * *║
╚══════════════════════════╧════════════════════╧════════════════════════════════════════════════════╝
```

|   |通过使用 Spring 云数据流 UI 创建、删除和列出计划的指令可以找到[here](https://dataflow.spring.io/docs/feature-guides/batch/scheduling/)。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

## 33. 持续部署

随着任务应用程序的发展，你希望将更新带到生产环境中。 Spring 云数据流围绕能够更新任务应用程序提供的功能，本节将介绍这些功能。

当一个任务应用程序被注册（参见[注册任务应用程序](#spring-cloud-dataflow-register-task-apps)）时，一个版本将与它关联。一个任务应用程序可以有多个与其关联的版本，其中一个被选为默认值。下面的图像演示了一个应用程序，该应用程序具有与其相关的多个版本（请参见时间戳条目）。

![任务应用程序版本](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-application-versions.png)

应用程序的版本是通过注册具有相同名称和坐标的多个应用程序来管理的，*除了*版本。例如，如果你使用以下值注册一个应用程序，那么你将获得一个应用程序，它注册了两个版本（2.1.0.Release 和 2.1.1.Release）：

* 应用程序 1

  * 名称：`timestamp`

  * 类型：`task`

  * URI:`maven://org.springframework.cloud.task.app:timestamp-task:2.1.0.RELEASE`

* 应用程序 2

  * 名称：`timestamp`

  * 类型：`task`

  * URI:`maven://org.springframework.cloud.task.app:timestamp-task:2.1.1.RELEASE`

Spring 除了具有多个版本外，云数据流还需要知道在下一个启动时运行哪个版本。这是通过将版本设置为默认版本来表示的。将任务应用程序配置为默认版本的任何版本，都是在下一个启动请求中运行的版本。你可以看到 UI 中的默认版本，如下图所示：

![任务应用程序默认版本](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-default-version.png)

### 33.1.任务启动生命周期

在 Spring 云数据流的先前版本中，当接收到启动任务的请求时， Spring 云数据流将部署应用程序（如果需要）并运行它。如果应用程序运行在不需要每次都部署应用程序的平台上（例如，CloudFoundry），则使用先前部署的应用程序。这一流程在 2.3 中发生了变化。下图显示了现在出现任务启动请求时会发生什么：

![启动任务的流程](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-launch-flow.png)

在前面的图表中有三个主要的流程需要考虑。第一次发射或不作任何改变就发射是一种。另外两个是在有更改时启动，但应用程序不是当前的，以及在有更改且应用程序正在运行时启动。我们先看流程，不做任何改变。

#### 33.1.1.在不做任何更改的情况下启动任务

1. 数据流中包含一个启动请求。数据流确定不需要升级，因为没有任何更改（自上次执行以来没有更改任何属性、部署属性或版本）。

1. 在缓存已部署工件的平台上（本文写作时为 CloudFoundry），数据流检查应用程序是否已预先部署。

2. 如果需要部署应用程序，那么数据流将部署任务应用程序。

3. 数据流启动应用程序。

这个流是默认的行为，如果没有任何变化，那么每次请求出现时都会发生。请注意，这是数据流一直用于启动任务的相同的流。

#### 33.1.2.使用当前未运行的更改启动任务

启动任务时要考虑的第二个流是，当任务没有运行，但任务应用程序版本、应用程序属性或部署属性中的任何一个发生了更改时。在这种情况下，将执行以下流程：

1. 数据流中包含一个启动请求。由于任务应用程序版本、应用程序属性或部署属性发生了更改，因此数据流确定需要进行升级。

2. 数据流检查任务定义的另一个实例是否正在运行。

1. 如果当前没有其他正在运行的任务定义实例，则删除旧的部署。

2. 在缓存已部署工件的平台上（本文所述为 CloudFoundry），数据流检查应用程序是否以前部署过（由于删除了旧的部署，此检查在该流中计算为`false`）。

3. 数据流使用更新的值（新的应用程序版本、新的合并属性和新的合并部署属性）进行任务应用程序的部署。

4. 数据流启动应用程序。

Spring 云数据流从根本上支持连续部署。

#### 33.1.3.在另一个实例运行时启动具有更改的任务

最后一个主流程是当启动请求来到 Spring 云数据流以执行升级但任务定义当前正在运行时。在这种情况下，由于需要删除当前应用程序，启动将被阻止。在某些平台上（本文所述为 CloudFoundry），删除应用程序会导致当前正在运行的所有应用程序被关闭。这个特性防止了这种情况的发生。下面的过程描述了当一个任务在另一个实例运行时发生更改时会发生的情况：

1. 数据流中包含一个启动请求。由于任务应用程序版本、应用程序属性或部署属性发生了更改，因此数据流确定需要进行升级。

2. 数据流检查任务定义的另一个实例是否正在运行。

3. 数据流阻止了启动的发生，因为任务定义的其他实例正在运行。

|   |由于需要删除当前正在运行的任务，任何需要升级在请求时正在运行的任务定义的启动都将被阻止运行。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

# 任务开发人员指南

有关如何在本地计算机上创建、测试和运行 Spring 云任务应用程序的更多信息，请参见 microSite 的[批处理开发者](https://dataflow.spring.io/docs/batch-developer-guides/)部分。

# 任务监控

有关如何监视作为任务一部分部署的应用程序的更多信息，请参见 MicroSite 的[任务监控](https://dataflow.spring.io/docs/feature-guides/batch/monitoring/)指南。

# 仪表板

本节描述了如何使用 Spring 云数据流的仪表板。

## 34. 导言

Spring 云数据流提供了一种称为仪表板的基于浏览器的 GUI 来管理以下信息：

* **应用程序**:**应用程序**选项卡列出了所有可用的应用程序，并提供了用于注册和取消注册它们的控件。

* **运行时**:**运行时**选项卡提供了所有正在运行的应用程序的列表。

* **溪流**:**溪流**选项卡允许你列出、设计、创建、部署和销毁流定义。

* **任务**:**任务**选项卡允许你列出、创建、启动、调度和销毁任务定义。

* **工作**:**工作**选项卡允许你执行批处理作业相关的功能。

启动 Spring 云数据流后，仪表板可在以下位置使用：

`[<host>:<port>/dashboard](http://<host>:<port>/dashboard)`

例如，如果 Spring 云数据流在本地运行，则仪表板在`[localhost:9393/dashboard](http://localhost:9393/dashboard)`处可用。

如果你启用了 HTTPS，则仪表板的位置为`[localhost:9393/dashboard](https://localhost:9393/dashboard)`。如果你启用了安全性，则登录表单的网址为`[localhost:9393/dashboard/#/login](http://localhost:9393/dashboard/#/login)`。

|   |默认的仪表板服务器端口是`9393`。|
|---|--------------------------------------------|

下图显示了 Spring 云数据流仪表板的打开页面：

![The Spring Cloud Data Flow Dashboard](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-dashboard-about.png)

图 10. Spring 云数据流仪表板

## 35. 应用程序

仪表板的**应用程序**选项卡列出了所有可用的应用程序，并提供了用于注册和取消注册它们的控件（如果适用）。通过使用“批量导入应用程序”操作，你可以一次导入多个应用程序。

下图显示了仪表板中可用应用程序的典型列表：

![可用的应用程序列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-available-apps-list.png)

图 11.可用的应用程序列表

### 35.1.应用程序的批量导入

应用程序可以通过“应用程序”页面上可用的多种方式导入。对于大容量导入，应用程序定义预计将以属性样式表示，如下所示：

```
<type>.<name> = <coordinates>
```

以下示例展示了典型的应用程序定义：

```
task.timestamp=maven://org.springframework.cloud.task.app:timestamp-task:1.2.0.RELEASE
processor.transform=maven://org.springframework.cloud.stream.app:transform-processor-rabbit:1.2.0.RELEASE
```

在“从 HTTP URI 位置导入应用程序坐标”部分中，你可以指定指向存储在其他地方的属性文件的 URI，它应该包含前面示例中所示的格式化的属性。或者，通过在“从属性文件导入应用程序坐标”部分中使用**作为属性的应用程序**文本框，你可以直接列出每个属性字符串。最后，如果属性存储在一个本地文件中，**导入一个文件**选项打开一个本地文件浏览器来选择该文件。在通过其中一个路由设置定义之后，单击**进口申请**。

下图显示了批量导入应用程序的一种方法的示例页面：

![批量导入应用程序](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-bulk-import-applications.png)

图 12.批量导入应用程序

## 36. 运行时

仪表板应用程序的**运行时**选项卡显示了所有正在运行的应用程序的列表。对于每个运行时应用程序，将显示部署的状态和部署的实例的数量。通过单击应用程序 ID，可以获得已用部署属性的列表。

下图显示了**运行时**选项卡的使用示例：

![正在运行的应用程序列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-runtime.png)

图 13.正在运行的应用程序列表

## 37. 溪流

**溪流**选项卡有两个子选项卡：**定义**和**创建流**。以下主题描述了如何与每一个项目合作：

* [处理流定义](#dashboard-stream-definitions)

* [创建一个流](#dashboard-flo-streams-designer)

* [部署流](#dashboard-stream-deploy)

* [访问流日志](#dashboard-stream-logs)

### 37.1.处理流定义

仪表板的**溪流**部分包括**定义**选项卡，该选项卡提供了流定义的列表。在这里，你可以选择部署或取消部署这些流定义。此外，你可以通过单击**毁灭**来删除该定义。每一行在左侧包括一个箭头，你可以单击该箭头查看定义的可视化表示。将鼠标悬停在可视化表示中的框中，将显示有关应用程序的更多详细信息，包括传递给它们的任何选项。

在下面的截图中，`timer`流已被扩展以显示可视化表示：

![流定义列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-streams-list-definitions.png)

图 14.流定义列表

如果单击 Details 按钮，视图会发生变化，以显示该流和任何相关流的可视化表示。在前面的示例中，如果单击`timer`流的详细信息，则该视图将更改为以下视图，该视图清楚地显示了三个流之间的关系（其中两个流正在访问`timer`流）：

![流详细信息页面](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-stream-details.png)

图 15.流详细信息页面

### 37.2.创建一个流

仪表板的**溪流**部分包括**创建流**选项卡，这使得[Spring Flo](https://github.com/spring-projects/spring-flo)设计器可用。设计器是一个画布应用程序，它提供了用于创建数据管道的交互式图形界面。

在此选项卡中，你可以：

* 通过使用 DSL、图形化画布或两者结合来创建、管理和可视化流管道

* 使用具有内容辅助和自动完成功能的 DSL 编写管道

* 在 GUI 中使用自动调整和网格布局功能，以实现更简单和交互式的管道组织

你应该注意这个[screencast](https://www.youtube.com/watch?v=78CgV46OstI)，它突出显示了一些“用于 Spring 云数据流的 FLO”功能。 Spring FLO[wiki](https://github.com/spring-projects/spring-flo/wiki)包括关于核心 FLO 能力的更详细的内容。

下图显示了正在使用的 FLO 设计器：

![Flo for Spring Cloud Data Flo](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-flo-create-stream.png)

图 16. Spring 云数据流的 FLO

### 37.3.部署流

Stream 部署 页面包括制表符，这些制表符提供了设置部署属性和部署流的不同方式。下面的屏幕截图显示了`foobar`（`time | log`）的流部署页面。

你可以使用以下方法定义部署属性：

* Form Builder 选项卡：帮助你定义部署属性（部署程序、应用程序属性等）的构建器

* Free Text 选项卡：一个自由的文本区（用于键-值对）

你可以在两个视图之间切换。

|   |表单生成器提供了对输入的更强的验证。|
|---|----------------------------------------------------------|

![表单构建器](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-stream-deploy-builder.png)

图 17.下图显示了表单生成器

![Free text](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-stream-deploy-freetext.png)

图 18.下图显示了自由文本中的相同属性。

### 37.4.访问流日志

一旦部署了流应用程序，就可以从流`summary`页面访问它们的日志，如下图所示：

![流日志](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-stream-logs.png)

### 37.5.创建扇入和扇出的流

在[扇入扇出](#spring-cloud-dataflow-stream-dsl-fanin-fanout)章节中，你可以通过使用[已命名的目的地](#spring-cloud-dataflow-stream-dsl-named-destinations)了解如何支持扇入扇出用例。UI 还为指定的目的地提供了专门的支持：

![扇入扇出示例](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-flo-create-stream-fanin-fanout.png)

图 19. Spring 云数据流的 FLO

在这个示例中，我们有来自*HTTP 源*和*JDBC 源代码*的数据，这些数据被发送到 *shareddata* 通道，这表示一个 Fan-in 用例。在另一端，我们有一个*Cassandra 水槽*和一个*文件接收器*订阅的*SharedData*通道，它代表一个扇出用例。

### 37.6.创建分接流

使用仪表板创建水龙头非常简单。假设你有一个由*HTTP 源*和*文件接收器*组成的流，并且你想要访问这个流来将数据发送到*JDBC 接收器*。要创建分接流，请将*HTTP 源*的输出连接器连接到*JDBC 接收器*。连接显示为虚线，表示你创建了一个分接流。

![Tap Stream 示例](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-flo-create-tap-stream.png)

图 20.创建分接流

主流（*HTTP 源 * 到*文件接收器*）将被自动命名，如果你还没有为该流提供名称的话。在创建分接流时，主流必须始终显式地命名。在前面的图像中，主流被命名为*http\_ingest*。

通过使用仪表板，你还可以切换主流，使其成为辅助分接流。

![将分接流切换到主流](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-flo-tap-stream-switch-to-primary-stream.png)

图 21.将一次流转换为二次抽头流

将鼠标悬停在现有的主流上，即*HTTP 源*和*文件接收器*之间的直线。出现了几个控制图标，通过单击标记为*切换到/从水龙头切换*的图标，你可以将主流更改为分接流。对分接流执行相同的操作，并将其切换到主流。

![将分接流转换为主流的最终结果](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-flo-tap-stream-switch-to-primary-stream-result.png)

图 22.切换主流的最终结果

|   |当与[已命名的目的地](#spring-cloud-dataflow-stream-dsl-named-destinations)直接交互时，<br/>可以有“n”个组合（输入/输出）。这允许你创建涉及<br/>各种数据源和目的地的复杂拓扑。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 37.7.导入和导出流程

仪表板的**进口/出口**选项卡包括一个页面，该页面提供了导入和导出流的选项。

下图显示了 溪流 导出页面：

![流 utils 导出](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-streams-utils-export.png)

图 23.流 utils 导出页面

在导入流时，必须从有效的 JSON 文件导入。你可以手动起草该文件，也可以从 Streams 导出页面导出该文件。

![流 utils 导入](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-streams-utils-import.png)

图 24.流 utils 导入页面

导入文件后，你将获得操作是否成功完成的确认。

![流 utils 导入结果](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-streams-utils-import-result.png)

图 25.流 utils 导入结果页

## 38. 任务

仪表板的**任务**选项卡当前有三个选项卡：

* [Apps](#dashboard-tasks-apps)

* [定义](#dashboard-task-definition)

* [Executions](#dashboard-tasks-executions)

* [调度](#dashboard-task-scheduling)

### 38.1.应用程序

每个应用程序都将一个工作单元封装到一个可重用的组件中。在数据流运行时环境中，应用程序允许你为流和任务创建定义。因此，**应用程序**选项卡中的**任务**选项卡允许你创建任务定义。

|   |你还可以使用此选项卡创建批处理作业。|
|---|-----------------------------------------------|

下图显示了一个典型的任务应用程序列表：

![任务应用程序列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-apps-list.png)

图 26.任务应用程序列表

在这个屏幕上，你可以执行以下操作：

* 查看详细信息，例如任务应用程序选项。

* 从相应的应用程序中创建任务定义。

#### 38.1.1.查看任务应用程序的详细信息

在此页面上，你可以查看所选任务应用程序的详细信息，包括该应用程序的可用选项（属性）列表。

### 38.2.定义

本页列出了数据流任务定义，并提供了启动或销毁这些任务的操作。

下图显示了定义页面：

![任务定义列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-definitions-list.png)

图 27.任务定义列表

#### 38.2.1.创建任务定义

下图显示了由时间戳应用程序以及可用于创建 TaskDefiniton 的任务应用程序列表组成的任务定义：

![任务应用程序列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-definition-create.png)

在这个页面上，你还可以指定应用程序部署期间使用的各种属性。一旦你对任务定义满意，就可以单击**CREATE TASK**按钮。然后，一个对话框要求提供任务定义名称和描述。至少，你必须为新定义提供一个名称。

#### 38.2.2.创建组合任务定义

仪表板包括**创建组合任务**选项卡，它提供了用于创建组合任务的交互式图形界面。

在此选项卡中，你可以：

* 通过使用 DSL、图形化画布或两者结合来创建和可视化组合任务。

* 在 GUI 中使用自动调整和网格布局功能，以实现对组合任务的更简单和交互式的组织。

在**创建组合任务**屏幕上，你可以通过输入参数键和参数值来定义一个或多个任务参数。

|   |未键入任务参数。|
|---|------------------------------|

下图显示了组成任务设计器：

![组合任务设计器](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-ctr-flo-tab.png)

图 28.组合任务设计器

#### 38.2.3.启动任务

一旦创建了任务定义，就可以通过仪表板启动任务。要这样做，请单击**任务**选项卡，然后按`Launch`选择要启动的任务。下图显示了任务启动页面：

![任务启动](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-launch.png)

图 29.任务启动页面

#### 38.2.4.导入/导出任务

**进口/出口**页面提供了导入和导出任务的选项。这可以通过单击页面左侧的**进口/出口**选项来完成。从这里，单击**导出任务：使用选定的任务创建一个 JSON 文件**选项。出现`Export Tasks(s)`页面。

下图显示了任务导出页面：

![任务 utils 导出](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-tasks-utils-export.png)

图 30.任务 utils 导出页面

类似地，你可以导入任务定义。要这样做，请单击页面左侧的**进口/出口**选项。从这里，单击**导入任务：从 JSON 文件导入任务**选项以显示**导入任务**页面。在**导入任务**页面上，你必须从有效的 JSON 文件导入。你可以手动起草该文件，也可以从**任务导出**页面导出该文件。

![任务 utils 导入](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-tasks-utils-import.png)

图 31.任务 utils 导入页面

在导入文件后，你将获得有关操作是否成功完成的确认。

![任务 utils 导入结果](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-tasks-utils-import-result.png)

图 32.任务 utils 导入结果页

### 38.3.处决

**任务执行**选项卡显示当前正在运行和已完成的任务执行情况。从这个页面，你可以向下钻取**任务执行**详细信息页面。此外，你可以重新启动**任务执行**或停止正在运行的执行。

最后，你可以清理一个或多个任务执行。此操作将从基础持久性存储中删除任何相关的任务或批处理作业。此操作只能为*家长*任务执行触发，并级联到子任务执行（如果有的话）。

下图显示了**处决**选项卡：

![任务执行列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-executions-list.png)

图 33.任务执行列表

### 38.4.执行细节

对于**任务执行**选项卡上的每个任务执行，你可以通过单击任务执行的**执行 ID**来检索有关特定执行的详细信息。

![任务执行列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-execution-detail.png)

在这个屏幕上，你不仅可以查看来自任务执行页面的信息，还可以查看：

* 任务参数

* 外部执行 ID

* 批处理作业指示器（指示任务执行是否包含 Spring 批处理作业）。

* 作业执行 ID 链接（单击作业执行 ID 将带你到该作业执行 ID 的[作业执行细节](#dashboard-job-executions-details)）。

* 任务执行持续时间

* 任务执行退出消息

* 记录任务执行的输出

此外，你还可以触发以下操作：

* 重新启动一项任务

* 停止正在运行的任务

* 任务执行清理（仅针对父任务执行）

#### 38.4.1.停止执行任务

要向平台提交停止任务执行请求，请单击需要停止的任务执行旁边的下拉按钮。现在点击**停止任务**选项。仪表板会显示一个对话框，询问你是否确定要停止任务执行。如果是，请单击`Stop Task Execution(s)`。

![停止执行任务](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-task-execution-stop.png)

|   |Spring 通过 Spring 批处理应用程序启动的使用远程分区的子云任务应用程序不被停止。|
|---|-------------------------------------------------------------------------------------------------------------------------|

## 39. 工作

仪表板的**工作执行**选项卡允许你检查批处理作业。屏幕的主要部分提供了一个作业执行列表。批处理作业是每个任务执行一个或多个批处理作业的任务。每个作业执行都有对任务执行 ID 的引用（在 Task ID 列中）。

作业执行列表还显示了底层作业定义的状态。因此，如果删除了底层定义，则在**状态**列中出现“未找到定义”。

对于每项工作，你可以采取以下操作：

* 重新启动（对于失败的作业）。

* 停止（运行作业）。

* 查看执行细节。

|   |单击“停止”按钮实际上会向正在运行的作业发送“停止”请求，该请求可能不会立即停止。|
|---|----------------------------------------------------------------------------------------------------------|

下图显示了**工作**选项卡：

![工作执行列表](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-job-executions-list.png)

图 34.工作执行列表

### 39.1.作业执行细节

在你启动了一个批处理作业之后，作业执行详细信息页面将显示有关该作业的信息。

下图显示了作业执行详细信息页面：

![作业执行细节](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-jobs-job-execution-details.png)

图 35.作业执行细节

作业执行详细信息页包含已执行步骤的列表。你可以点击放大镜图标，进一步深入了解每一步的执行细节。

### 39.2.步骤执行细节

Step Execution Details 页面提供了关于作业中单个步骤的信息。

下图显示了 Step Execution Details 页面：

![步骤执行历史](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-step-execution-history.png)

图 36.步骤执行细节

Step Execution Details 屏幕提供了所有 Step Execution Context 键-值对的完整列表。

|   |对于异常，**退出描述**字段包含额外的错误信息。<br/>但是，此字段最多可以有 2500 个字符。<br/>因此，在异常堆栈跟踪较长的情况下，可能会发生错误消息的微调。<br/>当发生这种情况时，请检查服务器日志文件以获取更多详细信息。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### 39.3.步骤执行历史

在**步骤执行历史**下，还可以查看与所选步骤相关的各种指标，例如持续时间、读计数、写计数等。

## 40. Scheduling

你可以从 SCDF 仪表板为任务定义创建计划。有关更多信息，请参见微型网站的[调度批处理作业](https://dataflow.spring.io/docs/feature-guides/batch/scheduling/)部分。

## 41. 审计

仪表板的审核页允许你访问记录的审核事件。审计事件记录如下：

* Streams

  * 创建

  * 删除

  * Deploy

  * 取消部署

* Tasks

  * 创建

  * 删除

  * Launch

* 任务的时间安排

  * 创建时间表

  * 删除时间表

下图显示了审计记录页面：

![可用的审计记录清单](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-audit-records-list.png)

图 37.列出审计记录概览

通过单击*显示详细信息*图标（右侧圆圈中的“i”），你可以获得有关审核详细信息的更多详细信息：

![单一审计记录的详细信息](https://raw.githubusercontent.com/spring-cloud/spring-cloud-dataflow/master/spring-cloud-dataflow-docs/src/main/asciidoc/images/dataflow-audit-records-details.png)

图 38.列出审计记录的细节

审计通常提供以下信息：

* 这一纪录是什么时候创造的？

* 触发审计事件的用户的名称（如果启用了安全性）

* 审计操作（计划、流程或任务）

* 执行的操作（创建、删除、部署、回滚、取消部署或更新）

* 相关 ID，例如流或任务名

* 审计数据

*审计数据*属性的写入值取决于执行的*审计业务*和*动作类型*。例如，在创建调度时，任务定义、任务定义属性、部署属性和命令行参数的名称将写入持久性存储。

在保存审核记录之前，会以尽力而为的方式对敏感信息进行消毒。检测到以下任何一个键，并屏蔽了它们的敏感值：

* password

* 秘密

* 钥匙

* 令牌

* 。\* 凭证。\*

* VCAP\_ 服务

# 样本

这一节展示了可用的样本。

## 42. 链接

已经创建了几个示例，以帮助你开始实现比参考指南中所示的基本流和任务更高级别的用例。这些样本是单独的[repository](https://github.com/spring-cloud/spring-cloud-dataflow-samples)的一部分，并且有自己的[参考文献](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/)。

以下样品可供选择：

一般情况

* [Java DSL](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_java_dsl)

* [HTTP 到 Cassandra](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#spring-cloud-data-flow-samples-http-cassandra-overview)

* [从 HTTP 到 MySQL](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_http_to_mysql_demo)

* [HTTP 到 Gemfire](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_http_to_gemfire_demo)

* [Gemfire CQ 到日志演示](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_gemfire_cq_to_log_demo)

* [Gemfire to Log 演示](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_gemfire_to_log_demo)

* [定制处理器](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_custom_spring_cloud_stream_processor)

任务和批处理

* [云铸造中的批处理作业](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_batch_job_on_cloud_foundry)

* [批处理文件摄取](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_batch_file_ingest)

数据科学

* [物种预测](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_species_prediction)

职能

* [Using Spring Cloud Function](https://docs.spring.io/spring-cloud-dataflow-samples/docs/current/reference/htmlsingle/#_functions_in_spring_cloud_data_flow)

# REST API 指南

本部分描述 Spring 云数据流 REST API。

## 43. 概述

Spring 云数据流提供了一个 REST API，该 API 允许你访问服务器的所有方面。实际上， Spring Cloud Data Flow shell 是该 API 的一流使用者。

|   |如果你计划在 Java 中使用 REST API，那么你应该考虑使用内部使用 REST API 的<br/>提供的 Java 客户机（`DataflowTemplate`）。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------|

### 43.1.HTTP 动词

Spring 云数据流试图在其使用 HTTP 动词时尽可能紧密地坚持标准 HTTP 和 REST 约定，如下表所述：

|  Verb  |用法|
|--------|------------------------------------------------------------------------------------------------------------------------------------------------|
| `GET`  |用于检索资源。|
| `POST` |用于创建新资源。|
| `PUT`  |用于更新现有资源，包括部分更新。也用于<br/>暗示`restarts`概念的资源，如任务。|
|`DELETE`|用于删除现有资源。|

### 43.2.HTTP 状态代码

Spring 云数据流试图在其使用 HTTP 状态代码时尽可能紧密地坚持标准 HTTP 和 REST 约定，如下表所示：

|       Status code        |用法|
|--------------------------|--------------------------------------------------------------------------------------------------------------------|
|         `200 OK`         |请求已成功完成。|
|      `201 Created`       |成功地创建了一个新的资源。资源的 URI 可以从响应的`Location`头中获得。|
|     `204 No Content`     |已成功地应用了对现有资源的更新。|
|    `400 Bad Request`     |这一要求是错误的。响应体包括提供进一步信息的错误描述。|
|     `404 Not Found`      |所要求的资源不存在。|
|      `409 Conflict`      |请求的资源已经存在。例如，任务已经存在，或者流已经被部署了。|
|`422 Unprocessable Entity`|在无法停止或重新启动作业执行的情况下返回。|

### 43.3.标头

每个响应都有以下标题：

|     Name     |说明|
|--------------|------------------------------------------------------------|
|`Content-Type`|有效负载的内容类型，例如`application/hal+json`|

### 43.4.错误

|   Path    |  Type  |说明|
|-----------|--------|------------------------------------------------------|
|  `error`  |`String`|发生的 HTTP 错误，例如`Bad Request`|
| `message` |`String`|对错误原因的描述|
|  `path`   |`String`|提出请求的路径|
| `status`  |`Number`|HTTP 状态代码，例如`400`|
|`timestamp`|`String`|发生错误的时间（以毫秒为单位）|

### 43.5.超媒体

Spring 云数据流使用超媒体，并且资源在其响应中包括到其他资源的链接。回复为[从资源到资源语言的超文本应用程序（HAL）](http://stateless.co/hal_specification.html)格式。链接可以在`_links`键下找到。API 的用户不应该自己创建 URI。相反，他们应该使用上述链接来导航。

## 44. 资源

该 API 包括以下资源：

* [Index](#api-guide-resources-index)

* [服务器元信息](#resources-about)

* [审计记录](#api-guide-resources-audit-records)

* [已登记的申请](#resources-registered-applications)

* [流定义](#api-guide-resources-stream-definitions)

* [流部署](#api-guide-resources-stream-deployment)

* [流验证](#api-guide-resources-stream-validate)

* [任务定义](#api-guide-resources-task-definitions)

* [任务执行](#api-guide-resources-task-executions)

* [任务调度程序](#api-guide-resources-task-scheduler)

* [任务验证](#api-guide-resources-task-validate)

* [工作执行](#api-guide-resources-job-executions)

* [作业实例](#api-guide-resources-job-instances)

* [作业步执行](#api-guide-resources-job-step-executions)

* [有关应用程序的运行时信息](#api-guide-resources-runtime-information-applications)

* [流日志](#api-guide-resources-stream-logs)

* [Task Logs](#api-guide-resources-task-logs)

### 44.1.索引

该索引提供了进入 Spring 云数据流的 REST API 的入口点。以下主题提供了更多细节：

* [访问索引](#api-guide-resources-index-access)

* [请求结构](#api-guide-resources-index-request-structure)

* [示例请求](#api-guide-resources-index-example-request)

* [反应结构](#api-guide-resources-index-response-structure)

* [示例响应](#api-guide-resources-index-example-response)

* [链接](#api-guide-resources-index-links)

#### 44.1.1.访问索引

使用`GET`请求访问索引。

##### 请求结构

```
GET / HTTP/1.1
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/' -i -X GET
```

##### 反应结构

|路径|  Type   |     说明     |
|-------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------|
|`_links`|`Object` |                              Links to other resources                              |
|`['api.revision']`|`Number` |           Incremented each time a change is implemented in this REST API           |
|`_links.audit-records.href`|`String` |                             Link to the audit records                              |
|`_links.dashboard.href`|`String` |                               Link to the dashboard                                |
|`_links.streams/definitions.href`|`String` |                          Link to the streams/definitions                           |
|`_links.streams/definitions/definition.href`|`String` |                     Link to the streams/definitions/definition                     |
|`_links.streams/definitions/definition.templated`|`Boolean`|                  Link streams/definitions/definition is templated                  |
|`_links.runtime/apps.href`|`String` |                              Link to the runtime/apps                              |
|`_links.runtime/apps/{appId}.href`|`String` |                          Link to the runtime/apps/{appId}                          |
|`_links.runtime/apps/{appId}.templated`|`Boolean`|                           Link runtime/apps is templated                           |
|`_links.runtime/apps/{appId}/instances.href`|`String` |                     Link to the runtime/apps/{appId}/instances                     |
|`_links.runtime/apps/{appId}/instances.templated`|`Boolean`|                  Link runtime/apps/{appId}/instances is templated                  |
|`_links.runtime/apps/{appId}/instances/{instanceId}.href`|`String` |              Link to the runtime/apps/{appId}/instances/{instanceId}               |
|`_links.runtime/apps/{appId}/instances/{instanceId}.templated`|`Boolean`|           Link runtime/apps/{appId}/instances/{instanceId} is templated            |
|`_links.runtime/streams.href`|`String` |                            Link to the runtime/streams                             |
|`_links.runtime/streams.templated`|`Boolean`|                         Link runtime/streams is templated                          |
|`_links.runtime/streams/{streamNames}.href`|`String` |                     Link to the runtime/streams/{streamNames}                      |
|`_links.runtime/streams/{streamNames}.templated`|`Boolean`|                  Link runtime/streams/{streamNames} is templated                   |
|`_links.streams/logs.href`|`String` |                              Link to the streams/logs                              |
|`_links.streams/logs/{streamName}.href`|`String` |                       Link to the streams/logs/{streamName}                        |
|`_links.streams/logs/{streamName}/{appName}.href`|`String` |                  Link to the streams/logs/{streamName}/{appName}                   |
|`_links.streams/logs/{streamName}.templated`|`Boolean`|                    Link streams/logs/{streamName} is templated                     |
|`_links.streams/logs/{streamName}/{appName}.templated`|`Boolean`|               Link streams/logs/{streamName}/{appName} is templated                |
|`_links.streams/deployments`|`Object` |                            Link to streams/deployments                             |
|`_links.streams/deployments.href`|`String` |                            Link to streams/deployments                             |
|`_links.streams/deployments/{name}`|`Object` |                    Link streams/deployments/{name} is templated                    |
|`_links.streams/deployments/{name}.href`|`String` |                    Link streams/deployments/{name} is templated                    |
|`_links.streams/deployments/{name}.templated`|`Boolean`|                    Link streams/deployments/{name} is templated                    |
|`_links.streams/deployments/{name}{?reuse-deployment-properties}.href`|`String` |                    Link streams/deployments/{name} is templated                    |
|`_links.streams/deployments/{name}{?reuse-deployment-properties}.templated`|`Boolean`|                    Link streams/deployments/{name} is templated                    |
|`_links.streams/deployments/deployment.href`|`String` |                     Link to the streams/deployments/deployment                     |
|`_links.streams/deployments/deployment.templated`|`Boolean`|                  Link streams/deployments/deployment is templated                  |
|`_links.streams/deployments/manifest/{name}/{version}.href`|`String` |             Link to the streams/deployments/manifest/{name}/{version}              |
|`_links.streams/deployments/manifest/{name}/{version}.templated`|`Boolean`|          Link streams/deployments/manifest/{name}/{version} is templated           |
|`_links.streams/deployments/history/{name}.href`|`String` |                   Link to the streams/deployments/history/{name}                   |
|`_links.streams/deployments/history/{name}.templated`|`Boolean`|                   Link streams/deployments/history is templated                    |
|`_links.streams/deployments/rollback/{name}/{version}.href`|`String` |             Link to the streams/deployments/rollback/{name}/{version}              |
|`_links.streams/deployments/rollback/{name}/{version}.templated`|`Boolean`|          Link streams/deployments/rollback/{name}/{version} is templated           |
|`_links.streams/deployments/update/{name}.href`|`String` |                   Link to the streams/deployments/update/{name}                    |
|`_links.streams/deployments/update/{name}.templated`|`Boolean`|                Link streams/deployments/update/{name} is templated                 |
|`_links.streams/deployments/platform/list.href`|`String` |                   Link to the streams/deployments/platform/list                    |
|`_links.streams/deployments/scale/{streamName}/{appName}/instances/{count}.href`|`String` |   Link to the streams/deployments/scale/{streamName}/{appName}/instances/{count}   |
|`_links.streams/deployments/scale/{streamName}/{appName}/instances/{count}.templated`|`Boolean`|Link streams/deployments/scale/{streamName}/{appName}/instances/{count} is templated|
|`_links.streams/validation.href`|`String` |                           Link to the streams/validation                           |
|`_links.streams/validation.templated`|`Boolean`|                        Link streams/validation is templated                        |
|`_links.tasks/platforms.href`|`String` |                            Link to the tasks/platforms                             |
|`_links.tasks/definitions.href`|`String` |                           Link to the tasks/definitions                            |
|`_links.tasks/definitions/definition.href`|`String` |                      Link to the tasks/definitions/definition                      |
|`_links.tasks/definitions/definition.templated`|`Boolean`|                   Link tasks/definitions/definition is templated                   |
|`_links.tasks/executions.href`|`String` |                            Link to the tasks/executions                            |
|`_links.tasks/executions/name.href`|`String` |                         Link to the tasks/executions/name                          |
|`_links.tasks/executions/name.templated`|`Boolean`|                      Link tasks/executions/name is templated                       |
|`_links.tasks/executions/current.href`|`String` |                        Link to the tasks/executions/current                        |
|`_links.tasks/executions/execution.href`|`String` |                       Link to the tasks/executions/execution                       |
|`_links.tasks/executions/execution.templated`|`Boolean`|                    Link tasks/executions/execution is templated                    |
|`_links.tasks/info/executions.href`|`String` |                         Link to the tasks/info/executions                          |
|`_links.tasks/info/executions.templated`|`Boolean`|                            Link tasks/info is templated                            |
|`_links.tasks/logs.href`|`String` |                               Link to the tasks/logs                               |
|`_links.tasks/logs.templated`|`Boolean`|                            Link tasks/logs is templated                            |
|`_links.tasks/schedules.href`|`String` |                       Link to the tasks/executions/schedules                       |
|`_links.tasks/schedules/instances.href`|`String` |                       Link to the tasks/schedules/instances                        |
|`_links.tasks/schedules/instances.templated`|`Boolean`|                    Link tasks/schedules/instances is templated                     |
|`_links.tasks/validation.href`|`String` |                            Link to the tasks/validation                            |
|`_links.tasks/validation.templated`|`Boolean`|                         Link tasks/validation is templated                         |
|`_links.jobs/executions.href`|`String` |                            Link to the jobs/executions                             |
|`_links.jobs/thinexecutions.href`|`String` |                          Link to the jobs/thinexecutions                           |
|`_links.jobs/executions/name.href`|`String` |                          Link to the jobs/executions/name                          |
|`_links.jobs/executions/name.templated`|`Boolean`|                       Link jobs/executions/name is templated                       |
|`_links.jobs/executions/status.href`|`String` |                         Link to the jobs/executions/status                         |
|`_links.jobs/executions/status.templated`|`Boolean`|                      Link jobs/executions/status is templated                      |
|`_links.jobs/thinexecutions/name.href`|`String` |                        Link to the jobs/thinexecutions/name                        |
|`_links.jobs/thinexecutions/name.templated`|`Boolean`|                       Link jobs/executions/name is templated                       |
|`_links.jobs/thinexecutions/jobInstanceId.href`|`String` |                   Link to the jobs/thinexecutions/jobInstanceId                    |
|`_links.jobs/thinexecutions/jobInstanceId.templated`|`Boolean`|                  Link jobs/executions/jobInstanceId is templated                   |
|`_links.jobs/thinexecutions/taskExecutionId.href`|`String` |                  Link to the jobs/thinexecutions/taskExecutionId                   |
|`_links.jobs/thinexecutions/taskExecutionId.templated`|`Boolean`|                 Link jobs/executions/taskExecutionId is templated                  |
|`_links.jobs/executions/execution.href`|`String` |                       Link to the jobs/executions/execution                        |
|`_links.jobs/executions/execution.templated`|`Boolean`|                    Link jobs/executions/execution is templated                     |
|`_links.jobs/executions/execution/steps.href`|`String` |                    Link to the jobs/executions/execution/steps                     |
|`_links.jobs/executions/execution/steps.templated`|`Boolean`|                 Link jobs/executions/execution/steps is templated                  |
|`_links.jobs/executions/execution/steps/step.href`|`String` |                  Link to the jobs/executions/execution/steps/step                  |
|`_links.jobs/executions/execution/steps/step.templated`|`Boolean`|               Link jobs/executions/execution/steps/step is templated               |
|`_links.jobs/executions/execution/steps/step/progress.href`|`String` |             Link to the jobs/executions/execution/steps/step/progress              |
|`_links.jobs/executions/execution/steps/step/progress.templated`|`Boolean`|          Link jobs/executions/execution/steps/step/progress is templated           |
|`_links.jobs/instances/name.href`|`String` |                          Link to the jobs/instances/name                           |
|`_links.jobs/instances/name.templated`|`Boolean`|                       Link jobs/instances/name is templated                        |
|`_links.jobs/instances/instance.href`|`String` |                        Link to the jobs/instances/instance                         |
|`_links.jobs/instances/instance.templated`|`Boolean`|                     Link jobs/instances/instance is templated                      |
|`_links.tools/parseTaskTextToGraph.href`|`String` |                       Link to the tools/parseTaskTextToGraph                       |
|`_links.tools/convertTaskGraphToText.href`|`String` |                      Link to the tools/convertTaskGraphToText                      |
|`_links.apps.href`|`String` |                                  Link to the apps                                  |
|`_links.about.href`|`String` |                                 Link to the about                                  |
|`_links.completions/stream.href`|`String` |                           Link to the completions/stream                           |
|`_links.completions/stream.templated`|`Boolean`|                        Link completions/stream is templated                        |
|`_links.completions/task.href`|`String` |                            Link to the completions/task                            |
|`_links.completions/task.templated`|`Boolean`|                         Link completions/task is templated                         |

##### 示例响应

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 7064

{
  "_links" : {
    "dashboard" : {
      "href" : "http://localhost:9393/dashboard"
    },
    "audit-records" : {
      "href" : "http://localhost:9393/audit-records"
    },
    "streams/definitions" : {
      "href" : "http://localhost:9393/streams/definitions"
    },
    "streams/definitions/definition" : {
      "href" : "http://localhost:9393/streams/definitions/{name}",
      "templated" : true
    },
    "streams/validation" : {
      "href" : "http://localhost:9393/streams/validation/{name}",
      "templated" : true
    },
    "runtime/streams" : {
      "href" : "http://localhost:9393/runtime/streams{?names}",
      "templated" : true
    },
    "runtime/streams/{streamNames}" : {
      "href" : "http://localhost:9393/runtime/streams/{streamNames}",
      "templated" : true
    },
    "runtime/apps" : {
      "href" : "http://localhost:9393/runtime/apps"
    },
    "runtime/apps/{appId}" : {
      "href" : "http://localhost:9393/runtime/apps/{appId}",
      "templated" : true
    },
    "runtime/apps/{appId}/instances" : {
      "href" : "http://localhost:9393/runtime/apps/{appId}/instances",
      "templated" : true
    },
    "runtime/apps/{appId}/instances/{instanceId}" : {
      "href" : "http://localhost:9393/runtime/apps/{appId}/instances/{instanceId}",
      "templated" : true
    },
    "streams/deployments" : {
      "href" : "http://localhost:9393/streams/deployments"
    },
    "streams/deployments/{name}{?reuse-deployment-properties}" : {
      "href" : "http://localhost:9393/streams/deployments/{name}?reuse-deployment-properties=false",
      "templated" : true
    },
    "streams/deployments/{name}" : {
      "href" : "http://localhost:9393/streams/deployments/{name}",
      "templated" : true
    },
    "streams/deployments/history/{name}" : {
      "href" : "http://localhost:9393/streams/deployments/history/{name}",
      "templated" : true
    },
    "streams/deployments/manifest/{name}/{version}" : {
      "href" : "http://localhost:9393/streams/deployments/manifest/{name}/{version}",
      "templated" : true
    },
    "streams/deployments/platform/list" : {
      "href" : "http://localhost:9393/streams/deployments/platform/list"
    },
    "streams/deployments/rollback/{name}/{version}" : {
      "href" : "http://localhost:9393/streams/deployments/rollback/{name}/{version}",
      "templated" : true
    },
    "streams/deployments/update/{name}" : {
      "href" : "http://localhost:9393/streams/deployments/update/{name}",
      "templated" : true
    },
    "streams/deployments/deployment" : {
      "href" : "http://localhost:9393/streams/deployments/{name}",
      "templated" : true
    },
    "streams/deployments/scale/{streamName}/{appName}/instances/{count}" : {
      "href" : "http://localhost:9393/streams/deployments/scale/{streamName}/{appName}/instances/{count}",
      "templated" : true
    },
    "streams/logs" : {
      "href" : "http://localhost:9393/streams/logs"
    },
    "streams/logs/{streamName}" : {
      "href" : "http://localhost:9393/streams/logs/{streamName}",
      "templated" : true
    },
    "streams/logs/{streamName}/{appName}" : {
      "href" : "http://localhost:9393/streams/logs/{streamName}/{appName}",
      "templated" : true
    },
    "tasks/platforms" : {
      "href" : "http://localhost:9393/tasks/platforms"
    },
    "tasks/definitions" : {
      "href" : "http://localhost:9393/tasks/definitions"
    },
    "tasks/definitions/definition" : {
      "href" : "http://localhost:9393/tasks/definitions/{name}",
      "templated" : true
    },
    "tasks/executions" : {
      "href" : "http://localhost:9393/tasks/executions"
    },
    "tasks/executions/name" : {
      "href" : "http://localhost:9393/tasks/executions{?name}",
      "templated" : true
    },
    "tasks/executions/current" : {
      "href" : "http://localhost:9393/tasks/executions/current"
    },
    "tasks/executions/execution" : {
      "href" : "http://localhost:9393/tasks/executions/{id}",
      "templated" : true
    },
    "tasks/validation" : {
      "href" : "http://localhost:9393/tasks/validation/{name}",
      "templated" : true
    },
    "tasks/info/executions" : {
      "href" : "http://localhost:9393/tasks/info/executions{?completed,name}",
      "templated" : true
    },
    "tasks/logs" : {
      "href" : "http://localhost:9393/tasks/logs/{taskExternalExecutionId}{?platformName}",
      "templated" : true
    },
    "tasks/schedules" : {
      "href" : "http://localhost:9393/tasks/schedules"
    },
    "tasks/schedules/instances" : {
      "href" : "http://localhost:9393/tasks/schedules/instances/{taskDefinitionName}",
      "templated" : true
    },
    "jobs/executions" : {
      "href" : "http://localhost:9393/jobs/executions"
    },
    "jobs/executions/name" : {
      "href" : "http://localhost:9393/jobs/executions{?name}",
      "templated" : true
    },
    "jobs/executions/status" : {
      "href" : "http://localhost:9393/jobs/executions{?status}",
      "templated" : true
    },
    "jobs/executions/execution" : {
      "href" : "http://localhost:9393/jobs/executions/{id}",
      "templated" : true
    },
    "jobs/executions/execution/steps" : {
      "href" : "http://localhost:9393/jobs/executions/{jobExecutionId}/steps",
      "templated" : true
    },
    "jobs/executions/execution/steps/step" : {
      "href" : "http://localhost:9393/jobs/executions/{jobExecutionId}/steps/{stepId}",
      "templated" : true
    },
    "jobs/executions/execution/steps/step/progress" : {
      "href" : "http://localhost:9393/jobs/executions/{jobExecutionId}/steps/{stepId}/progress",
      "templated" : true
    },
    "jobs/instances/name" : {
      "href" : "http://localhost:9393/jobs/instances{?name}",
      "templated" : true
    },
    "jobs/instances/instance" : {
      "href" : "http://localhost:9393/jobs/instances/{id}",
      "templated" : true
    },
    "tools/parseTaskTextToGraph" : {
      "href" : "http://localhost:9393/tools"
    },
    "tools/convertTaskGraphToText" : {
      "href" : "http://localhost:9393/tools"
    },
    "jobs/thinexecutions" : {
      "href" : "http://localhost:9393/jobs/thinexecutions"
    },
    "jobs/thinexecutions/name" : {
      "href" : "http://localhost:9393/jobs/thinexecutions{?name}",
      "templated" : true
    },
    "jobs/thinexecutions/jobInstanceId" : {
      "href" : "http://localhost:9393/jobs/thinexecutions{?jobInstanceId}",
      "templated" : true
    },
    "jobs/thinexecutions/taskExecutionId" : {
      "href" : "http://localhost:9393/jobs/thinexecutions{?taskExecutionId}",
      "templated" : true
    },
    "apps" : {
      "href" : "http://localhost:9393/apps"
    },
    "about" : {
      "href" : "http://localhost:9393/about"
    },
    "completions/stream" : {
      "href" : "http://localhost:9393/completions/stream{?start,detailLevel}",
      "templated" : true
    },
    "completions/task" : {
      "href" : "http://localhost:9393/completions/task{?start,detailLevel}",
      "templated" : true
    }
  },
  "api.revision" : 14
}
```

##### Links

索引的主要元素是链接，因为它们允许你遍历 API 并执行所需的功能：

|                              Relation                              |说明|
|--------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|                              `about`                               |访问元信息，包括已启用的功能、安全信息、版本信息|
|                            `dashboard`                             |访问仪表板 UI|
|                          `audit-records`                           |提供审计跟踪信息|
|                               `apps`                               |处理已登记的申请|
|                        `completions/stream`                        |公开 Stream 的 DSL 完成功能|
|                         `completions/task`                         |公开任务的 DSL 完成功能|
|                         `jobs/executions`                          |提供 JobExecution 资源|
|                       `jobs/thinexecutions`                        |提供不包含步骤执行的 JobExecution 瘦资源|
|                    `jobs/executions/execution`                     |提供特定作业执行的详细信息|
|                 `jobs/executions/execution/steps`                  |提供作业执行的步骤|
|               `jobs/executions/execution/steps/step`               |返回特定步骤的详细信息|
|          `jobs/executions/execution/steps/step/progress`           |提供特定步骤的进度信息|
|                       `jobs/executions/name`                       |按作业名称检索作业执行|
|                      `jobs/executions/status`                      |按作业状态检索作业执行|
|                     `jobs/thinexecutions/name`                     |按作业名检索作业执行，不包括步骤执行|
|                `jobs/thinexecutions/jobInstanceId`                 |通过不包含步骤执行的作业实例 ID 检索作业执行|
|               `jobs/thinexecutions/taskExecutionId`                |通过不包含步骤执行的任务执行 ID 检索作业执行|
|                     `jobs/instances/instance`                      |为特定的作业实例提供作业实例资源|
|                       `jobs/instances/name`                        |为特定的作业名称提供作业实例资源|
|                         `runtime/streams`                          |公开流运行时状态|
|                  `runtime/streams/{streamNames}`                   |公开给定流名称的流运行时状态|
|                           `runtime/apps`                           |提供运行时应用程序资源|
|                       `runtime/apps/{appId}`                       |公开特定应用程序的运行时状态|
|                  `runtime/apps/{appId}/instances`                  |提供应用程序实例的状态|
|           `runtime/apps/{appId}/instances/{instanceId}`            |提供特定应用程序实例的状态|
|                        `tasks/definitions`                         |提供任务定义资源|
|                   `tasks/definitions/definition`                   |提供特定任务定义的详细信息|
|                         `tasks/validation`                         |提供任务定义的验证|
|                         `tasks/executions`                         |返回任务执行并允许启动任务|
|                     `tasks/executions/current`                     |提供正在运行的任务的当前计数|
|                      `tasks/info/executions`                       |提供任务执行信息|
|                         `tasks/schedules`                          |提供任务的进度信息|
|                    `tasks/schedules/instances`                     |提供特定任务的进度信息|
|                      `tasks/executions/name`                       |返回给定任务名的所有任务执行|
|                    `tasks/executions/execution`                    |提供特定任务执行的详细信息|
|                         `tasks/platforms`                          |提供用于启动任务的平台帐户。通过添加一个请求参数“SchedulesEnabled=True”，可以对结果进行筛选，以显示支持调度的平台。|
|                            `tasks/logs`                            |检索任务应用程序日志|
|                       `streams/definitions`                        |公开 Streams 资源|
|                  `streams/definitions/definition`                  |处理特定的流定义|
|                        `streams/validation`                        |提供流定义的验证|
|                       `streams/deployments`                        |提供流部署操作|
|                    `streams/deployments/{name}`                    |请求流定义的部署信息|
|     `streams/deployments/{name}{?reuse-deployment-properties}`     |请求流定义的部署信息|
|                  `streams/deployments/deployment`                  |请求（非）部署现有的流定义|
|          `streams/deployments/manifest/{name}/{version}`           |返回发布版本的清单信息|
|                `streams/deployments/history/{name}`                |将 Stream 的部署历史记录作为此版本的一个或多个版本的列表获取|
|          `streams/deployments/rollback/{name}/{version}`           |将流回滚到流的上一个或特定版本|
|                `streams/deployments/update/{name}`                 |更新数据流。|
|                `streams/deployments/platform/list`                 |支持的部署平台列表|
|`streams/deployments/scale/{streamName}/{appName}/instances/{count}`|为选定的流按比例增加或减少应用程序实例的数量|
|                           `streams/logs`                           |检索流的应用程序日志|
|                    `streams/logs/{streamName}`                     |检索流的应用程序日志|
|               `streams/logs/{streamName}/{appName}`                |检索流的特定应用程序日志|
|                    `tools/parseTaskTextToGraph`                    |将任务定义解析为图结构|
|                   `tools/convertTaskGraphToText`                   |将图形格式转换为 DSL 文本格式|

### 44.2.服务器元信息

服务器元信息端点提供了有关服务器本身的更多信息。以下主题提供了更多细节：

* [检索有关服务器的信息](#api-guide-resources-server-meta-retrieving)

* [请求结构](#api-guide-resources-server-meta-request-structure)

* [示例请求](#api-guide-resources-server-meta-example-request)

* [反应结构](#api-guide-resources-server-meta-response-structure)

#### 44.2.1.检索有关服务器的信息

一个`GET`请求返回 Spring 云数据流的元信息，包括：

* 运行时环境信息

* 关于启用了哪些功能的信息

* Spring 云数据流服务器的依赖信息

* 安全信息

##### 请求结构

```
GET /about HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/about' -i -X GET \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 2598

{
  "featureInfo" : {
    "analyticsEnabled" : true,
    "streamsEnabled" : true,
    "tasksEnabled" : true,
    "schedulesEnabled" : true,
    "monitoringDashboardType" : "NONE"
  },
  "versionInfo" : {
    "implementation" : {
      "name" : "${info.app.name}",
      "version" : "${info.app.version}"
    },
    "core" : {
      "name" : "Spring Cloud Data Flow Core",
      "version" : "2.9.2"
    },
    "dashboard" : {
      "name" : "Spring Cloud Dataflow UI",
      "version" : "3.2.2"
    },
    "shell" : {
      "name" : "Spring Cloud Data Flow Shell",
      "version" : "2.9.2",
      "url" : "https://repo1.maven.org/maven2/org/springframework/cloud/spring-cloud-dataflow-shell/2.9.2/spring-cloud-dataflow-shell-2.9.2.jar"
    }
  },
  "securityInfo" : {
    "authenticationEnabled" : false,
    "authenticated" : false,
    "username" : null,
    "roles" : [ ]
  },
  "runtimeEnvironment" : {
    "appDeployer" : {
      "deployerImplementationVersion" : "Test Version",
      "deployerName" : "Test Server",
      "deployerSpiVersion" : "2.8.2",
      "javaVersion" : "1.8.0_322",
      "platformApiVersion" : "",
      "platformClientVersion" : "",
      "platformHostVersion" : "",
      "platformSpecificInfo" : {
        "default" : "local"
      },
      "platformType" : "Skipper Managed",
      "springBootVersion" : "2.5.8",
      "springVersion" : "5.3.14"
    },
    "taskLaunchers" : [ {
      "deployerImplementationVersion" : "2.7.2",
      "deployerName" : "LocalTaskLauncher",
      "deployerSpiVersion" : "2.7.2",
      "javaVersion" : "1.8.0_322",
      "platformApiVersion" : "Linux 5.11.0-1025-azure",
      "platformClientVersion" : "5.11.0-1025-azure",
      "platformHostVersion" : "5.11.0-1025-azure",
      "platformSpecificInfo" : { },
      "platformType" : "Local",
      "springBootVersion" : "2.5.8",
      "springVersion" : "5.3.14"
    }, {
      "deployerImplementationVersion" : "2.7.2",
      "deployerName" : "LocalTaskLauncher",
      "deployerSpiVersion" : "2.7.2",
      "javaVersion" : "1.8.0_322",
      "platformApiVersion" : "Linux 5.11.0-1025-azure",
      "platformClientVersion" : "5.11.0-1025-azure",
      "platformHostVersion" : "5.11.0-1025-azure",
      "platformSpecificInfo" : { },
      "platformType" : "Local",
      "springBootVersion" : "2.5.8",
      "springVersion" : "5.3.14"
    } ]
  },
  "monitoringDashboardInfo" : {
    "url" : "",
    "refreshInterval" : 15,
    "dashboardType" : "NONE",
    "source" : "default-scdf-source"
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/about"
    }
  }
}
```

### 44.3.已登记的申请

注册的应用程序端点提供了关于已注册到 Spring 云数据流服务器的应用程序的信息。以下主题提供了更多细节：

* [列出应用程序](#resources-app-registry-list)

* [获取特定应用程序的信息](#resources-app-registry-get)

* [注册新的应用程序](#resources-app-registry-post)

* [用 Version 注册新应用程序](#resources-app-registry-post-versioned)

* [批量注册应用程序](#resources-app-registry-bulk)

* [设置默认的应用程序版本](#resources-app-registry-default)

* [取消注册应用程序](#resources-app-registry-delete)

* [取消注册所有应用程序](#resources-app-registry-delete-all)

#### 44.3.1.列出应用程序

一个`GET`请求列出了 Spring 云数据流中已知的所有应用程序。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-request-structure)

* [请求参数](#api-guide-resources-app-registry-request-parameters)

* [示例请求](#api-guide-resources-app-registry-example-request)

* [反应结构](#api-guide-resources-app-registry-response-structure)

##### 请求结构

```
GET /apps?search=&type=source&defaultVersion=true&page=0&size=10&sort=name%2CASC HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 请求参数

|   Parameter    |说明|
|----------------|----------------------------------------------------------------------------------------------|
|    `search`    |对名称执行的搜索字符串（可选）|
|     `type`     |将返回的应用程序限制为应用程序的类型。[应用程序、源程序、处理器、接收器、任务]之一|
|`defaultVersion`|设置为仅检索缺省版本的应用程序的布尔标志（可选）|
|     `page`     |基于零的页码（可选）|
|     `sort`     |列表中的排序（可选）|
|     `size`     |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/apps?search=&type=source&defaultVersion=true&page=0&size=10&sort=name%2CASC' -i -X GET \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1097

{
  "_embedded" : {
    "appRegistrationResourceList" : [ {
      "name" : "http",
      "type" : "source",
      "uri" : "maven://org.springframework.cloud.stream.app:http-source-rabbit:1.2.0.RELEASE",
      "version" : "1.2.0.RELEASE",
      "defaultVersion" : true,
      "versions" : [ "1.2.0.RELEASE" ],
      "label" : null,
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/apps/source/http/1.2.0.RELEASE"
        }
      }
    }, {
      "name" : "time",
      "type" : "source",
      "uri" : "maven://org.springframework.cloud.stream.app:time-source-rabbit:1.2.0.RELEASE",
      "version" : "1.2.0.RELEASE",
      "defaultVersion" : true,
      "versions" : [ "1.2.0.RELEASE" ],
      "label" : null,
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/apps/source/time/1.2.0.RELEASE"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/apps?page=0&size=10&sort=name,asc"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.3.2.获取特定应用程序的信息

在`/apps/<type>/<name>`上的`GET`请求获取特定应用程序的信息。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-get-request-structure)

* [路径参数](#api-guide-resources-app-registry-get-path-parameters)

* [示例请求](#api-guide-resources-app-registry-get-example-request)

* [反应结构](#api-guide-resources-app-registry-get-response-structure)

##### 请求结构

```
GET /apps/source/http?exhaustive=false HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 请求参数

| Parameter  |   说明   |
|------------|--------------------------------------------------------------------------|
|`exhaustive`|返回所有应用程序属性，包括常见的 Spring 引导属性|

##### 路径参数

/apps/{type}/{name}

|Parameter|说明|
|---------|-----------------------------------------------------------------------------|
| `type`  |要查询的应用程序类型。[应用程序、源程序、处理器、接收器、任务]之一|
| `name`  |要查询的应用程序的名称|

##### 示例请求

```
$ curl 'http://localhost:9393/apps/source/http?exhaustive=false' -i -X GET \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 2100

{
  "name" : "http",
  "type" : "source",
  "uri" : "maven://org.springframework.cloud.stream.app:http-source-rabbit:1.2.0.RELEASE",
  "version" : "1.2.0.RELEASE",
  "defaultVersion" : true,
  "versions" : null,
  "label" : null,
  "options" : [ {
    "id" : "http.path-pattern",
    "name" : "path-pattern",
    "type" : "java.lang.String",
    "description" : "An Ant-Style pattern to determine which http requests will be captured.",
    "shortDescription" : "An Ant-Style pattern to determine which http requests will be captured.",
    "defaultValue" : "/",
    "hints" : {
      "keyHints" : [ ],
      "keyProviders" : [ ],
      "valueHints" : [ ],
      "valueProviders" : [ ]
    },
    "deprecation" : null,
    "deprecated" : false
  }, {
    "id" : "http.mapped-request-headers",
    "name" : "mapped-request-headers",
    "type" : "java.lang.String[]",
    "description" : "Headers that will be mapped.",
    "shortDescription" : "Headers that will be mapped.",
    "defaultValue" : null,
    "hints" : {
      "keyHints" : [ ],
      "keyProviders" : [ ],
      "valueHints" : [ ],
      "valueProviders" : [ ]
    },
    "deprecation" : null,
    "deprecated" : false
  }, {
    "id" : "http.secured",
    "name" : "secured",
    "type" : "java.lang.Boolean",
    "description" : "Secure or not HTTP source path.",
    "shortDescription" : "Secure or not HTTP source path.",
    "defaultValue" : false,
    "hints" : {
      "keyHints" : [ ],
      "keyProviders" : [ ],
      "valueHints" : [ ],
      "valueProviders" : [ ]
    },
    "deprecation" : null,
    "deprecated" : false
  }, {
    "id" : "server.port",
    "name" : "port",
    "type" : "java.lang.Integer",
    "description" : "Server HTTP port.",
    "shortDescription" : "Server HTTP port.",
    "defaultValue" : null,
    "hints" : {
      "keyHints" : [ ],
      "keyProviders" : [ ],
      "valueHints" : [ ],
      "valueProviders" : [ ]
    },
    "deprecation" : null,
    "deprecated" : false
  } ],
  "shortDescription" : null,
  "inboundPortNames" : [ ],
  "outboundPortNames" : [ ],
  "optionGroups" : { }
}
```

#### 44.3.3.注册新的应用程序

在`/apps/<type>/<name>`上的`POST`请求允许注册新的申请。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-post-request-structure)

* [请求参数](#api-guide-resources-app-registry-post-request-parameters)

* [路径参数](#api-guide-resources-app-registry-post-path-parameters)

* [示例请求](#api-guide-resources-app-registry-post-example-request)

* [反应结构](#api-guide-resources-app-registry-post-response-structure)

##### 请求结构

```
POST /apps/source/http HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

uri=maven%3A%2F%2Forg.springframework.cloud.stream.app%3Ahttp-source-rabbit%3A1.1.0.RELEASE
```

##### 请求参数

|  Parameter   |说明|
|--------------|--------------------------------------------------------------------------------------------------------|
|    `uri`     |应用程序位所在的 URI|
|`metadata-uri`|可以找到应用程序元数据 JAR 的 URI|
|   `force`    |如果已经存在具有相同名称和类型的注册，则必须为 true，否则将发生错误|

##### 路径参数

/apps/{type}/{name}

|Parameter|说明|
|---------|--------------------------------------------------------------------------------|
| `type`  |申请注册的类别。[应用程序、源程序、处理器、接收器、任务]之一|
| `name`  |登记申请书的名称|

##### 示例请求

```
$ curl 'http://localhost:9393/apps/source/http' -i -X POST \
    -d 'uri=maven%3A%2F%2Forg.springframework.cloud.stream.app%3Ahttp-source-rabbit%3A1.1.0.RELEASE'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

#### 44.3.4.用 Version 注册新应用程序

在`/apps/<type>/<name>/<version>`上的`POST`请求允许注册新的申请。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-post-versioned-request-structure)

* [请求参数](#api-guide-resources-app-registry-post-versioned-request-parameters)

* [路径参数](#api-guide-resources-app-registry-post-versioned-path-parameters)

* [示例请求](#api-guide-resources-app-registry-post-versioned-example-request)

* [反应结构](#api-guide-resources-app-registry-post-versioned-response-structure)

##### 请求结构

```
POST /apps/source/http/1.1.0.RELEASE HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

uri=maven%3A%2F%2Forg.springframework.cloud.stream.app%3Ahttp-source-rabbit%3A1.1.0.RELEASE
```

##### 请求参数

|  Parameter   |说明|
|--------------|--------------------------------------------------------------------------------------------------------|
|    `uri`     |应用程序位所在的 URI|
|`metadata-uri`|可以找到应用程序元数据 JAR 的 URI|
|   `force`    |如果已经存在具有相同名称和类型的注册，则必须为 true，否则将发生错误|

##### 路径参数

/apps/{type}/{name}/{version:.+}

|Parameter|说明|
|---------|-------------------------------------------------------------------------------------------|
| `type`  |申请注册的类别。[应用程序、源程序、处理器、接收器、任务]之一（可选）|
| `name`  |登记申请书的名称|
|`version`|要注册的应用程序的版本|

##### 示例请求

```
$ curl 'http://localhost:9393/apps/source/http/1.1.0.RELEASE' -i -X POST \
    -d 'uri=maven%3A%2F%2Forg.springframework.cloud.stream.app%3Ahttp-source-rabbit%3A1.1.0.RELEASE'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

#### 44.3.5.批量注册应用程序

在`/apps`上的`POST`请求允许一次注册多个应用程序。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-bulk-request-structure)

* [请求参数](#api-guide-resources-app-registry-bulk-request-parameters)

* [示例请求](#api-guide-resources-app-registry-bulk-example-request)

* [反应结构](#api-guide-resources-app-registry-bulk-response-structure)

##### 请求结构

```
POST /apps HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

apps=source.http%3Dmaven%3A%2F%2Forg.springframework.cloud.stream.app%3Ahttp-source-rabbit%3A1.1.0.RELEASE&force=false
```

##### 请求参数

|Parameter|说明|
|---------|--------------------------------------------------------------------------------------------------------|
|  `uri`  |可以获取包含注册的属性文件的 URI。独家使用`apps`。|
| `apps`  |内联注册集。独家使用`uri`。|
| `force` |如果已经存在具有相同名称和类型的注册，则必须为 true，否则将发生错误|

##### 示例请求

```
$ curl 'http://localhost:9393/apps' -i -X POST \
    -d 'apps=source.http%3Dmaven%3A%2F%2Forg.springframework.cloud.stream.app%3Ahttp-source-rabbit%3A1.1.0.RELEASE&force=false'
```

##### 反应结构

```
HTTP/1.1 201 Created
Content-Type: application/hal+json
Content-Length: 658

{
  "_embedded" : {
    "appRegistrationResourceList" : [ {
      "name" : "http",
      "type" : "source",
      "uri" : "maven://org.springframework.cloud.stream.app:http-source-rabbit:1.1.0.RELEASE",
      "version" : "1.1.0.RELEASE",
      "defaultVersion" : true,
      "versions" : null,
      "label" : null,
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/apps/source/http/1.1.0.RELEASE"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/apps?page=0&size=20"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.3.6.设置默认的应用程序版本

对于具有相同`name`和`type`的应用程序，可以注册多个版本。在这种情况下，你可以选择其中一个版本作为默认应用程序。

以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-default-request-structure)

* [路径参数](#api-guide-resources-app-registry-default-path-parameters)

* [示例请求](#api-guide-resources-app-registry-default-example-request)

* [反应结构](#api-guide-resources-app-registry-default-response-structure)

##### 请求结构

```
PUT /apps/source/http/1.2.0.RELEASE HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 路径参数

/apps/{type}/{name}/{version:.+}

|Parameter|       说明       |
|---------|--------------------------------------------------------------------|
| `type`  |应用程序的类型。[应用程序、源程序、处理器、接收器、任务]之一|
| `name`  |申请的名称|
|`version`|应用程序的版本|

##### 示例请求

```
$ curl 'http://localhost:9393/apps/source/http/1.2.0.RELEASE' -i -X PUT \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 202 Accepted
```

#### 44.3.7.取消注册应用程序

在`/apps/<type>/<name>`上的`DELETE`请求取消注册先前已注册的应用程序。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-delete-request-structure)

* [路径参数](#api-guide-resources-app-registry-delete-path-parameters)

* [示例请求](#api-guide-resources-app-registry-delete-example-request)

* [反应结构](#api-guide-resources-app-registry-delete-response-structure)

##### 请求结构

```
DELETE /apps/source/http/1.2.0.RELEASE HTTP/1.1
Host: localhost:9393
```

##### 路径参数

/apps/{type}/{name}/{version}

|Parameter|说明|
|---------|----------------------------------------------------------------------------------|
| `type`  |要取消注册的应用程序的类型。[应用程序、源程序、处理器、接收器、任务]之一|
| `name`  |要注销的应用程序的名称|
|`version`|要取消注册的应用程序的版本（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/apps/source/http/1.2.0.RELEASE' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.3.8.取消注册所有应用程序

在`/apps`上的`DELETE`请求取消注册所有的应用程序。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-app-registry-delete-all-request-structure)

* [示例请求](#api-guide-resources-app-registry-delete-all-example-request)

* [反应结构](#api-guide-resources-app-registry-delete-all-response-structure)

##### 请求结构

```
DELETE /apps HTTP/1.1
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/apps' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

### 44.4.审计记录

审计记录端点提供有关审计记录的信息。以下主题提供了更多细节：

* [列出所有审计记录](#api-guide-resources-audit-records-list)

* [检索审计记录详细信息](#api-guide-resources-audit-record-get)

* [列出所有审计操作类型](#api-guide-resources-audit-action-types)

* [列出所有审计操作类型](#api-guide-resources-audit-operation-types)

#### 44.4.1.列出所有审计记录

审计记录端点允许你检索审计跟踪信息。

以下主题提供了更多细节：

* [请求结构](#api-guide-resources-audit-records-list-request-structure)

* [请求参数](#api-guide-resources-audit-records-list-request-parameters)

* [示例请求](#api-guide-resources-audit-records-list-example-request)

* [反应结构](#api-guide-resources-audit-records-list-response-structure)

##### 请求结构

```
GET /audit-records?page=0&size=10&operations=STREAM&actions=CREATE&fromDate=2000-01-01T00%3A00%3A00&toDate=2099-01-01T00%3A00%3A00 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

| Parameter  |    说明    |
|------------|------------------------------------------------------|
|   `page`   |基于零的页码（可选）|
|   `size`   |请求的页面大小（可选）|
|`operations`|用逗号分隔的审计操作列表（可选）|
| `actions`  |用逗号分隔的审计操作列表（可选）|
| `fromDate` |自日期过滤器（例如：2019-02-03T00:00:30）（可选）|
|  `toDate`  |截止日期过滤器（例如：2019-02-03T00:00:30）（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/audit-records?page=0&size=10&operations=STREAM&actions=CREATE&fromDate=2000-01-01T00%3A00%3A00&toDate=2099-01-01T00%3A00%3A00' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 680

{
  "_embedded" : {
    "auditRecordResourceList" : [ {
      "auditRecordId" : 5,
      "createdBy" : null,
      "correlationId" : "timelog",
      "auditData" : "time --format='YYYY MM DD' | log",
      "createdOn" : "2022-01-18T18:52:24.663Z",
      "auditAction" : "CREATE",
      "auditOperation" : "STREAM",
      "platformName" : null,
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/audit-records/5"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/audit-records?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.4.2.检索审计记录详细信息

审计记录端点允许你获得单个审计记录。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-audit-record-get-request-structure)

* [路径参数](#api-guide-resources-audit-record-get-path-parameters)

* [示例请求](#api-guide-resources-audit-record-get-example-request)

* [反应结构](#api-guide-resources-audit-record-get-response-structure)

##### 请求结构

```
GET /audit-records/5 HTTP/1.1
Host: localhost:9393
```

##### 路径参数

/audit-records/{id}

|Parameter|                 Description                  |
|---------|----------------------------------------------|
|  `id`   |要查询的审计记录的 ID（必需的）|

##### 示例请求

```
$ curl 'http://localhost:9393/audit-records/5' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 354

{
  "auditRecordId" : 5,
  "createdBy" : null,
  "correlationId" : "timelog",
  "auditData" : "time --format='YYYY MM DD' | log",
  "createdOn" : "2022-01-18T18:52:24.663Z",
  "auditAction" : "CREATE",
  "auditOperation" : "STREAM",
  "platformName" : null,
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/audit-records/5"
    }
  }
}
```

#### 44.4.3.列出所有审计操作类型

审计记录端点允许你获取操作类型。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-audit-action-types-request-structure)

* [示例请求](#api-guide-resources-audit-action-types-example-request)

* [反应结构](#api-guide-resources-audit-action-types-response-structure)

##### 请求结构

```
GET /audit-records/audit-action-types HTTP/1.1
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/audit-records/audit-action-types' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1111

[ {
  "id" : 100,
  "name" : "Create",
  "description" : "Create an Entity",
  "nameWithDescription" : "Create (Create an Entity)",
  "key" : "CREATE"
}, {
  "id" : 200,
  "name" : "Delete",
  "description" : "Delete an Entity",
  "nameWithDescription" : "Delete (Delete an Entity)",
  "key" : "DELETE"
}, {
  "id" : 300,
  "name" : "Deploy",
  "description" : "Deploy an Entity",
  "nameWithDescription" : "Deploy (Deploy an Entity)",
  "key" : "DEPLOY"
}, {
  "id" : 400,
  "name" : "Rollback",
  "description" : "Rollback an Entity",
  "nameWithDescription" : "Rollback (Rollback an Entity)",
  "key" : "ROLLBACK"
}, {
  "id" : 500,
  "name" : "Undeploy",
  "description" : "Undeploy an Entity",
  "nameWithDescription" : "Undeploy (Undeploy an Entity)",
  "key" : "UNDEPLOY"
}, {
  "id" : 600,
  "name" : "Update",
  "description" : "Update an Entity",
  "nameWithDescription" : "Update (Update an Entity)",
  "key" : "UPDATE"
}, {
  "id" : 700,
  "name" : "SuccessfulLogin",
  "description" : "Successful login",
  "nameWithDescription" : "SuccessfulLogin (Successful login)",
  "key" : "LOGIN_SUCCESS"
} ]
```

#### 44.4.4.列出所有审计操作类型

审计记录端点允许你获得操作类型。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-audit-operation-types-request-structure)

* [示例请求](#api-guide-resources-audit-operation-types-example-request)

* [反应结构](#api-guide-resources-audit-operation-types-response-structure)

##### 请求结构

```
GET /audit-records/audit-operation-types HTTP/1.1
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/audit-records/audit-operation-types' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 315

[ {
  "id" : 100,
  "name" : "App Registration",
  "key" : "APP_REGISTRATION"
}, {
  "id" : 200,
  "name" : "Schedule",
  "key" : "SCHEDULE"
}, {
  "id" : 300,
  "name" : "Stream",
  "key" : "STREAM"
}, {
  "id" : 400,
  "name" : "Task",
  "key" : "TASK"
}, {
  "id" : 500,
  "name" : "Login",
  "key" : "LOGIN"
} ]
```

### 44.5.流定义

注册的应用程序端点提供了关于已注册到 Spring 云数据流服务器的流定义的信息。以下主题提供了更多细节：

* [创建新的流定义](#api-guide-resources-stream-definitions-create)

* [列出所有流定义](#api-guide-resources-stream-definitions-list)

* [列出相关的流定义](#api-guide-resources-stream-definitions-list-related)

* [检索流定义详细信息](#api-guide-resources-stream-definition-get)

* [删除单个流定义](#api-guide-resources-stream-definitions-delete-one)

* [删除所有流定义](#api-guide-resources-stream-definitions-delete-all)

* [部署流定义](#api-guide-resources-stream-deployment-deploy)

* [取消部署流定义](#api-guide-resources-stream-deployment-undeploy)

* [取消部署所有流定义](#api-guide-resources-stream-deployment-undeploy-all)

#### 44.5.1.创建新的流定义

创建流定义是通过创建对流定义端点的 POST 请求来实现的。对`ticktock`流的 curl 请求可能类似于以下内容：

```
curl -X POST -d "name=ticktock&definition=time | log" localhost:9393/streams/definitions?deploy=false
```

流定义还可以包含其他参数。例如，在“[请求结构](#api-guide-resources-stream-definitions-create-request-structure)”下显示的示例中，我们还提供了日期时间格式。

以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-create-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-create-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-create-example-request)

* [反应结构](#api-guide-resources-stream-definitions-create-response-structure)

##### 请求结构

```
POST /streams/definitions HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

name=timelog&definition=time+--format%3D%27YYYY+MM+DD%27+%7C+log&description=Demo+stream+for+testing&deploy=false
```

##### 请求参数

|  Parameter  |说明|
|-------------|----------------------------------------------------------------|
|   `name`    |创建的任务定义的名称|
|`definition` |流的定义，使用数据流 DSL|
|`description`|流定义的描述|
|  `deploy`   |如果为真，则在创建时部署流（默认值为 false）|

##### 示例请求

```
$ curl 'http://localhost:9393/streams/definitions' -i -X POST \
    -d 'name=timelog&definition=time+--format%3D%27YYYY+MM+DD%27+%7C+log&description=Demo+stream+for+testing&deploy=false'
```

##### 反应结构

```
HTTP/1.1 201 Created
Content-Type: application/hal+json
Content-Length: 410

{
  "name" : "timelog",
  "dslText" : "time --format='YYYY MM DD' | log",
  "originalDslText" : "time --format='YYYY MM DD' | log",
  "status" : "undeployed",
  "description" : "Demo stream for testing",
  "statusDescription" : "The app or group is known to the system, but is not currently deployed",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/streams/definitions/timelog"
    }
  }
}
```

#### 44.5.2.列出所有流定义

Streams Endpoint 允许你列出所有的流定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-list-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-list-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-list-example-request)

* [反应结构](#api-guide-resources-stream-definitions-list-response-structure)

##### 请求结构

```
GET /streams/definitions?page=0&sort=name%2CASC&search=&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|--------------------------------------------------|
| `page`  |基于零的页码（可选）|
|`search` |对名称执行的搜索字符串（可选）|
| `sort`  |列表中的排序（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/streams/definitions?page=0&sort=name%2CASC&search=&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1160

{
  "_embedded" : {
    "streamDefinitionResourceList" : [ {
      "name" : "mysamplestream",
      "dslText" : "time | log",
      "originalDslText" : "time | log",
      "status" : "undeployed",
      "description" : "",
      "statusDescription" : "The app or group is known to the system, but is not currently deployed",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/streams/definitions/mysamplestream"
        }
      }
    }, {
      "name" : "timelog",
      "dslText" : "time --format='YYYY MM DD' | log",
      "originalDslText" : "time --format='YYYY MM DD' | log",
      "status" : "undeployed",
      "description" : "Demo stream for testing",
      "statusDescription" : "The app or group is known to the system, but is not currently deployed",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/streams/definitions/timelog"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/streams/definitions?page=0&size=10&sort=name,asc"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.5.3.列出相关的流定义

Streams Endpoint 允许你列出相关的流定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-list-related-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-list-related-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-list-related-example-request)

* [反应结构](#api-guide-resources-stream-definitions-list-related-response-structure)

##### 请求结构

```
GET /streams/definitions/timelog/related?page=0&sort=name%2CASC&search=&size=10&nested=true HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|--------------------------------------------------------------------------------------|
|`nested` |我们是否应该通过 tasknameContains 递归地找到相关的流定义（可选）|
| `page`  |基于零的页码（可选）|
|`search` |对名称执行的搜索字符串（可选）|
| `sort`  |列表中的排序（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/streams/definitions/timelog/related?page=0&sort=name%2CASC&search=&size=10&nested=true' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 769

{
  "_embedded" : {
    "streamDefinitionResourceList" : [ {
      "name" : "timelog",
      "dslText" : "time --format='YYYY MM DD' | log",
      "originalDslText" : "time --format='YYYY MM DD' | log",
      "status" : "undeployed",
      "description" : "Demo stream for testing",
      "statusDescription" : "The app or group is known to the system, but is not currently deployed",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/streams/definitions/timelog"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/streams/definitions/timelog/related?page=0&size=10&sort=name,asc"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.5.4.检索流定义详细信息

流定义端点允许你获得单个流定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definition-get-request-structure)

* [路径参数](#api-guide-resources-stream-definition-get-path-parameters)

* [示例请求](#api-guide-resources-stream-definition-get-example-request)

* [反应结构](#api-guide-resources-stream-definition-get-response-structure)

##### 请求结构

```
GET /streams/definitions/timelog HTTP/1.1
Host: localhost:9393
```

##### 路径参数

/streams/definitions/{name}

|Parameter|说明|
|---------|-----------------------------------------------------|
| `name`  |要查询的流定义的名称（必需的）|

##### 示例请求

```
$ curl 'http://localhost:9393/streams/definitions/timelog' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 410

{
  "name" : "timelog",
  "dslText" : "time --format='YYYY MM DD' | log",
  "originalDslText" : "time --format='YYYY MM DD' | log",
  "status" : "undeployed",
  "description" : "Demo stream for testing",
  "statusDescription" : "The app or group is known to the system, but is not currently deployed",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/streams/definitions/timelog"
    }
  }
}
```

#### 44.5.5.删除单个流定义

Streams Endpoint 允许你删除单个流定义。（另请参见：[删除所有流定义](#api-guide-resources-stream-definitions-delete-all)。）以下主题提供了更多详细信息：

* [请求结构](#api-guide-resources-stream-definitions-delete-one-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-delete-one-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-delete-one-example-request)

* [反应结构](#api-guide-resources-stream-definitions-delete-one-response-structure)

##### 请求结构

```
DELETE /streams/definitions/timelog HTTP/1.1
Host: localhost:9393
```

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/definitions/timelog' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.5.6.删除所有流定义

Streams Endpoint 允许你删除所有单个流定义。（另请参见：[删除单个流定义](#api-guide-resources-stream-definitions-delete-one)。）以下主题提供了更多详细信息：

* [请求结构](#api-guide-resources-stream-definitions-delete-all-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-delete-all-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-delete-all-example-request)

* [反应结构](#api-guide-resources-stream-definitions-delete-all-response-structure)

##### 请求结构

```
DELETE /streams/definitions HTTP/1.1
Host: localhost:9393
```

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/definitions' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

### 44.6.流验证

流验证端点允许你在流定义中验证应用程序。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-validation-request-structure)

* [路径参数](#api-guide-resources-stream-validation-path-parameters)

* [示例请求](#api-guide-resources-stream-validation-example-request)

* [反应结构](#api-guide-resources-stream-validation-response-structure)

#### 44.6.1.请求结构

```
GET /streams/validation/timelog HTTP/1.1
Host: localhost:9393
```

#### 44.6.2.路径参数

/streams/validation/{name}

|Parameter|说明|
|---------|----------------------------------------------------------|
| `name`  |要验证的流定义的名称（必需的）|

#### 44.6.3.示例请求

```
$ curl 'http://localhost:9393/streams/validation/timelog' -i -X GET
```

#### 44.6.4.反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 197

{
  "appName" : "timelog",
  "dsl" : "time --format='YYYY MM DD' | log",
  "description" : "Demo stream for testing",
  "appStatuses" : {
    "source:time" : "valid",
    "sink:log" : "valid"
  }
}
```

### 44.7.流部署

部署定义端点提供有关在 Spring 云数据流服务器中注册的部署的信息。以下主题提供了更多细节：

* [部署流定义](#api-guide-resources-stream-deployment-deploy)

* [取消部署流定义](#api-guide-resources-stream-deployment-undeploy)

* [取消部署所有流定义](#api-guide-resources-stream-deployment-undeploy-all)

* [更新部署的流](#api-guide-resources-stream-deployment-update)

* [回滚流定义](#api-guide-resources-stream-deployment-rollback)

* [获取舱单](#api-guide-resources-stream-deployment-manifest)

* [获取部署历史](#api-guide-resources-stream-deployment-history)

* [获取部署平台](#api-guide-resources-stream-deployment-platform-list)

* [缩放流定义](#api-guide-resources-stream-deployment-scale)

#### 44.7.1.部署流定义

流定义端点允许你部署单个流定义。你可以选择在请求主体中将应用程序参数作为属性传递。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-deployment-deploy-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-deploy-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-deploy-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-deploy-response-structure)

##### 请求结构

```
POST /streams/deployments/timelog HTTP/1.1
Content-Type: application/json
Content-Length: 36
Host: localhost:9393

{"app.time.timestamp.format":"YYYY"}
```

/流/部署/{timelog}

|Parameter|说明|
|---------|----------------------------------------------------|
|`timelog`|现有流定义的名称（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/timelog' -i -X POST \
    -H 'Content-Type: application/json' \
    -d '{"app.time.timestamp.format":"YYYY"}'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

#### 44.7.2.取消部署流定义

流定义端点允许你取消部署单个流定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-deployment-undeploy-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-undeploy-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-undeploy-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-undeploy-response-structure)

##### 请求结构

```
DELETE /streams/deployments/timelog HTTP/1.1
Host: localhost:9393
```

/流/部署/{timelog}

|Parameter|说明|
|---------|----------------------------------------------------|
|`timelog`|现有流定义的名称（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/timelog' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.7.3.取消部署所有流定义

流定义端点允许你取消部署所有单个流定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-deployment-undeploy-all-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-undeploy-all-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-undeploy-all-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-undeploy-all-response-structure)

##### 请求结构

```
DELETE /streams/deployments HTTP/1.1
Host: localhost:9393
```

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.7.4.更新部署的流

多亏了 Skipper，你可以更新部署的流，并提供额外的部署属性。

* [请求结构](#api-guide-resources-stream-definitions-deployment-update-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-update-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-update-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-update-response-structure)

##### 请求结构

```
POST /streams/deployments/update/timelog1 HTTP/1.1
Content-Type: application/json
Content-Length: 196
Host: localhost:9393

{"releaseName":"timelog1","packageIdentifier":{"repositoryName":"test","packageName":"timelog1","packageVersion":"1.0.0"},"updateProperties":{"app.time.timestamp.format":"YYYYMMDD"},"force":false}
```

/流/部署/更新/{timelog1}

|Parameter |说明|
|----------|----------------------------------------------------|
|`timelog1`|现有流定义的名称（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/update/timelog1' -i -X POST \
    -H 'Content-Type: application/json' \
    -d '{"releaseName":"timelog1","packageIdentifier":{"repositoryName":"test","packageName":"timelog1","packageVersion":"1.0.0"},"updateProperties":{"app.time.timestamp.format":"YYYYMMDD"},"force":false}'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

#### 44.7.5.回滚流定义

将流回滚到流的上一个或特定版本。

* [请求结构](#api-guide-resources-stream-definitions-deployment-rollback-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-rollback-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-rollback-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-rollback-response-structure)

##### 请求结构

```
POST /streams/deployments/rollback/timelog1/1 HTTP/1.1
Content-Type: application/json
Host: localhost:9393
```

/流/部署/回滚/{name}/{version}

|Parameter|说明|
|---------|----------------------------------------------------|
| `name`  |现有流定义的名称（必需的）|
|`version`|要回滚到的版本|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/rollback/timelog1/1' -i -X POST \
    -H 'Content-Type: application/json'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

#### 44.7.6.获取舱单

返回已发布版本的清单。对于具有依赖关系的包，清单包括这些依赖关系的内容。

* [请求结构](#api-guide-resources-stream-definitions-deployment-manifest-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-manifest-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-manifest-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-manifest-response-structure)

##### 请求结构

```
GET /streams/deployments/manifest/timelog1/1 HTTP/1.1
Content-Type: application/json
Host: localhost:9393
```

/流/部署/清单/{name}/{version}

|Parameter|说明|
|---------|----------------------------------------------------|
| `name`  |现有流定义的名称（必需的）|
|`version`|流的版本|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/manifest/timelog1/1' -i -X GET \
    -H 'Content-Type: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.7.7.获取部署历史

获取数据流的部署历史。

* [请求结构](#api-guide-resources-stream-definitions-deployment-history-request-structure)

* [示例请求](#api-guide-resources-stream-definitions-deployment-history-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-history-response-structure)

##### 请求结构

```
GET /streams/deployments/history/timelog1 HTTP/1.1
Content-Type: application/json
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/history/timelog1' -i -X GET \
    -H 'Content-Type: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 162

[ {
  "name" : null,
  "version" : 0,
  "info" : null,
  "pkg" : null,
  "configValues" : {
    "raw" : null
  },
  "manifest" : null,
  "platformName" : null
} ]
```

#### 44.7.8.获取部署平台

检索受支持的部署平台的列表。

* [请求结构](#api-guide-resources-stream-definitions-deployment-platform-list-request-structure)

* [示例请求](#api-guide-resources-stream-definitions-deployment-history-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-history-response-structure)

##### 请求结构

```
GET /streams/deployments/platform/list HTTP/1.1
Content-Type: application/json
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/platform/list' -i -X GET \
    -H 'Content-Type: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 106

[ {
  "id" : null,
  "name" : "default",
  "type" : "local",
  "description" : null,
  "options" : [ ]
} ]
```

#### 44.7.9.缩放流定义

流定义端点允许你在流定义中扩展单个应用程序。你可以选择在请求主体中将应用程序参数作为属性传递。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-definitions-deployment-scale-request-structure)

* [请求参数](#api-guide-resources-stream-definitions-deployment-scale-request-parameters)

* [示例请求](#api-guide-resources-stream-definitions-deployment-scale-example-request)

* [反应结构](#api-guide-resources-stream-definitions-deployment-scale-response-structure)

##### 请求结构

```
POST /streams/deployments/scale/timelog/log/instances/1 HTTP/1.1
Content-Type: application/json
Content-Length: 36
Host: localhost:9393

{"app.time.timestamp.format":"YYYY"}
```

/streams/deployments/scale/{streamname}/{appname}/instance/{count}

| Parameter  |说明|
|------------|------------------------------------------------------------------|
|`streamName`|现有流定义的名称（必需的）|
| `appName`  |在流应用程序中按比例命名|
|  `count`   |选定的流应用程序的实例数（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/streams/deployments/scale/timelog/log/instances/1' -i -X POST \
    -H 'Content-Type: application/json' \
    -d '{"app.time.timestamp.format":"YYYY"}'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

### 44.8.任务定义

任务定义端点提供了有关在 Spring 云数据流服务器中注册的任务定义的信息。以下主题提供了更多细节：

* [创建新的任务定义](#api-guide-resources-task-definitions-creating)

* [列出所有任务定义](#api-guide-resources-task-definitions-list)

* [检索任务定义详细信息](#api-guide-resources-task-definition-detail)

* [删除任务定义](#api-guide-resources-delete-task-definition)

#### 44.8.1.创建新的任务定义

任务定义端点允许你创建一个新的任务定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-task-definitions-creating-request-structure)

* [请求参数](#api-guide-resources-stream-task-definitions-creating-request-parameters)

* [示例请求](#api-guide-resources-stream-task-definitions-creating-example-request)

* [反应结构](#api-guide-resources-stream-task-definitions-creating-response-structure)

##### 请求结构

```
POST /tasks/definitions HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

name=my-task&definition=timestamp+--format%3D%27YYYY+MM+DD%27&description=Demo+task+definition+for+testing
```

##### 请求参数

|  Parameter  |说明|
|-------------|------------------------------------------------|
|   `name`    |创建的任务定义的名称|
|`definition` |任务的定义，使用数据流 DSL|
|`description`|任务定义的描述|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/definitions' -i -X POST \
    -d 'name=my-task&definition=timestamp+--format%3D%27YYYY+MM+DD%27&description=Demo+task+definition+for+testing'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 342

{
  "name" : "my-task",
  "dslText" : "timestamp --format='YYYY MM DD'",
  "description" : "Demo task definition for testing",
  "composed" : false,
  "composedTaskElement" : false,
  "lastTaskExecution" : null,
  "status" : "UNKNOWN",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/definitions/my-task"
    }
  }
}
```

#### 44.8.2.列出所有任务定义

任务定义端点允许你获得所有任务定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-task-definitions-list-request-structure)

* [请求参数](#api-guide-resources-stream-task-definitions-list-request-parameters)

* [示例请求](#api-guide-resources-stream-task-definitions-list-example-request)

* [反应结构](#api-guide-resources-stream-task-definitions-list-response-structure)

##### 请求结构

```
GET /tasks/definitions?page=0&size=10&sort=taskName%2CASC&search=&manifest=true HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter |说明|
|----------|-------------------------------------------------------------------------------|
|  `page`  |基于零的页码（可选）|
|  `size`  |请求的页面大小（可选）|
| `search` |对名称执行的搜索字符串（可选）|
|  `sort`  |列表中的排序（可选）|
|`manifest`|将任务清单包含到最新任务执行中的标志（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/definitions?page=0&size=10&sort=taskName%2CASC&search=&manifest=true' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 689

{
  "_embedded" : {
    "taskDefinitionResourceList" : [ {
      "name" : "my-task",
      "dslText" : "timestamp --format='YYYY MM DD'",
      "description" : "Demo task definition for testing",
      "composed" : false,
      "composedTaskElement" : false,
      "lastTaskExecution" : null,
      "status" : "UNKNOWN",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/tasks/definitions/my-task"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/definitions?page=0&size=10&sort=taskName,asc"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.8.3.检索任务定义详细信息

任务定义端点允许你获得单个任务定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-task-definitions-detail-request-structure)

* [请求参数](#api-guide-resources-stream-task-definitions-detail-request-parameters)

* [示例请求](#api-guide-resources-stream-task-definitions-detail-example-request)

* [反应结构](#api-guide-resources-stream-task-definitions-detail-response-structure)

##### 请求结构

```
GET /tasks/definitions/my-task?manifest=true HTTP/1.1
Host: localhost:9393
```

/tasks/definitions/{my-task}

|Parameter|说明|
|---------|--------------------------------------------------|
|`my-task`|现有任务定义的名称（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/definitions/my-task?manifest=true' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 342

{
  "name" : "my-task",
  "dslText" : "timestamp --format='YYYY MM DD'",
  "description" : "Demo task definition for testing",
  "composed" : false,
  "composedTaskElement" : false,
  "lastTaskExecution" : null,
  "status" : "UNKNOWN",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/definitions/my-task"
    }
  }
}
```

#### 44.8.4.删除任务定义

任务定义端点允许你删除单个任务定义。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-delete-task-definition-request-structure)

* [请求参数](#api-guide-resources-delete-task-definition-request-parameters)

* [示例请求](#api-guide-resources-delete-task-definition-example-request)

* [反应结构](#api-guide-resources-delete-task-definition-response-structure)

##### 请求结构

```
DELETE /tasks/definitions/my-task?cleanup=true HTTP/1.1
Host: localhost:9393
```

/tasks/definitions/{my-task}

|Parameter|说明|
|---------|--------------------------------------------------|
|`my-task`|现有任务定义的名称（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/definitions/my-task?cleanup=true' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

### 44.9.任务调度程序

任务调度程序端点提供了有关已注册到调度程序实现中的任务调度的信息。以下主题提供了更多细节：

* [创建新的任务时间表](#api-guide-resources-task-schedule-creating)

* [列出所有时间表](#api-guide-resources-task-schedule-list)

* [列出过滤的时间表](#api-guide-resources-task-schedule-filtered-list)

* [删除任务时间表](#api-guide-resources-task-delete-schedule)

#### 44.9.1.创建新的任务时间表

任务调度端点允许你创建一个新的任务调度。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-task-schedule-creating-request-structure)

* [请求参数](#api-guide-resources-stream-task-schedule-creating-request-parameters)

* [示例请求](#api-guide-resources-stream-task-schedule-creating-example-request)

* [反应结构](#api-guide-resources-stream-task-schedule-creating-response-structure)

##### 请求结构

```
POST /tasks/schedules HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

scheduleName=myschedule&taskDefinitionName=mytaskname&properties=scheduler.cron.expression%3D00+22+17+%3F+*&arguments=--foo%3Dbar
```

##### 请求参数

|     Parameter      |说明|
|--------------------|----------------------------------------------------------------|
|   `scheduleName`   |创建的计划的名称|
|`taskDefinitionName`|要调度的任务定义的名称|
|    `properties`    |调度和启动任务所需的属性|
|    `arguments`     |用于启动任务的命令行参数|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/schedules' -i -X POST \
    -d 'scheduleName=myschedule&taskDefinitionName=mytaskname&properties=scheduler.cron.expression%3D00+22+17+%3F+*&arguments=--foo%3Dbar'
```

##### 反应结构

```
HTTP/1.1 201 Created
```

#### 44.9.2.列出所有时间表

任务调度端点允许你获取所有任务调度。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-task-schedule-list-request-structure)

* [请求参数](#api-guide-resources-stream-task-schedule-list-request-parameters)

* [示例请求](#api-guide-resources-stream-task-schedule-list-example-request)

* [反应结构](#api-guide-resources-stream-task-schedule-list-response-structure)

##### 请求结构

```
GET /tasks/schedules?page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/schedules?page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 587

{
  "_embedded" : {
    "scheduleInfoResourceList" : [ {
      "scheduleName" : "FOO",
      "taskDefinitionName" : "BAR",
      "scheduleProperties" : {
        "scheduler.AAA.spring.cloud.scheduler.cron.expression" : "00 41 17 ? * *"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/tasks/schedules/FOO"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/schedules?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.9.3.列出过滤的时间表

任务调度端点允许你获得具有指定任务定义名称的所有任务调度。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-task-schedule-list-filtered-request-structure)

* [请求参数](#api-guide-resources-stream-task-schedule-list-filtered-request-parameters)

* [示例请求](#api-guide-resources-stream-task-schedule-list-filtered-example-request)

* [反应结构](#api-guide-resources-stream-task-schedule-list-filtered-response-structure)

##### 请求结构

```
GET /tasks/schedules/instances/FOO?page=0&size=10 HTTP/1.1
Host: localhost:9393
```

/tasks/schedues/instance/{task-definition-name}

|      Parameter       |说明|
|----------------------|------------------------------------------------------------------|
|`task-definition-name`|根据指定的任务定义（必需的）筛选计划|

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/schedules/instances/FOO?page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 599

{
  "_embedded" : {
    "scheduleInfoResourceList" : [ {
      "scheduleName" : "FOO",
      "taskDefinitionName" : "BAR",
      "scheduleProperties" : {
        "scheduler.AAA.spring.cloud.scheduler.cron.expression" : "00 41 17 ? * *"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/tasks/schedules/FOO"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/schedules/instances/FOO?page=0&size=1"
    }
  },
  "page" : {
    "size" : 1,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.9.4.删除任务时间表

任务调度端点允许你删除单个任务调度。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-delete-task-schedule-request-structure)

* [请求参数](#api-guide-resources-delete-task-schedule-request-parameters)

* [示例请求](#api-guide-resources-delete-task-schedule-example-request)

* [反应结构](#api-guide-resources-delete-task-schedule-response-structure)

##### 请求结构

```
DELETE /tasks/schedules/mytestschedule HTTP/1.1
Host: localhost:9393
```

/tasks/schedues/{schedulename}

|  Parameter   |说明|
|--------------|-------------------------------------------|
|`scheduleName`|现有时间表的名称（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/schedules/mytestschedule' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

### 44.10.任务验证

任务验证端点允许你在任务定义中验证应用程序。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-validation-request-structure)

* [路径参数](#api-guide-resources-task-validation-path-parameters)

* [示例请求](#api-guide-resources-task-validation-example-request)

* [反应结构](#api-guide-resources-task-validation-response-structure)

#### 44.10.1.请求结构

```
GET /tasks/validation/taskC HTTP/1.1
Host: localhost:9393
```

#### 44.10.2.路径参数

/tasks/validation/{name}

|Parameter|说明|
|---------|--------------------------------------------------------|
| `name`  |要验证的任务定义的名称（必需的）|

#### 44.10.3.示例请求

```
$ curl 'http://localhost:9393/tasks/validation/taskC' -i -X GET
```

#### 44.10.4.反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 144

{
  "appName" : "taskC",
  "dsl" : "timestamp --format='yyyy MM dd'",
  "description" : "",
  "appStatuses" : {
    "task:taskC" : "valid"
  }
}
```

### 44.11.任务执行

任务执行端点提供了有关在 Spring 云数据流服务器上注册的任务执行的信息。以下主题提供了更多细节：

* [启动一项任务](#api-guide-resources-task-executions-launching)

* [停止任务](#api-guide-resources-task-executions-stopping)

* [列出所有的任务执行](#api-guide-resources-task-executions-list)

* [用指定的任务名列出所有的任务执行](#api-guide-resources-task-executions-list-by-name)

* [任务执行细节](#api-guide-resources-task-executions-detail)

* [删除任务执行](#api-guide-resources-task-executions-delete)

* [任务执行当前计数](#api-guide-resources-task-executions-current-count)

#### 44.11.1.启动一项任务

通过请求创建新的任务执行来启动任务。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-launching-request-structure)

* [请求参数](#api-guide-resources-task-executions-launching-request-parameters)

* [示例请求](#api-guide-resources-task-executions-launching-example-request)

* [反应结构](#api-guide-resources-task-executions-launching-response-structure)

##### 请求结构

```
POST /tasks/executions HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

name=taskA&properties=app.my-task.foo%3Dbar%2Cdeployer.my-task.something-else%3D3&arguments=--server.port%3D8080+--foo%3Dbar
```

##### 请求参数

| Parameter  |说明|
|------------|----------------------------------------------------------|
|   `name`   |要启动的任务定义的名称|
|`properties`|启动时要使用的应用程序和部署程序属性|
|`arguments` |要传递给任务的命令行参数|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/executions' -i -X POST \
    -d 'name=taskA&properties=app.my-task.foo%3Dbar%2Cdeployer.my-task.something-else%3D3&arguments=--server.port%3D8080+--foo%3Dbar'
```

##### 反应结构

```
HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 1

1
```

#### 44.11.2.停止任务

停止任务是通过发布现有任务执行的 ID 来完成的。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-stopping-request-structure)

* [路径参数](#api-guide-resources-task-executions-stopping-path-parameters)

* [请求参数](#api-guide-resources-task-executions-stopping-request-parameters)

* [示例请求](#api-guide-resources-task-executions-stopping-example-request)

* [反应结构](#api-guide-resources-task-executions-stopping-response-structure)

##### 请求结构

```
POST /tasks/executions/1 HTTP/1.1
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

platform=default
```

##### 路径参数

/任务/执行/{id}

|Parameter|说明|
|---------|------------------------------------------------|
|  `id`   |现有任务执行的 ID（必需的）|

##### 请求参数

|Parameter |说明|
|----------|---------------------------------------------------------|
|`platform`|与任务执行相关联的平台（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/executions/1' -i -X POST \
    -d 'platform=default'
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.11.3.列出所有的任务执行

Task Executions Endpoint 允许你列出所有的任务执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-list-request-structure)

* [请求参数](#api-guide-resources-task-executions-list-request-parameters)

* [示例请求](#api-guide-resources-task-executions-list-example-request)

* [反应结构](#api-guide-resources-task-executions-list-response-structure)

##### 请求结构

```
GET /tasks/executions?page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/executions?page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 2711

{
  "_embedded" : {
    "taskExecutionResourceList" : [ {
      "executionId" : 2,
      "exitCode" : null,
      "taskName" : "taskB",
      "startTime" : null,
      "endTime" : null,
      "exitMessage" : null,
      "arguments" : [ ],
      "jobExecutionIds" : [ ],
      "errorMessage" : null,
      "externalExecutionId" : "taskB-7939f7fe-40a8-438c-a00c-4b2e041a42a7",
      "parentExecutionId" : null,
      "resourceUrl" : "org.springframework.cloud.task.app:timestamp-task:jar:1.2.0.RELEASE",
      "appProperties" : {
        "management.metrics.tags.service" : "task-application",
        "timestamp.format" : "yyyy MM dd",
        "spring.datasource.username" : null,
        "spring.datasource.url" : null,
        "spring.datasource.driverClassName" : null,
        "management.metrics.tags.application" : "${spring.cloud.task.name:unknown}-${spring.cloud.task.executionid:unknown}",
        "spring.cloud.task.name" : "taskB"
      },
      "deploymentProperties" : {
        "app.my-task.foo" : "bar",
        "deployer.my-task.something-else" : "3"
      },
      "platformName" : "default",
      "taskExecutionStatus" : "UNKNOWN",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/tasks/executions/2"
        }
      }
    }, {
      "executionId" : 1,
      "exitCode" : null,
      "taskName" : "taskA",
      "startTime" : null,
      "endTime" : null,
      "exitMessage" : null,
      "arguments" : [ ],
      "jobExecutionIds" : [ ],
      "errorMessage" : null,
      "externalExecutionId" : "taskA-37a1d8dd-9c33-4080-859f-899ed0e91b84",
      "parentExecutionId" : null,
      "resourceUrl" : "org.springframework.cloud.task.app:timestamp-task:jar:1.2.0.RELEASE",
      "appProperties" : {
        "management.metrics.tags.service" : "task-application",
        "timestamp.format" : "yyyy MM dd",
        "spring.datasource.username" : null,
        "spring.datasource.url" : null,
        "spring.datasource.driverClassName" : null,
        "management.metrics.tags.application" : "${spring.cloud.task.name:unknown}-${spring.cloud.task.executionid:unknown}",
        "spring.cloud.task.name" : "taskA"
      },
      "deploymentProperties" : {
        "app.my-task.foo" : "bar",
        "deployer.my-task.something-else" : "3"
      },
      "platformName" : "default",
      "taskExecutionStatus" : "UNKNOWN",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/tasks/executions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/executions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.11.4.用指定的任务名列出所有的任务执行

Task Executions Endpoint 允许你使用指定的任务名称列出任务执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-list-by-name-request-structure)

* [请求参数](#api-guide-resources-task-executions-list-by-name-request-parameters)

* [示例请求](#api-guide-resources-task-executions-list-by-name-example-request)

* [反应结构](#api-guide-resources-task-executions-list-by-name-response-structure)

##### 请求结构

```
GET /tasks/executions?name=taskB&page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|
| `name`  |与任务执行相关联的名称|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/executions?name=taskB&page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1492

{
  "_embedded" : {
    "taskExecutionResourceList" : [ {
      "executionId" : 2,
      "exitCode" : null,
      "taskName" : "taskB",
      "startTime" : null,
      "endTime" : null,
      "exitMessage" : null,
      "arguments" : [ ],
      "jobExecutionIds" : [ ],
      "errorMessage" : null,
      "externalExecutionId" : "taskB-7939f7fe-40a8-438c-a00c-4b2e041a42a7",
      "parentExecutionId" : null,
      "resourceUrl" : "org.springframework.cloud.task.app:timestamp-task:jar:1.2.0.RELEASE",
      "appProperties" : {
        "management.metrics.tags.service" : "task-application",
        "timestamp.format" : "yyyy MM dd",
        "spring.datasource.username" : null,
        "spring.datasource.url" : null,
        "spring.datasource.driverClassName" : null,
        "management.metrics.tags.application" : "${spring.cloud.task.name:unknown}-${spring.cloud.task.executionid:unknown}",
        "spring.cloud.task.name" : "taskB"
      },
      "deploymentProperties" : {
        "app.my-task.foo" : "bar",
        "deployer.my-task.something-else" : "3"
      },
      "platformName" : "default",
      "taskExecutionStatus" : "UNKNOWN",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/tasks/executions/2"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/executions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.11.5.任务执行细节

Task Executions Endpoint 允许你获得有关任务执行的详细信息。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-detail-request-structure)

* [请求参数](#api-guide-resources-task-executions-detail-request-parameters)

* [示例请求](#api-guide-resources-task-executions-detail-example-request)

* [反应结构](#api-guide-resources-task-executions-detail-response-structure)

##### 请求结构

```
GET /tasks/executions/1 HTTP/1.1
Host: localhost:9393
```

/任务/执行/{id}

|Parameter|说明|
|---------|-----------------------------------------------|
|  `id`   |现有任务执行的 ID（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/executions/1' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1085

{
  "executionId" : 1,
  "exitCode" : null,
  "taskName" : "taskA",
  "startTime" : null,
  "endTime" : null,
  "exitMessage" : null,
  "arguments" : [ ],
  "jobExecutionIds" : [ ],
  "errorMessage" : null,
  "externalExecutionId" : "taskA-37a1d8dd-9c33-4080-859f-899ed0e91b84",
  "parentExecutionId" : null,
  "resourceUrl" : "org.springframework.cloud.task.app:timestamp-task:jar:1.2.0.RELEASE",
  "appProperties" : {
    "management.metrics.tags.service" : "task-application",
    "timestamp.format" : "yyyy MM dd",
    "spring.datasource.username" : null,
    "spring.datasource.url" : null,
    "spring.datasource.driverClassName" : null,
    "management.metrics.tags.application" : "${spring.cloud.task.name:unknown}-${spring.cloud.task.executionid:unknown}",
    "spring.cloud.task.name" : "taskA"
  },
  "deploymentProperties" : {
    "app.my-task.foo" : "bar",
    "deployer.my-task.something-else" : "3"
  },
  "platformName" : "default",
  "taskExecutionStatus" : "UNKNOWN",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/tasks/executions/1"
    }
  }
}
```

#### 44.11.6.删除任务执行

任务执行端点允许你：

* 清理用于部署任务的资源

* 从持久性存储中删除相关的任务数据以及可能相关的 Spring 批处理作业数据

|   |清理实现（第一个选项）是特定于平台的。这两个操作都可以一次或单独触发<br/>。|
|---|---------------------------------------------------------------------------------------------------------------------------|

以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-delete-request-structure)

* [请求参数](#api-guide-resources-task-executions-delete-request-parameters)

* [示例请求](#api-guide-resources-task-executions-delete-example-request)

* [反应结构](#api-guide-resources-task-executions-delete-response-structure)

请参阅以下有关[删除任务执行数据](#api-guide-resources-task-executions-delete-multiple-and-task-data)的部分。

##### 请求结构

```
DELETE /tasks/executions/1,2?action=CLEANUP,REMOVE_DATA HTTP/1.1
Host: localhost:9393
```

/任务/执行/{ids}

|Parameter|说明|
|---------|-----------------------------------------------------|
|  `ids`  |提供 2 个逗号分隔的任务执行 ID 值。|

|   |你必须提供实际存在的任务执行 ID。否则，将返回`404`（未找到）HTTP 状态。<br/>在提交多个任务执行 ID 的情况下，单个任务执行 ID 的无效将导致整个请求失败，<br/>不执行任何操作。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

##### 请求参数

这个端点支持一个名为**行动**的可选请求参数。它是一个枚举，支持以下值：

* CLEANUP

* 删除 \_ 数据

|Parameter|说明|
|---------|-----------------------------------------------------------|
|`action` |同时使用两个动作清理和删除 \_data。|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/executions/1,2?action=CLEANUP,REMOVE_DATA' -i -X DELETE
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.11.7.删除任务执行数据

你不仅可以清理用于部署任务的资源，还可以从底层持久性存储中删除与任务执行相关的数据。此外，如果一个任务执行与一个或多个批处理作业执行相关联，那么这些执行也会被删除。

下面的示例说明了如何使用多个任务执行 ID 和多个动作来发出请求：

```
$ curl 'http://localhost:9393/tasks/executions/1,2?action=CLEANUP,REMOVE_DATA' -i -X DELETE
```

/任务/执行/{ids}

|Parameter|说明|
|---------|-----------------------------------------------------|
|  `ids`  |提供 2 个逗号分隔的任务执行 ID 值。|

|Parameter|说明|
|---------|-----------------------------------------------------------|
|`action` |同时使用两个动作清理和删除 \_data。|

|   |当使用`REMOVE_DATA`动作参数从持久性存储中删除数据时，你必须提供表示父任务执行的<br/>任务执行 ID。当你提供子任务执行（作为组合任务的一部分执行）时，将返回<br/>a`400`（错误请求）HTTP 状态。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |当删除大量的任务执行时，一些数据库类型限制`IN`子句中的条目数量（云数据流用来删除任务执行关系的方法）。<br/> Spring 云数据流支持 SQL Server 的分块删除（最多 2100 个条目）和 Oracle DBS（最多 1000 个条目）。<br/>然而， Spring 云数据流允许用户设置自己的分块因子。要做到这一点，将`spring.cloud.dataflow.task.executionDeleteChunkSize`属性设置为适当的块大小。<br/>默认值为`0`，这意味着 Spring 云数据流不会将块任务执行删除（Oracle 和 SQL Server 数据库除外）。|
|---| |

#### 44.11.8.任务执行当前计数

Task Executions Current Endpoint 允许你检索当前运行的执行次数。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-executions-current-count-request-structure)

* [请求参数](#api-guide-resources-task-executions-current-count-request-parameters)

* [示例请求](#api-guide-resources-task-executions-current-count-example-request)

* [反应结构](#api-guide-resources-task-executions-current-count-response-structure)

##### 请求结构

ADOC-包括：/home/runner/work/ Spring-cloud-dataflow/ Spring-cloud-dataflow/ Spring-cloud-dataflow-DOCS/../ Spring-cloud-dataflow-classic-DOCS/target/genered-snippets/task-executions-documentation/launch-task-current-count/http-request.ADOC[]

##### 请求参数

这个端点没有请求参数。

##### 示例请求

ADOC-包括：/home/runner/work/ Spring-cloud-dataflow/ Spring-cloud-dataflow/ Spring-cloud-dataflow-DOCS/../ Spring-cloud-dataflow-classic-DOCS/target/generated-snippets/task-executions-documentation/launch-task-current/current-count/curl-request.ADOC[]

##### 反应结构

ADOC-包括：/home/runner/work/ Spring-cloud-dataflow/ Spring-cloud-dataflow/ Spring-cloud-dataflow-DOCS/../ Spring-cloud-dataflow-classic-DOCS/target/generated-snippets/task-executions-documentation/launch-task-current-current-count/http-response.ADOC[]

### 44.12.工作执行

作业执行端点提供了关于在 Spring 云数据流服务器上注册的作业执行的信息。以下主题提供了更多细节：

* [列出所有的工作执行](#api-guide-resources-job-executions-list)

* [列出所有未包含步骤执行的作业执行](#api-guide-resources-job-executions-thin-job-execution-list)

* [用指定的作业名称列出所有作业执行](#api-guide-resources-job-executions-job-execution-info-only-list-by-name)

* [列出所有具有指定作业名的作业执行，但不包括步骤执行](#api-guide-resources-job-executions-thin-job-execution-info-only-list-by-name)

* [列出指定日期范围内不包括步骤执行的所有作业执行](#api-guide-resources-job-executions-thin-job-execution-info-only-list-by-date)

* [列出指定作业实例 ID 的所有作业执行，其中不包括步骤执行](#api-guide-resources-job-executions-thin-job-execution-info-only-list-by-job-instance-id)

* [列出指定任务执行 ID 的所有作业执行，但不包括步骤执行](#api-guide-resources-job-executions-thin-job-execution-info-only-list-by-task-execution-id)

* [作业执行细节](#api-guide-resources-job-executions-detail)

* [停止作业执行](#api-guide-resources-job-executions-stop)

* [重新启动作业执行](#api-guide-resources-job-executions-restart)

#### 44.12.1.列出所有的工作执行

作业执行端点允许你列出所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-list-request-structure)

* [请求参数](#api-guide-resources-job-executions-list-request-parameters)

* [示例请求](#api-guide-resources-job-executions-list-example-request)

* [反应结构](#api-guide-resources-job-executions-list-response-structure)

##### 请求结构

```
GET /jobs/executions?page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions?page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 3066

{
  "_embedded" : {
    "jobExecutionResourceList" : [ {
      "executionId" : 2,
      "stepExecutionCount" : 0,
      "jobId" : 2,
      "taskExecutionId" : 2,
      "name" : "DOCJOB1",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "duration" : "00:00:00",
      "jobExecution" : {
        "id" : 2,
        "version" : 1,
        "jobParameters" : {
          "parameters" : { }
        },
        "jobInstance" : {
          "id" : 2,
          "jobName" : "DOCJOB1",
          "version" : null
        },
        "stepExecutions" : [ ],
        "status" : "STOPPED",
        "startTime" : "2022-01-18T18:54:42.193+0000",
        "createTime" : "2022-01-18T18:54:42.192+0000",
        "endTime" : null,
        "lastUpdated" : "2022-01-18T18:54:42.193+0000",
        "exitStatus" : {
          "exitCode" : "UNKNOWN",
          "exitDescription" : ""
        },
        "executionContext" : {
          "dirty" : false,
          "empty" : true,
          "values" : [ ]
        },
        "failureExceptions" : [ ],
        "jobConfigurationName" : null,
        "allFailureExceptions" : [ ]
      },
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : true,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : true,
      "timeZone" : "UTC",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/executions/2"
        }
      }
    }, {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "duration" : "00:00:00",
      "jobExecution" : {
        "id" : 1,
        "version" : 2,
        "jobParameters" : {
          "parameters" : { }
        },
        "jobInstance" : {
          "id" : 1,
          "jobName" : "DOCJOB",
          "version" : null
        },
        "stepExecutions" : [ ],
        "status" : "STOPPING",
        "startTime" : "2022-01-18T18:54:42.189+0000",
        "createTime" : "2022-01-18T18:54:42.188+0000",
        "endTime" : null,
        "lastUpdated" : "2022-01-18T18:54:42.260+0000",
        "exitStatus" : {
          "exitCode" : "UNKNOWN",
          "exitDescription" : ""
        },
        "executionContext" : {
          "dirty" : false,
          "empty" : true,
          "values" : [ ]
        },
        "failureExceptions" : [ ],
        "jobConfigurationName" : null,
        "allFailureExceptions" : [ ]
      },
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : false,
      "timeZone" : "UTC",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/executions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/executions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.2.列出所有未包含步骤执行的作业执行

作业执行端点允许你列出不包括步骤执行的所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-thin-job-execution-list-request-structure)

* [请求参数](#api-guide-resources-job-executions-thin-job-execution-list-request-parameters)

* [示例请求](#api-guide-resources-job-executions-thin-job-execution-list-example-request)

* [反应结构](#api-guide-resources-job-executions-thin-job-execution-list-response-structure)

##### 请求结构

```
GET /jobs/thinexecutions?page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/thinexecutions?page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1604

{
  "_embedded" : {
    "jobExecutionThinResourceList" : [ {
      "executionId" : 2,
      "stepExecutionCount" : 0,
      "jobId" : 2,
      "taskExecutionId" : 2,
      "instanceId" : 2,
      "name" : "DOCJOB1",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.193+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : true,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : true,
      "timeZone" : "UTC",
      "status" : "STOPPED",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/2"
        }
      }
    }, {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "instanceId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.189+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : false,
      "stoppable" : true,
      "defined" : false,
      "timeZone" : "UTC",
      "status" : "STARTED",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/thinexecutions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.3.用指定的作业名称列出所有作业执行

作业执行端点允许你列出所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-list-by-name-request-structure)

* [请求参数](#api-guide-resources-job-executions-list-by-name-request-parameters)

* [示例请求](#api-guide-resources-job-executions-list-by-name-example-request)

* [反应结构](#api-guide-resources-job-executions-list-by-name-response-structure)

##### 请求结构

```
GET /jobs/executions?name=DOCJOB&page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|------------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|
| `name`  |与作业执行相关联的名称|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions?name=DOCJOB&page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1669

{
  "_embedded" : {
    "jobExecutionResourceList" : [ {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "duration" : "00:00:00",
      "jobExecution" : {
        "id" : 1,
        "version" : 2,
        "jobParameters" : {
          "parameters" : { }
        },
        "jobInstance" : {
          "id" : 1,
          "jobName" : "DOCJOB",
          "version" : null
        },
        "stepExecutions" : [ ],
        "status" : "STOPPING",
        "startTime" : "2022-01-18T18:54:42.189+0000",
        "createTime" : "2022-01-18T18:54:42.188+0000",
        "endTime" : null,
        "lastUpdated" : "2022-01-18T18:54:42.260+0000",
        "exitStatus" : {
          "exitCode" : "UNKNOWN",
          "exitDescription" : ""
        },
        "executionContext" : {
          "dirty" : false,
          "empty" : true,
          "values" : [ ]
        },
        "failureExceptions" : [ ],
        "jobConfigurationName" : null,
        "allFailureExceptions" : [ ]
      },
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : false,
      "timeZone" : "UTC",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/executions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/executions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.4.列出所有具有指定作业名的作业执行，但不包括步骤执行

作业执行端点允许你列出所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-thin-list-by-name-request-structure)

* [请求参数](#api-guide-resources-job-executions-thin-list-by-name-request-parameters)

* [示例请求](#api-guide-resources-job-executions-thin-list-by-name-example-request)

* [反应结构](#api-guide-resources-job-executions-thin-list-by-name-response-structure)

##### 请求结构

```
GET /jobs/thinexecutions?name=DOCJOB&page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|------------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|
| `name`  |与作业执行相关联的名称|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/thinexecutions?name=DOCJOB&page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 943

{
  "_embedded" : {
    "jobExecutionThinResourceList" : [ {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "instanceId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.189+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : false,
      "timeZone" : "UTC",
      "status" : "STOPPING",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/thinexecutions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.5.列出指定日期范围内不包括步骤执行的所有作业执行

作业执行端点允许你列出所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-thin-list-by-date-request-structure)

* [请求参数](#api-guide-resources-job-executions-thin-list-by-date-request-parameters)

* [示例请求](#api-guide-resources-job-executions-thin-list-by-date-example-request)

* [反应结构](#api-guide-resources-job-executions-thin-list-by-date-response-structure)

##### 请求结构

```
GET /jobs/thinexecutions?page=0&size=10&fromDate=2000-09-24T17%3A00%3A45%2C000&toDate=2050-09-24T18%3A00%3A45%2C000 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter |说明|
|----------|----------------------------------------------------------------------------|
|  `page`  |基于零的页码（可选）|
|  `size`  |请求的页面大小（可选）|
|`fromDate`|以“yyyy-mm-dd’t”格式筛选来自起始日期的结果，格式为：mm:ss，sss|
| `toDate` |过滤结果到`to`日期，格式为“yyyy-mm-dd’t”hh:mm:ss，sss"|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/thinexecutions?page=0&size=10&fromDate=2000-09-24T17%3A00%3A45%2C000&toDate=2050-09-24T18%3A00%3A45%2C000' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1605

{
  "_embedded" : {
    "jobExecutionThinResourceList" : [ {
      "executionId" : 2,
      "stepExecutionCount" : 0,
      "jobId" : 2,
      "taskExecutionId" : 2,
      "instanceId" : 2,
      "name" : "DOCJOB1",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.193+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : true,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : true,
      "timeZone" : "UTC",
      "status" : "STOPPED",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/2"
        }
      }
    }, {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "instanceId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.189+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : false,
      "timeZone" : "UTC",
      "status" : "STOPPING",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/thinexecutions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.6.列出指定作业实例 ID 的所有作业执行，其中不包括步骤执行

作业执行端点允许你列出所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-thin-list-by-job-instance-id-request-structure)

* [请求参数](#api-guide-resources-job-executions-thin-list-by-job-instance-id-request-parameters)

* [示例请求](#api-guide-resources-job-executions-thin-list-by-job-instance-id-example-request)

* [反应结构](#api-guide-resources-job-executions-thin-list-by-job-instance-id-response-structure)

##### 请求结构

```
GET /jobs/thinexecutions?page=0&size=10&jobInstanceId=1 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|   Parameter   |说明|
|---------------|-------------------------------------|
|    `page`     |基于零的页码（可选）|
|    `size`     |请求的页面大小（可选）|
|`jobInstanceId`|根据作业实例 ID 筛选结果|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/thinexecutions?page=0&size=10&jobInstanceId=1' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 943

{
  "_embedded" : {
    "jobExecutionThinResourceList" : [ {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "instanceId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.189+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : false,
      "timeZone" : "UTC",
      "status" : "STOPPING",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/thinexecutions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.7.列出指定任务执行 ID 的所有作业执行，但不包括步骤执行

作业执行端点允许你列出所有作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-thin-list-by-task-execution-id-request-structure)

* [请求参数](#api-guide-resources-job-executions-thin-list-by-task-execution-id-request-parameters)

* [示例请求](#api-guide-resources-job-executions-thin-list-by-task-execution-id-example-request)

* [反应结构](#api-guide-resources-job-executions-thin-list-by-task-execution-id-response-structure)

##### 请求结构

```
GET /jobs/thinexecutions?page=0&size=10&taskExecutionId=1 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|    Parameter    |说明|
|-----------------|--------------------------------------|
|     `page`      |基于零的页码（可选）|
|     `size`      |请求的页面大小（可选）|
|`taskExecutionId`|通过任务执行 ID 过滤结果|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/thinexecutions?page=0&size=10&taskExecutionId=1' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 943

{
  "_embedded" : {
    "jobExecutionThinResourceList" : [ {
      "executionId" : 1,
      "stepExecutionCount" : 0,
      "jobId" : 1,
      "taskExecutionId" : 1,
      "instanceId" : 1,
      "name" : "DOCJOB",
      "startDate" : "2022-01-18",
      "startTime" : "18:54:42",
      "startDateTime" : "2022-01-18T18:54:42.189+0000",
      "duration" : "00:00:00",
      "jobParameters" : { },
      "jobParametersString" : "",
      "restartable" : false,
      "abandonable" : true,
      "stoppable" : false,
      "defined" : false,
      "timeZone" : "UTC",
      "status" : "STOPPING",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/thinexecutions/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/thinexecutions?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.12.8.作业执行细节

作业执行端点允许你获得有关作业执行的详细信息。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-detail-request-structure)

* [请求参数](#api-guide-resources-job-executions-detail-request-parameters)

* [示例请求](#api-guide-resources-job-executions-detail-example-request)

* [反应结构](#api-guide-resources-job-executions-detail-response-structure)

##### 请求结构

```
GET /jobs/executions/2 HTTP/1.1
Host: localhost:9393
```

/jobs/executions/{id}

|Parameter|说明|
|---------|----------------------------------------------|
|  `id`   |现有作业执行的 ID（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions/2' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1188

{
  "executionId" : 2,
  "stepExecutionCount" : 0,
  "jobId" : 2,
  "taskExecutionId" : 2,
  "name" : "DOCJOB1",
  "startDate" : "2022-01-18",
  "startTime" : "18:54:42",
  "duration" : "00:00:00",
  "jobExecution" : {
    "id" : 2,
    "version" : 1,
    "jobParameters" : {
      "parameters" : { }
    },
    "jobInstance" : {
      "id" : 2,
      "jobName" : "DOCJOB1",
      "version" : 0
    },
    "stepExecutions" : [ ],
    "status" : "STOPPED",
    "startTime" : "2022-01-18T18:54:42.193+0000",
    "createTime" : "2022-01-18T18:54:42.192+0000",
    "endTime" : null,
    "lastUpdated" : "2022-01-18T18:54:42.193+0000",
    "exitStatus" : {
      "exitCode" : "UNKNOWN",
      "exitDescription" : ""
    },
    "executionContext" : {
      "dirty" : false,
      "empty" : true,
      "values" : [ ]
    },
    "failureExceptions" : [ ],
    "jobConfigurationName" : null,
    "allFailureExceptions" : [ ]
  },
  "jobParameters" : { },
  "jobParametersString" : "",
  "restartable" : true,
  "abandonable" : true,
  "stoppable" : false,
  "defined" : true,
  "timeZone" : "UTC",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/executions/2"
    }
  }
}
```

#### 44.12.9.停止作业执行

作业执行端点允许你停止作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-stop-request-structure)

* [请求参数](#api-guide-resources-job-executions-stop-request-parameters)

* [示例请求](#api-guide-resources-job-executions-stop-example-request)

* [反应结构](#api-guide-resources-job-executions-stop-response-structure)

##### 请求结构

```
PUT /jobs/executions/1 HTTP/1.1
Accept: application/json
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

stop=true
```

/jobs/executions/{id}

|Parameter|说明|
|---------|----------------------------------------------|
|  `id`   |现有作业执行的 ID（必需的）|

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------------|
| `stop`  |如果设置为 true，则发送停止作业的信号|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions/1' -i -X PUT \
    -H 'Accept: application/json' \
    -d 'stop=true'
```

##### 反应结构

```
HTTP/1.1 200 OK
```

#### 44.12.10.重新启动作业执行

作业执行端点允许你重新启动作业执行。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-executions-restart-request-structure)

* [请求参数](#api-guide-resources-job-executions-restart-request-parameters)

* [示例请求](#api-guide-resources-job-executions-restart-example-request)

* [反应结构](#api-guide-resources-job-executions-restart-response-structure)

##### 请求结构

```
PUT /jobs/executions/2 HTTP/1.1
Accept: application/json
Host: localhost:9393
Content-Type: application/x-www-form-urlencoded

restart=true
```

/jobs/executions/{id}

|Parameter|说明|
|---------|----------------------------------------------|
|  `id`   |现有作业执行的 ID（必需的）|

##### 请求参数

|Parameter|说明|
|---------|----------------------------------------------|
|`restart`|如果设置为 true，则发送重启作业的信号|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions/2' -i -X PUT \
    -H 'Accept: application/json' \
    -d 'restart=true'
```

##### 反应结构

```
HTTP/1.1 200 OK
```

### 44.13.作业实例

作业实例端点提供了关于在 Spring 云数据流服务器上注册的作业实例的信息。以下主题提供了更多细节：

* [列出所有作业实例](#api-guide-resources-job-instances-list)

* [作业实例详细信息](#api-guide-resources-job-instances-detail)

#### 44.13.1.列出所有作业实例

作业实例端点允许你列出所有作业实例。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-instances-list-request-structure)

* [请求参数](#api-guide-resources-job-instances-list-request-parameters)

* [示例请求](#api-guide-resources-job-instances-list-example-request)

* [反应结构](#api-guide-resources-job-instances-list-response-structure)

##### 请求结构

```
GET /jobs/instances?name=DOCJOB&page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-----------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|
| `name`  |与作业实例关联的名称|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/instances?name=DOCJOB&page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1845

{
  "_embedded" : {
    "jobInstanceResourceList" : [ {
      "jobName" : "DOCJOB",
      "jobInstanceId" : 1,
      "jobExecutions" : [ {
        "executionId" : 1,
        "stepExecutionCount" : 0,
        "jobId" : 1,
        "taskExecutionId" : 1,
        "name" : "DOCJOB",
        "startDate" : "2022-01-18",
        "startTime" : "18:54:40",
        "duration" : "00:00:00",
        "jobExecution" : {
          "id" : 1,
          "version" : 1,
          "jobParameters" : {
            "parameters" : { }
          },
          "jobInstance" : {
            "id" : 1,
            "jobName" : "DOCJOB",
            "version" : 0
          },
          "stepExecutions" : [ ],
          "status" : "STARTED",
          "startTime" : "2022-01-18T18:54:40.048+0000",
          "createTime" : "2022-01-18T18:54:40.045+0000",
          "endTime" : null,
          "lastUpdated" : "2022-01-18T18:54:40.048+0000",
          "exitStatus" : {
            "exitCode" : "UNKNOWN",
            "exitDescription" : ""
          },
          "executionContext" : {
            "dirty" : false,
            "empty" : true,
            "values" : [ ]
          },
          "failureExceptions" : [ ],
          "jobConfigurationName" : null,
          "allFailureExceptions" : [ ]
        },
        "jobParameters" : { },
        "jobParametersString" : "",
        "restartable" : false,
        "abandonable" : false,
        "stoppable" : true,
        "defined" : false,
        "timeZone" : "UTC"
      } ],
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/instances/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/instances?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.13.2.作业实例详细信息

作业实例端点允许你列出所有作业实例。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-instances-detail-request-structure)

* [请求参数](#api-guide-resources-job-instances-detail-request-parameters)

* [示例请求](#api-guide-resources-job-instances-detail-example-request)

* [反应结构](#api-guide-resources-job-instances-detail-response-structure)

##### 请求结构

```
GET /jobs/instances/1 HTTP/1.1
Host: localhost:9393
```

/jobs/instances/{id}

|Parameter|说明|
|---------|---------------------------------------------|
|  `id`   |现有作业实例的 ID（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/instances/1' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1354

{
  "jobName" : "DOCJOB",
  "jobInstanceId" : 1,
  "jobExecutions" : [ {
    "executionId" : 1,
    "stepExecutionCount" : 0,
    "jobId" : 1,
    "taskExecutionId" : 1,
    "name" : "DOCJOB",
    "startDate" : "2022-01-18",
    "startTime" : "18:54:40",
    "duration" : "00:00:00",
    "jobExecution" : {
      "id" : 1,
      "version" : 1,
      "jobParameters" : {
        "parameters" : { }
      },
      "jobInstance" : {
        "id" : 1,
        "jobName" : "DOCJOB",
        "version" : 0
      },
      "stepExecutions" : [ ],
      "status" : "STARTED",
      "startTime" : "2022-01-18T18:54:40.048+0000",
      "createTime" : "2022-01-18T18:54:40.045+0000",
      "endTime" : null,
      "lastUpdated" : "2022-01-18T18:54:40.048+0000",
      "exitStatus" : {
        "exitCode" : "UNKNOWN",
        "exitDescription" : ""
      },
      "executionContext" : {
        "dirty" : false,
        "empty" : true,
        "values" : [ ]
      },
      "failureExceptions" : [ ],
      "jobConfigurationName" : null,
      "allFailureExceptions" : [ ]
    },
    "jobParameters" : { },
    "jobParametersString" : "",
    "restartable" : false,
    "abandonable" : false,
    "stoppable" : true,
    "defined" : false,
    "timeZone" : "UTC"
  } ],
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/instances/1"
    }
  }
}
```

### 44.14.作业步执行

Job Step Executions Endpoint 提供了关于在 Spring 云数据流服务器上注册的 Job Step Executions 的信息。以下主题提供了更多细节：

* [列出作业执行的所有步骤执行](#api-guide-resources-job-step-executions-list)

* [作业步骤执行细节](#api-guide-resources-job-step-execution-detail)

* [作业步骤执行进度](#api-guide-resources-job-step-execution-progress)

#### 44.14.1.列出作业执行的所有步骤执行

Job Step Executions Endpoint 允许你列出所有 Job Step Executions。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-step-executions-list-request-structure)

* [请求参数](#api-guide-resources-job-step-executions-list-request-parameters)

* [示例请求](#api-guide-resources-job-step-executions-list-example-request)

* [反应结构](#api-guide-resources-job-step-executions-list-response-structure)

##### 请求结构

```
GET /jobs/executions/1/steps?page=0&size=10 HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|Parameter|说明|
|---------|-------------------------------------|
| `page`  |基于零的页码（可选）|
| `size`  |请求的页面大小（可选）|

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions/1/steps?page=0&size=10' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1623

{
  "_embedded" : {
    "stepExecutionResourceList" : [ {
      "jobExecutionId" : 1,
      "stepExecution" : {
        "stepName" : "DOCJOB_STEP",
        "id" : 1,
        "version" : 0,
        "status" : "STARTING",
        "readCount" : 0,
        "writeCount" : 0,
        "commitCount" : 0,
        "rollbackCount" : 0,
        "readSkipCount" : 0,
        "processSkipCount" : 0,
        "writeSkipCount" : 0,
        "startTime" : "2022-01-18T18:52:35.974+0000",
        "endTime" : null,
        "lastUpdated" : "2022-01-18T18:52:35.974+0000",
        "executionContext" : {
          "dirty" : false,
          "empty" : true,
          "values" : [ ]
        },
        "exitStatus" : {
          "exitCode" : "EXECUTING",
          "exitDescription" : ""
        },
        "terminateOnly" : false,
        "filterCount" : 0,
        "failureExceptions" : [ ],
        "jobParameters" : {
          "parameters" : { }
        },
        "jobExecutionId" : 1,
        "skipCount" : 0,
        "summary" : "StepExecution: id=1, version=0, name=DOCJOB_STEP, status=STARTING, exitStatus=EXECUTING, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=0, rollbackCount=0"
      },
      "stepType" : "",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9393/jobs/executions/1/steps/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/executions/1/steps?page=0&size=10"
    }
  },
  "page" : {
    "size" : 10,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}
```

#### 44.14.2.作业步骤执行细节

Job Step Executions Endpoint 允许你获得有关 Job Step 执行的详细信息。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-step-execution-detail-request-structure)

* [请求参数](#api-guide-resources-job-step-execution-detail-request-parameters)

* [示例请求](#api-guide-resources-job-step-execution-detail-example-request)

* [反应结构](#api-guide-resources-job-step-execution-detail-response-structure)

##### 请求结构

```
GET /jobs/executions/1/steps/1 HTTP/1.1
Host: localhost:9393
```

/jobs/executions/{id}/stepid/{stepid}

|Parameter|说明|
|---------|----------------------------------------------------------------------------|
|  `id`   |现有作业执行的 ID（必需的）|
|`stepid` |特定作业执行的已有步骤执行的 ID（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions/1/steps/1' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 1173

{
  "jobExecutionId" : 1,
  "stepExecution" : {
    "stepName" : "DOCJOB_STEP",
    "id" : 1,
    "version" : 0,
    "status" : "STARTING",
    "readCount" : 0,
    "writeCount" : 0,
    "commitCount" : 0,
    "rollbackCount" : 0,
    "readSkipCount" : 0,
    "processSkipCount" : 0,
    "writeSkipCount" : 0,
    "startTime" : "2022-01-18T18:52:35.974+0000",
    "endTime" : null,
    "lastUpdated" : "2022-01-18T18:52:35.974+0000",
    "executionContext" : {
      "dirty" : false,
      "empty" : true,
      "values" : [ ]
    },
    "exitStatus" : {
      "exitCode" : "EXECUTING",
      "exitDescription" : ""
    },
    "terminateOnly" : false,
    "filterCount" : 0,
    "failureExceptions" : [ ],
    "jobParameters" : {
      "parameters" : { }
    },
    "jobExecutionId" : 1,
    "skipCount" : 0,
    "summary" : "StepExecution: id=1, version=0, name=DOCJOB_STEP, status=STARTING, exitStatus=EXECUTING, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=0, rollbackCount=0"
  },
  "stepType" : "",
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/executions/1/steps/1"
    }
  }
}
```

#### 44.14.3.作业步骤执行进度

Job Step Executions Endpoint 允许你获得有关 Job Step 执行进度的详细信息。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-job-step-execution-progress-request-structure)

* [请求参数](#api-guide-resources-job-step-execution-progress-request-parameters)

* [示例请求](#api-guide-resources-job-step-execution-progress-example-request)

* [反应结构](#api-guide-resources-job-step-execution-progress-response-structure)

##### 请求结构

```
GET /jobs/executions/1/steps/1/progress HTTP/1.1
Host: localhost:9393
```

/jobs/executions/{id}/steps/{stepid}/progress

|Parameter|说明|
|---------|----------------------------------------------------------------------------|
|  `id`   |现有作业执行的 ID（必需的）|
|`stepid` |特定作业执行的已有步骤执行的 ID（必需的）|

##### 请求参数

这个端点没有请求参数。

##### 示例请求

```
$ curl 'http://localhost:9393/jobs/executions/1/steps/1/progress' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 2676

{
  "stepExecution" : {
    "stepName" : "DOCJOB_STEP",
    "id" : 1,
    "version" : 0,
    "status" : "STARTING",
    "readCount" : 0,
    "writeCount" : 0,
    "commitCount" : 0,
    "rollbackCount" : 0,
    "readSkipCount" : 0,
    "processSkipCount" : 0,
    "writeSkipCount" : 0,
    "startTime" : "2022-01-18T18:52:35.974+0000",
    "endTime" : null,
    "lastUpdated" : "2022-01-18T18:52:35.974+0000",
    "executionContext" : {
      "dirty" : false,
      "empty" : true,
      "values" : [ ]
    },
    "exitStatus" : {
      "exitCode" : "EXECUTING",
      "exitDescription" : ""
    },
    "terminateOnly" : false,
    "filterCount" : 0,
    "failureExceptions" : [ ],
    "jobParameters" : {
      "parameters" : { }
    },
    "jobExecutionId" : 1,
    "skipCount" : 0,
    "summary" : "StepExecution: id=1, version=0, name=DOCJOB_STEP, status=STARTING, exitStatus=EXECUTING, readCount=0, filterCount=0, writeCount=0 readSkipCount=0, writeSkipCount=0, processSkipCount=0, commitCount=0, rollbackCount=0"
  },
  "stepExecutionHistory" : {
    "stepName" : "DOCJOB_STEP",
    "count" : 0,
    "commitCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "rollbackCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "readCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "writeCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "filterCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "readSkipCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "writeSkipCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "processSkipCount" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "duration" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    },
    "durationPerRead" : {
      "count" : 0,
      "min" : 0.0,
      "max" : 0.0,
      "standardDeviation" : 0.0,
      "mean" : 0.0
    }
  },
  "percentageComplete" : 0.5,
  "finished" : false,
  "duration" : 151.0,
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/jobs/executions/1/steps/1"
    }
  }
}
```

### 44.15.有关应用程序的运行时信息

你可以获得系统已知的运行应用程序的信息，无论是全局的还是单独的。以下主题提供了更多细节：

* [列出运行时的所有应用程序](#api-guide-resources-runtime-information-applications-listing-all)

* [查询单个应用程序的所有实例](#api-guide-resources-runtime-information-applications-querying-all-instances-single-app)

* [查询单个应用程序的单个实例](#api-guide-resources-runtime-information-applications-querying-single-instance-single-app)

#### 44.15.1.列出运行时的所有应用程序

要检索所有应用程序的所有实例的信息，请使用`GET`查询`/runtime/apps`端点。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-runtime-information-applications-listing-all-request-structure)

* [示例请求](#api-guide-resources-runtime-information-applications-listing-all-example-request)

* [反应结构](#api-guide-resources-runtime-information-applications-listing-all-response-structure)

##### 请求结构

```
GET /runtime/apps HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/runtime/apps' -i -X GET \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 209

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/runtime/apps?page=0&size=20"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}
```

#### 44.15.2.查询单个应用程序的所有实例

要检索有关特定应用程序的所有实例的信息，请使用`GET`查询`/runtime/apps/<appId>/instances`端点。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-runtime-information-applications-querying-all-instances-single-app-request-structure)

* [示例请求](#api-guide-resources-runtime-information-applications-querying-all-instances-single-app-example-request)

* [反应结构](#api-guide-resources-runtime-information-applications-querying-all-instances-single-app-response-structure)

##### 请求结构

```
GET /runtime/apps HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/runtime/apps' -i -X GET \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 209

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/runtime/apps?page=0&size=20"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}
```

#### 44.15.3.查询单个应用程序的单个实例

要检索有关特定应用程序的特定实例的信息，请使用`GET`查询`/runtime/apps/<appId>/instances/<instanceId>`端点。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-runtime-information-applications-querying-single-instance-single-app-request-structure)

* [示例请求](#api-guide-resources-runtime-information-applications-querying-single-instance-single-app-example-request)

* [反应结构](#api-guide-resources-runtime-information-applications-querying-single-instance-single-app-response-structure)

##### 请求结构

```
GET /runtime/apps HTTP/1.1
Accept: application/json
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/runtime/apps' -i -X GET \
    -H 'Accept: application/json'
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 209

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:9393/runtime/apps?page=0&size=20"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 0,
    "totalPages" : 0,
    "number" : 0
  }
}
```

### 44.16.流日志

你可以获得该流的应用程序日志，用于整个流或流中的特定应用程序。以下主题提供了更多细节：

* [根据流名获取应用程序的日志](#api-guide-resources-stream-logs-by-stream-name)

* [从流中获取特定应用程序的日志](#api-guide-resources-stream-logs-by-app-name)

#### 44.16.1.根据流名获取应用程序的日志

使用带有`/streams/logs/<streamName>`REST 端点的 http`GET`方法来检索给定流名称的所有应用程序日志。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-logs-by-stream-name-request-structure)

* [示例请求](#api-guide-resources-stream-logs-by-stream-name-example-request)

* [反应结构](#api-guide-resources-stream-logs-by-stream-name-response-structure)

##### 请求结构

```
GET /streams/logs/ticktock HTTP/1.1
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/streams/logs/ticktock' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 93

{
  "logs" : {
    "ticktock-time-v1" : "Logs-time",
    "ticktock-log-v1" : "Logs-log"
  }
}
```

#### 44.16.2.从流中获取特定应用程序的日志

要从流中检索特定应用程序的日志，请使用`GET`HTTP 方法查询`/streams/logs/<streamName>/<appName>`端点。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-stream-logs-by-app-name-request-structure)

* [示例请求](#api-guide-resources-stream-logs-by-app-name-example-request)

* [反应结构](#api-guide-resources-stream-logs-by-app-name-response-structure)

##### 请求结构

```
GET /streams/logs/ticktock/ticktock-log-v1 HTTP/1.1
Host: localhost:9393
```

##### 示例请求

```
$ curl 'http://localhost:9393/streams/logs/ticktock/ticktock-log-v1' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 55

{
  "logs" : {
    "ticktock-log-v1" : "Logs-log"
  }
}
```

### 44.17.任务日志

你可以获得特定任务执行的任务执行日志。

以下主题提供了更多细节：

* [获取任务执行日志](#api-guide-resources-stream-logs-by-task-id)

#### 44.17.1.获取任务执行日志

要检索任务执行的日志，请使用 http`GET`方法查询`/tasks/logs/<ExternalTaskExecutionId>`端点。以下主题提供了更多细节：

* [请求结构](#api-guide-resources-task-logs-by-task-id-request-structure)

* [请求参数](#api-guide-resources-task-logs-by-task-id-request-parameters)

* [示例请求](#api-guide-resources-task-logs-by-task-id-example-request)

* [反应结构](#api-guide-resources-task-logs-by-task-id-response-structure)

##### 请求结构

```
GET /tasks/logs/taskA-a5f123da-9a3b-42e8-b839-2eb441c561de?platformName=default HTTP/1.1
Host: localhost:9393
```

##### 请求参数

|  Parameter   |说明|
|--------------|----------------------------------------------|
|`platformName`|该任务启动的平台的名称。|

##### 示例请求

```
$ curl 'http://localhost:9393/tasks/logs/taskA-a5f123da-9a3b-42e8-b839-2eb441c561de?platformName=default' -i -X GET
```

##### 反应结构

```
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 10043

"stdout:\n2022-01-18 18:54:53.319  INFO 3680 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[email protected]4e515669: startup date [Tue Jan 18 18:54:53 UTC 2022]; root of context hierarchy\n2022-01-18 18:54:53.728  INFO 3680 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$b056ca48] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n\n  .   ____          _            __ _ _\n /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\\n( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\\n \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )\n  '  |____| .__|_| |_|_| |_\\__, | / / / /\n =========|_|==============|___/=/_/_/_/\n :: Spring Boot ::        (v1.5.2.RELEASE)\n\n2022-01-18 18:54:53.937  INFO 3680 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:8888\n2022-01-18 18:54:54.009  WARN 3680 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Could not locate PropertySource: I/O error on GET request for \"http://localhost:8888/timestamp-task/default\": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)\n2022-01-18 18:54:54.019  INFO 3680 --- [           main] o.s.c.t.a.t.TimestampTaskApplication     : No active profile set, falling back to default profiles: default\n2022-01-18 18:54:54.042  INFO 3680 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.spring[email protected]445b84c0: startup date [Tue Jan 18 18:54:54 UTC 2022]; parent: org.spring[email protected]4e515669\n2022-01-18 18:54:54.607  INFO 3680 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=1e36064f-ccbe-3d2f-9196-128427cc78a0\n2022-01-18 18:54:54.696  INFO 3680 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$b056ca48] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n2022-01-18 18:54:54.705  INFO 3680 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$943cc74b] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)\n2022-01-18 18:54:55.229  INFO 3680 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executing SQL script from class path resource [org/springframework/cloud/task/schema-h2.sql]\n2022-01-18 18:54:55.258  INFO 3680 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executed SQL script from class path resource [org/springframework/cloud/task/schema-h2.sql] in 29 ms.\n2022-01-18 18:54:55.582  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup\n2022-01-18 18:54:55.589  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'configurationPropertiesRebinder' has been autodetected for JMX exposure\n2022-01-18 18:54:55.589  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'environmentManager' has been autodetected for JMX exposure\n2022-01-18 18:54:55.591  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'refreshScope' has been autodetected for JMX exposure\n2022-01-18 18:54:55.592  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'environmentManager': registering with JMX server as MBean [taskA-a5f123da-9a3b-42e8-b839-2eb441c561de:name=environmentManager,type=EnvironmentManager]\n2022-01-18 18:54:55.603  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'refreshScope': registering with JMX server as MBean [taskA-a5f123da-9a3b-42e8-b839-2eb441c561de:name=refreshScope,type=RefreshScope]\n2022-01-18 18:54:55.613  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located managed bean 'configurationPropertiesRebinder': registering with JMX server as MBean [taskA-a5f123da-9a3b-42e8-b839-2eb441c561de:name=configurationPropertiesRebinder,context=445b84c0,type=ConfigurationPropertiesRebinder]\n2022-01-18 18:54:55.690  INFO 3680 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 0\n2022-01-18 18:54:55.702  WARN 3680 --- [           main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'taskLifecycleListener'; nested exception is java.lang.IllegalArgumentException: Invalid TaskExecution, ID 1 not found\n2022-01-18 18:54:55.703  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown\n2022-01-18 18:54:55.703  INFO 3680 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans\n2022-01-18 18:54:55.703 ERROR 3680 --- [           main] o.s.c.t.listener.TaskLifecycleListener   : An event to end a task has been received for a task that has not yet started.\n2022-01-18 18:54:55.709  INFO 3680 --- [           main] utoConfigurationReportLoggingInitializer : \n\nError starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.\n2022-01-18 18:54:55.715 ERROR 3680 --- [           main] o.s.boot.SpringApplication               : Application startup failed\n\norg.springframework.context.ApplicationContextException: Failed to start bean 'taskLifecycleListener'; nested exception is java.lang.IllegalArgumentException: Invalid TaskExecution, ID 1 not found\n\tat org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:50) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:348) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:151) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:114) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:879) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) [spring-boot-1.5.2.RELEASE.jar!/:1.5.2.RELEASE]\n\tat org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) [spring-boot-1.5.2.RELEASE.jar!/:1.5.2.RELEASE]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.5.2.RELEASE.jar!/:1.5.2.RELEASE]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.2.RELEASE.jar!/:1.5.2.RELEASE]\n\tat org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.2.RELEASE.jar!/:1.5.2.RELEASE]\n\tat org.springframework.cloud.task.app.timestamp.TimestampTaskApplication.main(TimestampTaskApplication.java:29) [classes!/:1.2.0.RELEASE]\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_322]\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_322]\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_322]\n\tat java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_322]\n\tat org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48) [timestamp-task-1.2.0.RELEASE.jar:1.2.0.RELEASE]\n\tat org.springframework.boot.loader.Launcher.launch(Launcher.java:87) [timestamp-task-1.2.0.RELEASE.jar:1.2.0.RELEASE]\n\tat org.springframework.boot.loader.Launcher.launch(Launcher.java:50) [timestamp-task-1.2.0.RELEASE.jar:1.2.0.RELEASE]\n\tat org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51) [timestamp-task-1.2.0.RELEASE.jar:1.2.0.RELEASE]\nCaused by: java.lang.IllegalArgumentException: Invalid TaskExecution, ID 1 not found\n\tat org.springframework.util.Assert.notNull(Assert.java:134) ~[spring-core-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\tat org.springframework.cloud.task.listener.TaskLifecycleListener.doTaskStart(TaskLifecycleListener.java:200) ~[spring-cloud-task-core-1.2.0.RELEASE.jar!/:1.2.0.RELEASE]\n\tat org.springframework.cloud.task.listener.TaskLifecycleListener.start(TaskLifecycleListener.java:282) ~[spring-cloud-task-core-1.2.0.RELEASE.jar!/:1.2.0.RELEASE]\n\tat org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:175) ~[spring-context-4.3.7.RELEASE.jar!/:4.3.7.RELEASE]\n\t... 20 common frames omitted\n\n"
```

# 附录

在云数据流方面遇到了麻烦，我们很乐意提供帮助！

* 问一个问题。我们监控[stackoverflow.com](https://stackoverflow.com)中带有[`spring-cloud-dataflow`]标记的问题（https://stackoverflow.com/tags/ Spring-cloud-dataflow）。

* 在[github.com/spring-cloud/spring-cloud-dataflow/issues](https://github.com/spring-cloud/spring-cloud-dataflow/issues)处使用 Spring 云数据流报告错误。

## 附录 A:数据流模板

正如 API 指南一章中所描述的， Spring 云数据流的功能通过 REST 端点完全公开。虽然你可以直接使用这些端点， Spring 云数据流还提供了基于 Java 的 API，这使得使用这些 REST 端点更加容易。

中心入口点是`org.springframework.cloud.dataflow.rest.client`包中的`DataFlowTemplate`类。

该类实现`DataFlowOperations`接口，并委托给以下子模板，这些子模板为每个功能集提供特定的功能：

|          Interface          |说明|
|-----------------------------|----------------------------------------------|
|     `StreamOperations`      |用于流操作的 REST 客户机|
|     `CounterOperations`     |用于柜台操作的 REST 客户机|
|`FieldValueCounterOperations`|用于字段值计数器操作的 REST 客户机|
|`AggregateCounterOperations` |用于聚合计数器操作的 REST 客户机|
|      `TaskOperations`       |用于任务操作的 REST 客户机|
|       `JobOperations`       |REST 客户机用于作业操作|
|   `AppRegistryOperations`   |应用程序注册操作的 REST 客户端|
|   `CompletionOperations`    |用于完成操作的 REST 客户机|
|     `RuntimeOperations`     |用于运行时操作的 REST 客户机|

在初始化`DataFlowTemplate`时，可以通过 Hateoas（作为应用状态引擎的超媒体）提供的 REST 关系发现子模板。

|   |如果无法解析某个资源，则相应的子模板将以 null 表示<br/>。一个常见的原因是 Spring 云数据流允许在启动时启用或禁用特定的<br/>特征集。有关更多信息，请参见[local](#configuration-local-enable-disable-specific-features)、[Cloud Foundry](#configuration-cloudfoundry-enable-disable-specific-features)或[Kubernetes](#configuration-kubernetes-enable-disable-specific-features)配置章节中的一章，这取决于你在哪里部署应用程序。|
|---| |

### A.1.使用数据流模板

当你使用数据流模板时，唯一需要的数据流依赖项是 Spring Cloud Data Flow REST 客户机，如以下 Maven 片段所示：

```
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-dataflow-rest-client</artifactId>
  <version>2.9.2</version>
</dependency>
```

有了这个依赖关系，你就得到了`DataFlowTemplate`类，以及调用 Spring 云数据流服务器所需的所有依赖关系。

在实例化`DataFlowTemplate`时，还传递一个`RestTemplate`。请注意，所需的`RestTemplate`需要一些额外的配置才能在`DataFlowTemplate`的上下文中有效。当将`RestTemplate`声明为 Bean 时，以下配置就足够了：

```
  @Bean
  public static RestTemplate restTemplate() {
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.setErrorHandler(new VndErrorResponseErrorHandler(restTemplate.getMessageConverters()));
    for(HttpMessageConverter<?> converter : restTemplate.getMessageConverters()) {
      if (converter instanceof MappingJackson2HttpMessageConverter) {
        final MappingJackson2HttpMessageConverter jacksonConverter =
            (MappingJackson2HttpMessageConverter) converter;
        jacksonConverter.getObjectMapper()
            .registerModule(new Jackson2HalModule())
            .addMixIn(JobExecution.class, JobExecutionJacksonMixIn.class)
            .addMixIn(JobParameters.class, JobParametersJacksonMixIn.class)
            .addMixIn(JobParameter.class, JobParameterJacksonMixIn.class)
            .addMixIn(JobInstance.class, JobInstanceJacksonMixIn.class)
            .addMixIn(ExitStatus.class, ExitStatusJacksonMixIn.class)
            .addMixIn(StepExecution.class, StepExecutionJacksonMixIn.class)
            .addMixIn(ExecutionContext.class, ExecutionContextJacksonMixIn.class)
            .addMixIn(StepExecutionHistory.class, StepExecutionHistoryJacksonMixIn.class);
      }
    }
    return restTemplate;
  }
```

|   |你还可以通过使用`DataFlowTemplate.getDefaultDataflowRestTemplate();`获得预先配置的`RestTemplate`|
|---|-------------------------------------------------------------------------------------------------------------|

现在，你可以使用以下代码实例化`DataFlowTemplate`:

```
DataFlowTemplate dataFlowTemplate = new DataFlowTemplate(
    new URI("http://localhost:9393/"), restTemplate);         (1)
```

|**1**|`URI`指向你的 Spring 云数据流服务器的根。|
|-----|-------------------------------------------------------------------|

根据你的需求，你现在可以调用服务器。例如，如果你想获得当前可用应用程序的列表，可以运行以下代码：

```
PagedResources<AppRegistrationResource> apps = dataFlowTemplate.appRegistryOperations().list();

System.out.println(String.format("Retrieved %s application(s)",
    apps.getContent().size()));

for (AppRegistrationResource app : apps.getContent()) {
  System.out.println(String.format("App Name: %s, App Type: %s, App URI: %s",
    app.getName(),
    app.getType(),
    app.getUri()));
}
```

### A.2.数据流模板和安全性

当使用`DataFlowTemplate`时，还可以提供所有与安全相关的选项，就像使用*数据流壳*一样。实际上，*数据流壳*对其所有操作都使用`DataFlowTemplate`。

为了让你开始使用，我们提供了一个`HttpClientConfigurer`，它使用 Builder 模式来设置各种与安全相关的选项：

```
	HttpClientConfigurer
		.create(targetUri)                                             (1)
		.basicAuthCredentials(username, password)                      (2)
		.skipTlsCertificateVerification()                              (3)
		.withProxyCredentials(proxyUri, proxyUsername, proxyPassword)  (4)
		.addInterceptor(interceptor)                                   (5)
		.buildClientHttpRequestFactory()                               (6)
```

|**1**|使用提供的目标 URI 创建 HttpClientConfigurer。|
|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|设置基本身份验证的凭据（使用 OAuth2Password Grant）|
|**3**|跳过 SSL 证书验证（** 仅用于开发！**）|
|**4**|配置任何代理设置|
|**5**|添加一个自定义拦截器，例如设置 OAuth2 授权头。这允许<br/>你传递一个 OAuth2 访问令牌，而不是用户名/密码凭据。|
|**6**|构建可以在`RestTemplate`上设置的`ClientHttpRequestFactory`。|

一旦配置了`HttpClientConfigurer`，就可以使用其`buildClientHttpRequestFactory`构建`ClientHttpRequestFactory`，然后在`RestTemplate`上设置相应的属性。然后可以使用`RestTemplate`实例化实际的`DataFlowTemplate`。

要配置*基本身份验证*，需要进行以下设置：

```
	RestTemplate restTemplate = DataFlowTemplate.getDefaultDataflowRestTemplate();
	HttpClientConfigurer httpClientConfigurer = HttpClientConfigurer.create("http://localhost:9393");

	httpClientConfigurer.basicAuthCredentials("my_username", "my_password");
	restTemplate.setRequestFactory(httpClientConfigurer.buildClientHttpRequestFactory());

	DataFlowTemplate dataFlowTemplate = new DataFlowTemplate("http://localhost:9393", restTemplate);
```

你可以在 GitHub 上的[spring-cloud-dataflow-samples](https://github.com/spring-cloud/spring-cloud-dataflow-samples/tree/master/dataflow-template-example)存储库中找到一个示例应用程序。

## 附录 B:“操作指南”

这一部分提供了一些常见的“我该如何做到这一点……”问题的答案，这些问题在人们使用 Spring 云数据流时经常会出现。

如果你有一个我们在此未讨论的特定问题，你可能想查看[stackoverflow.com](https://stackoverflow.com/tags/spring-cloud-dataflow)，以查看是否有人已经提供了答案。这也是一个提出新问题的好地方（使用`spring-cloud-dataflow`标签）。

我们也非常乐意扩展这一部分。如果你想添加一个“how-to”，你可以给我们发送[拉请求](https://github.com/spring-cloud/spring-cloud-dataflow)。

### B.1.配置 Maven 属性

你可以在启动数据流服务器时，通过命令行属性设置 Maven 属性，例如本地 Maven 存储库位置、远程 Maven 存储库、身份验证凭据和代理服务器属性。或者，你可以通过为数据流服务器设置`SPRING_APPLICATION_JSON`环境属性来设置这些属性。

如果应用程序是通过使用 Maven 存储库解析的，则需要显式地配置远程 Maven 存储库，但`local`数据流服务器除外。其他数据流服务器实现（它们使用 Maven 资源来解析应用程序工件）对于远程存储库没有默认值。`local`服务器将`[repo.spring.io/libs-snapshot](https://repo.spring.io/libs-snapshot)`作为默认的远程存储库。

要将属性作为命令行选项传递，请使用类似于以下命令的命令运行服务器：

```
$ java -jar <dataflow-server>.jar --maven.localRepository=mylocal
--maven.remote-repositories.repo1.url=https://repo1
--maven.remote-repositories.repo1.auth.username=repo1user
--maven.remote-repositories.repo1.auth.password=repo1pass
--maven.remote-repositories.repo2.url=https://repo2 --maven.proxy.host=proxyhost
--maven.proxy.port=9018 --maven.proxy.auth.username=proxyuser
--maven.proxy.auth.password=proxypass
```

你也可以使用`SPRING_APPLICATION_JSON`环境属性：

```
export SPRING_APPLICATION_JSON='{ "maven": { "local-repository": "local","remote-repositories": { "repo1": { "url": "https://repo1", "auth": { "username": "repo1user", "password": "repo1pass" } },
"repo2": { "url": "https://repo2" } }, "proxy": { "host": "proxyhost", "port": 9018, "auth": { "username": "proxyuser", "password": "proxypass" } } } }'
```

以下是格式良好的 JSON 中的相同内容：

```
SPRING_APPLICATION_JSON='{
  "maven": {
    "local-repository": "local",
    "remote-repositories": {
      "repo1": {
        "url": "https://repo1",
        "auth": {
          "username": "repo1user",
          "password": "repo1pass"
        }
      },
      "repo2": {
        "url": "https://repo2"
      }
    },
    "proxy": {
      "host": "proxyhost",
      "port": 9018,
      "auth": {
        "username": "proxyuser",
        "password": "proxypass"
      }
    }
  }
}'
```

|   |Spring 根据云数据流服务器的实现方式，你可能必须通过使用平台特定的环境设置能力来传递环境属性。例如，在 Cloud Foundry 中，将它们传递为`cf set-env SPRING_APPLICATION_JSON`。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### B.2.故障排除

本节介绍如何在你选择的平台上对 Spring 云数据流进行故障诊断。关于[Stream](https://dataflow.spring.io/docs/stream-developer-guides/troubleshooting/)和[Batch](https://dataflow.spring.io/docs/batch-developer-guides/troubleshooting/)处理，请参见微站点的故障排除部分。

### B.3.常见问题

在这一部分中，我们回顾了 Spring 云数据流的常见问题。有关更多信息，请参见微型网站的[常见问题](https://dataflow.spring.io/docs/resources/faq/)部分。

## 附录 C:建筑物

本附录描述了如何构建 Spring 云数据流。

要构建源代码，你需要安装 JDK1.8.

构建使用 Maven 包装器，因此你不必安装 Maven 的特定版本。

主要的构建命令如下：

```
$ ./mvnw clean install
```

为了加快构建速度，你可以添加`-DskipTests`以避免运行测试。

|   |你也可以自己安装 Maven（\>=3.3.3），并在下面的示例中运行`mvn`命令来代替`./mvnw`。<br/>如果你这样做了，如果你的本地 Maven 设置不包含 Spring 预发布工件的存储库声明，那么你可能还需要添加`-P spring`。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |你可能需要通过设置一个`MAVEN_OPTS`的值类似于`-Xmx512m -XX:MaxPermSize=128m`的环境变量来增加 Maven 可用的内存量。<br/>我们试图在`.mvn`配置中覆盖这一点，所以，如果你发现你必须这样做才能使构建成功，请提出一张票，让设置添加到源代码控制。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

### C.1.文件

有一个生成文档的`full`配置文件。你可以使用以下命令只构建文档：

```
$ ./mvnw clean package -DskipTests -P full -pl {project-artifactId} -am
```

### C.2.使用代码

如果你没有最喜欢的 IDE，我们建议你在使用代码时使用[Spring Tools Suite](https://spring.io/tools)或[Eclipse](https://www.eclipse.org)。我们使用[m2eclipse](https://www.eclipse.org/m2e/)Eclipse 插件来提供 Maven 支持。其他 IDE 和工具通常也可以正常工作。

#### C.2.1.用 M2Eclipse 导入到 Eclipse 中

在使用 Eclipse 时，我们推荐[m2eclipe](https://www.eclipse.org/m2e/)Eclipse 插件。如果你还没有安装 M2Eclipse，那么可以从 Eclipse 市场获得它。

遗憾的是，M2E 还不支持 Maven 3.3.因此，一旦项目被导入到 Eclipse 中，你还需要告诉 M2Eclipse 为项目使用`.settings.xml`文件。如果不这样做，你可能会在项目中看到与 POM 相关的许多不同的错误。这样做：

1. 打开你的 Eclipse 首选项。

2. 展开**Maven preferences**。

3. 选择**用户设置**。

4. 在**用户设置**字段中，单击**浏览**并导航到你导入的 Spring 云项目。

5. 在该项目中选择`.settings.xml`文件。

6. 点击**申请**。

7. 点击**OK**。

|   |或者，你可以将 Spring Cloud 的[`.settings.xml`]（https://github.com/ Spring-cloud/ Spring-cloud-build/blob/master/.settings.xml）文件中的存储库设置复制到你自己的`~/.m2/settings.xml`文件中。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### C.2.2.在没有 M2Eclipse 的情况下导入 Eclipse

如果不喜欢使用 M2Eclipse，可以使用以下命令生成 Eclipse 项目元数据：

```
$ ./mvnw eclipse:eclipse
```

通过从**档案**菜单中选择**导入现有项目**，可以导入生成的 Eclipse 项目。

## 附录 D:贡献

Spring 云是在非限制性的 Apache2.0 许可下发布的，遵循非常标准的 GitHub 开发流程，使用 GitHub Tracker 处理问题，并将拉请求合并到主分支中。如果你想贡献一些微不足道的东西，请不要犹豫，但请遵循本附录中的指导方针。

### D.1.签署贡献者许可协议

在我们接受一个非平凡的（除了纠正打印错误以外的任何事情）补丁或拉请求之前，我们需要你签署[供款人协议](https://cla.pivotal.io)。签署贡献者的协议并不会授予任何人对主库的提交权限，但这确实意味着我们可以接受你的贡献，并且如果我们接受了，你将获得作者信用。活跃的贡献者可能会被要求加入核心团队，并被赋予合并拉请求的能力。

### D.2.守则惯例和内部管理

对于一个拉请求，下面的指导原则都不是必不可少的，但是它们都可以帮助你的开发人员同事理解并处理你的代码。它们也可以在原始的拉请求之后但在合并之前添加。

* 使用 Spring 框架代码格式约定。如果使用 Eclipse，可以使用`eclipse-code-formatter.xml`项目中的[Spring Cloud Build](https://github.com/spring-cloud/spring-cloud-build/blob/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml)文件导入格式化设置。如果使用 IntelliJ，则可以使用[Eclipse 代码格式化插件](https://plugins.jetbrains.com/plugin/6546)导入相同的文件。

* 确保所有新的`.java`文件都有一个简单的 Javadoc 类注释，其中至少有一个`@author`标记来标识你，并且最好至少有一个段落来描述类的目的。

* 将 ASF 许可标头注释添加到所有新的`.java`文件中（要这样做，请从项目中的现有文件中复制它）。

* 将自己作为`@author`添加到要进行实质性修改的.java 文件中（不仅仅是外观上的更改）。

* 添加一些 Javadocs，如果你更改了名称空间，还可以添加一些 XSDDOC 元素。

* 一些单元测试也会有很大帮助。必须有人来做这件事，而你的开发人员也会对此表示赞赏。

* 如果没有其他人使用你的分支，则将它与当前的主分支（或主项目中的其他目标分支）重新绑定。

* 写提交消息时，请遵循[这些约定](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)。如果修复了现有的问题，请在提交消息的末尾添加`Fixes gh-XXXX`（其中 xxxx 是问题编号）。

  \== 身份提供者

本附录包含如何设置特定的提供者来处理数据流安全性的信息。

在撰写本文时，Azure 是唯一的身份提供程序。

### D.3.天蓝色

Azure AD 是一个成熟的身份提供程序，提供了一系列有关身份验证和授权的功能。与任何其他提供商一样，它也有自己的细微差别，这意味着必须小心地设置它。

在这一节中，我们将介绍如何为 AD 和 Spring 云数据流完成 OAuth2 设置。

|   |你需要完整的组织访问权限来正确设置所有内容。|
|---|------------------------------------------------------------------------|

#### D.3.1.创造一个新的广告环境

要开始，请创建一个新的 ActiveDirectory 环境。选择类型为 Azure Active Directory（而不是 B2C 类型），然后选择你的组织名称和初始域。下图显示了设置：

<img alt="Create AD Environment" src="./images/azure-create-ad-env.png" width="60%" />

#### D.3.2.创建一个新的应用程序注册

应用程序注册是创建 OAuth 客户端以被 OAuth 应用程序使用的地方。至少，你需要创建两个客户机，一个用于数据流和 Skipper 服务器，另一个用于数据流壳层，因为这两个客户机的配置略有不同。服务器应用程序可以被认为是受信任的应用程序，而 shell 则不受信任（因为用户可以看到它的完整配置）。

注意：我们建议对数据流和 Skipper 服务器都使用相同的 OAuth 客户机。虽然你可以使用不同的客户机，但它目前不会提供任何价值，因为配置需要相同。

下图显示了创建一个新应用程序注册的设置：

<img alt="Create App Registration" src="./images/azure-create-app-registration.png" width="60%" />

|   |当需要时，在 AD 中的`Certificates & secrets`下创建一个客户端秘密。|
|---|------------------------------------------------------------------------------|

#### D.3.3.公开 DataFlow API

要准备 OAuth 范围，请为每个数据流安全角色创建一个。在这个例子中，这些将是

* `api://dataflow-server/dataflow.create`

* `api://dataflow-server/dataflow.deploy`

* `api://dataflow-server/dataflow.destroy`

* `api://dataflow-server/dataflow.manage`

* `api://dataflow-server/dataflow.schedule`

* `api://dataflow-server/dataflow.modify`

* `api://dataflow-server/dataflow.view`

下图显示了要公开的 API:

<img alt="Expose APIs" src="./images/azure-expose-apis.png" width="60%" />

需要将先前创建的作用域添加为 API 权限，如下图所示：

<img alt="Api Permissions" src="./images/azure-api-permissions.png" width="60%" />

#### D.3.4.创建特权客户端

对于即将使用密码授权的 OAuth 客户机，需要为 OAuth 客户机创建与服务器使用的 API 权限相同（在上一节中进行了描述）。

|   |所有这些权限都需要以管理权限授予。|
|---|---------------------------------------------------------------|

下图显示了特权设置：

<img alt="Privileged Client" src="./images/azure-privileged-client.png" width="60%" />

|   |特权客户机需要一个客户机秘密，当在 shell 中使用时，该秘密需要公开给客户机<br/>配置。如果不想公开该秘密，请使用[创建公共客户端](#appendix-identity-provider-azure-pubclient)公共客户端。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### D.3.5.创建公共客户端

公共客户机基本上是一个没有客户机秘密且其类型设置为公共的客户机。

下图显示了一个公共客户端的配置：

<img alt="Public Client" src="./images/azure-public-client.png" width="60%" />

#### D.3.6.配置示例

本节包含数据流和 Skipper 服务器以及 shell 的配置示例。

启动数据流服务器：

```
$ java -jar spring-cloud-dataflow-server.jar \
  --spring.config.additional-location=dataflow-azure.yml
```

dataFlow-azure.yml

```
spring:
  cloud:
    dataflow:
      security:
        authorization:
          provider-role-mappings:
            dataflow-server:
              map-oauth-scopes: true
              role-mappings:
                ROLE_VIEW: dataflow.view
                ROLE_CREATE: dataflow.create
                ROLE_MANAGE: dataflow.manage
                ROLE_DEPLOY: dataflow.deploy
                ROLE_DESTROY: dataflow.destroy
                ROLE_MODIFY: dataflow.modify
                ROLE_SCHEDULE: dataflow.schedule
  security:
    oauth2:
      client:
        registration:
          dataflow-server:
            provider: azure
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            client-id: <client id>
            client-secret: <client secret>
            scope:
            - openid
            - profile
            - email
            - offline_access
            - api://dataflow-server/dataflow.view
            - api://dataflow-server/dataflow.deploy
            - api://dataflow-server/dataflow.destroy
            - api://dataflow-server/dataflow.manage
            - api://dataflow-server/dataflow.modify
            - api://dataflow-server/dataflow.schedule
            - api://dataflow-server/dataflow.create
        provider:
          azure:
            issuer-uri: https://login.microsoftonline.com/799dcfde-b9e3-4dfc-ac25-659b326e0bcd/v2.0
            user-name-attribute: name
      resourceserver:
        jwt:
          jwk-set-uri: https://login.microsoftonline.com/799dcfde-b9e3-4dfc-ac25-659b326e0bcd/discovery/v2.0/keys
```

要启动 Skipper 服务器：

```
$ java -jar spring-cloud-skipper-server.jar \
  --spring.config.additional-location=skipper-azure.yml
```

Skipper-Azure.yml

```
spring:
  cloud:
    skipper:
      security:
        authorization:
          provider-role-mappings:
            skipper-server:
              map-oauth-scopes: true
              role-mappings:
                ROLE_VIEW: dataflow.view
                ROLE_CREATE: dataflow.create
                ROLE_MANAGE: dataflow.manage
                ROLE_DEPLOY: dataflow.deploy
                ROLE_DESTROY: dataflow.destroy
                ROLE_MODIFY: dataflow.modify
                ROLE_SCHEDULE: dataflow.schedule
  security:
    oauth2:
      client:
        registration:
          skipper-server:
            provider: azure
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            client-id: <client id>
            client-secret: <client secret>
            scope:
            - openid
            - profile
            - email
            - offline_access
            - api://dataflow-server/dataflow.view
            - api://dataflow-server/dataflow.deploy
            - api://dataflow-server/dataflow.destroy
            - api://dataflow-server/dataflow.manage
            - api://dataflow-server/dataflow.modify
            - api://dataflow-server/dataflow.schedule
            - api://dataflow-server/dataflow.create
        provider:
          azure:
            issuer-uri: https://login.microsoftonline.com/799dcfde-b9e3-4dfc-ac25-659b326e0bcd/v2.0
            user-name-attribute: name
      resourceserver:
        jwt:
          jwk-set-uri: https://login.microsoftonline.com/799dcfde-b9e3-4dfc-ac25-659b326e0bcd/discovery/v2.0/keys
```

要启动 shell 并（可选地）将凭据作为选项传递：

```
$ java -jar spring-cloud-dataflow-shell.jar \
  --spring.config.additional-location=dataflow-azure-shell.yml \
  --dataflow.username=<USERNAME> \
  --dataflow.password=<PASSWORD>
```

dataflow-azure-shell.yml

```
  security:
    oauth2:
      client:
        registration:
          dataflow-shell:
            provider: azure
            client-id: <client id>
            client-secret: <client secret>
            authorization-grant-type: password
            scope:
            - offline_access
            - api://dataflow-server/dataflow.create
            - api://dataflow-server/dataflow.deploy
            - api://dataflow-server/dataflow.destroy
            - api://dataflow-server/dataflow.manage
            - api://dataflow-server/dataflow.modify
            - api://dataflow-server/dataflow.schedule
            - api://dataflow-server/dataflow.view
        provider:
          azure:
            issuer-uri: https://login.microsoftonline.com/799dcfde-b9e3-4dfc-ac25-659b326e0bcd/v2.0
```

启动一个公共 shell 并（可选地）将凭据作为选项传递：

```
$ java -jar spring-cloud-dataflow-shell.jar \
  --spring.config.additional-location=dataflow-azure-shell-public.yml \
  --dataflow.username=<USERNAME> \
  --dataflow.password=<PASSWORD>
```

dataFlow-Azure-shell-public.yml

```
spring:
  security:
    oauth2:
      client:
        registration:
          dataflow-shell:
            provider: azure
            client-id: <client id>
            authorization-grant-type: password
            client-authentication-method: post
            scope:
            - offline_access
            - api://dataflow-server/dataflow.create
            - api://dataflow-server/dataflow.deploy
            - api://dataflow-server/dataflow.destroy
            - api://dataflow-server/dataflow.manage
            - api://dataflow-server/dataflow.modify
            - api://dataflow-server/dataflow.schedule
            - api://dataflow-server/dataflow.view
        provider:
          azure:
            issuer-uri: https://login.microsoftonline.com/799dcfde-b9e3-4dfc-ac25-659b326e0bcd/v2.0
```