

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 使用 Step Functions 创建活动状态机
<a name="tutorial-creating-activity-state-machine"></a>

本教程介绍如何使用 Java 和 AWS Step Functions创建基于活动的状态机。活动允许您控制在状态机中的其他位置运行的工作程序代码。有关概述，请参阅 [了解 Step Functions 中的状态机](concepts-statemachines.md)中的[了解 Step Functions 中的活动](concepts-activities.md)。

要完成本教程，您需要：
+ [适用于 Java 的 SDK](https://aws.amazon.com/sdk-for-java/)。本教程中的示例活动是一个 Java 应用程序，它使用与 适用于 Java 的 AWS SDK 进行通信 AWS。
+ AWS 环境或标准 AWS 配置文件中的凭据。有关更多信息，请参阅《*适用于 Java 的 AWS SDK 开发者指南》*中的[设置您的 AWS 凭证](https://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/set-up-creds.html)。

## 第 1 步：创建活动
<a name="create-activity-state-machine-step-1"></a>

您必须让 Step Functions 知道某项*活动*，您希望创建该活动的*工作线程*（一个程序）。Step Functions 使用为活动建立身份的 Amazon 资源名称 (ARN) 作为响应。可以使用此身份协调状态机与工作线程之间传递的信息。

**重要**  
确保您的活动任务与状态机属于同一个 AWS 帐户。

1. 在 [Step Functions 控制台](https://console.aws.amazon.com/states/home)左侧的导航窗格中，选择**活动**。

1. 选择**创建活动**。

1. 输入活动的**名称**，例如 `get-greeting`，然后选择**创建活动**。

1. 创建活动任务时，请记下其 ARN，如以下示例所示。

   ```
   arn:aws:states:region:123456789012:activity:get-greeting
   ```

## 第 2 步：创建状态机
<a name="create-activity-state-machine-step-2"></a>

创建一个状态机，该状态机确定什么时候调用活动以及什么时候工作线程执行其主要工作，收集其结果并返回这些结果。要创建状态机，您需要使用 Workflow Studio 的[代码编辑器](workflow-studio.md#wfs-interface-code-editor)。

1. 在 [Step Functions 控制台](https://console.aws.amazon.com/states/home)左侧的导航窗格中，选择**状态机**。

1. 在**状态机**页面上，选择**创建状态机**。

1. 选择**从空白创建**。

1. 为状态机命名，然后选择**继续**，在 Workflow Studio 中编辑状态机。

1. 在本教程中，您将在代码编辑器中编写状态机的 [Amazon States Language](concepts-amazon-states-language.md) (ASL) 定义。要执行此操作，请选择**代码**。

1. 删除现有的样板代码并粘贴以下代码。请记住将 `Resource` 字段中的示例 ARN 替换为您之前在[第 1 步：创建活动](#create-activity-state-machine-step-1)中创建的活动任务的 ARN。

   ```
   {
     "Comment": "An example using a Task state.",
     "StartAt": "getGreeting",
     "Version": "1.0",
     "TimeoutSeconds": 300,
     "States":
     {
       "getGreeting": {
         "Type": "Task",
         "Resource": "arn:aws:states:region:123456789012:activity:get-greeting",
         "End": true
       }
     }
   }
   ```

   这是使用 [Amazon States Language](concepts-amazon-states-language.md)（ASL）的状态机的说明。它定义了名为 `getGreeting` 的单个 `Task` 状态。有关更多信息，请参阅[状态机结构](statemachine-structure.md)。

1. 在 [图形可视化](workflow-studio.md#wfs-interface-code-graph-viz) 上，确保您添加的 ASL 定义的工作流图表与下图相似。  
![\[带有 RunActivity 任务状态的状态机的图形可视化。\]](http://docs.aws.amazon.com/zh_cn/step-functions/latest/dg/images/tutorial-create-state-machine-custom-preview.png)

1. 为状态机指定一个名称。为此，请选择默认状态机名称旁边的编辑图标**MyStateMachine**。然后，找到**状态机配置**，在**状态机名称**框中指定一个名称。

   对于本教程，请输入名称 **ActivityStateMachine**。

1. （可选）在**状态机配置**中，指定其他工作流设置，例如状态机类型及其执行角色。

   在本教程中，请保留**状态机设置**中的所有默认选项。

   如果您之前为状态机[创建了具有正确权限的 IAM 角色](procedure-create-iam-role.md)并想使用该角色，请在**权限**中选择**选择现有角色**，然后从列表中选择一个角色。或者选择**输入角色 ARN**，然后为该 IAM 角色的 ARN 获取该角色。

1. 在**确认角色创建**对话框中，选择**确认**继续。

   您也可以选择**查看角色设置**，返回至**状态机配置**。
**注意**  
如果您删除了 Step Functions 创建的 IAM 角色，Step Functions 在以后无法重新创建该角色。同样，如果您修改了该角色（例如，通过在 IAM 策略中从主体中删除 Step Functions），Step Functions 在以后也无法还原其原始设置。

## 第 3 步：实现工作线程
<a name="create-activity-state-machine-step-3"></a>

创建*工作线程*。工作线程是负责以下事务的程序：
+ 使用 `GetActivityTask` API 操作在 Step Functions 中轮询活动。
+ 使用您的代码执行活动的工作（例如，以下代码中的 `getGreeting()` 方法）。
+ 使用 `SendTaskSuccess`、`SendTaskFailure` 和 `SendTaskHeartbeat` API 操作返回结果。

**注意**  
有关活动工作线程的更完整示例，请参阅[示例：Ruby 中的活动工作线程](concepts-activities.md#example-ruby-activity-worker)。该示例提供了一个基于最佳实操的实现，您可以参考它创建自己的活动工作线程。这段代码实现了消费者/生产者模型，并且允许对轮询器和活动工作线程的线程数进行配置。

### 实施工作线程
<a name="create-activity-state-machine-implement-worker"></a>

1. 创建一个名为 `GreeterActivities.java`的文件。

1. 将以下代码添加到其中。

   ```
   import com.amazonaws.ClientConfiguration;
   import com.amazonaws.auth.EnvironmentVariableCredentialsProvider;
   import com.amazonaws.regions.Regions;
   import com.amazonaws.services.stepfunctions.AWSStepFunctions;
   import com.amazonaws.services.stepfunctions.AWSStepFunctionsClientBuilder;
   import com.amazonaws.services.stepfunctions.model.GetActivityTaskRequest;
   import com.amazonaws.services.stepfunctions.model.GetActivityTaskResult;
   import com.amazonaws.services.stepfunctions.model.SendTaskFailureRequest;
   import com.amazonaws.services.stepfunctions.model.SendTaskSuccessRequest;
   import com.amazonaws.util.json.Jackson;
   import com.fasterxml.jackson.databind.JsonNode;
   import java.util.concurrent.TimeUnit;
   
   
   public class GreeterActivities {
   
       public String getGreeting(String who) throws Exception {
           return "{\"Hello\": \"" + who + "\"}";
       }
   
       public static void main(final String[] args) throws Exception {
           GreeterActivities greeterActivities = new GreeterActivities();
           ClientConfiguration clientConfiguration = new ClientConfiguration();
           clientConfiguration.setSocketTimeout((int)TimeUnit.SECONDS.toMillis(70));
   
           AWSStepFunctions client = AWSStepFunctionsClientBuilder.standard()
                   .withRegion(Regions.US_EAST_1)
                   .withCredentials(new EnvironmentVariableCredentialsProvider())
                   .withClientConfiguration(clientConfiguration)
                   .build();
   
           while (true) {
               GetActivityTaskResult getActivityTaskResult =
                       client.getActivityTask(
                               new GetActivityTaskRequest().withActivityArn(ACTIVITY_ARN));
   
               if (getActivityTaskResult.getTaskToken() != null) {
                   try {
                       JsonNode json = Jackson.jsonNodeOf(getActivityTaskResult.getInput());
                       String greetingResult =
                               greeterActivities.getGreeting(json.get("who").textValue());
                       client.sendTaskSuccess(
                               new SendTaskSuccessRequest().withOutput(
                                       greetingResult).withTaskToken(getActivityTaskResult.getTaskToken()));
                   } catch (Exception e) {
                       client.sendTaskFailure(new SendTaskFailureRequest().withTaskToken(
                               getActivityTaskResult.getTaskToken()));
                   }
               } else {
                   Thread.sleep(1000);
               }
           }
       }
   }
   ```
**注意**  
该示例中的 `EnvironmentVariableCredentialsProvider` 类假定已设置了 `AWS_ACCESS_KEY_ID`（或 `AWS_ACCESS_KEY`）和 `AWS_SECRET_KEY`（或 `AWS_SECRET_ACCESS_KEY`）环境变量。有关向工厂提供所需凭证的更多信息，请参阅 *适用于 Java 的 AWS SDK API 参考*中的[AWSCredentials提供商](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/AWSCredentialsProvider.html)和《*适用于 Java 的 AWS SDK 开发人员指南》中的 “为开发人员*[设置 AWS 凭证和区域](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-credentials.html)”。  
默认情况下， AWS SDK 最多会等待 50 秒才能从服务器接收任何操作的数据。`GetActivityTask` 操作是一个长时间运行的轮询操作，对于下一个可用任务将等待多达 60 秒。为防止收到`SocketTimeoutException`错误，请设置 SocketTimeout 为 70 秒。

1. 在 `GetActivityTaskRequest().withActivityArn()` 构造函数的参数列表中，将 `ACTIVITY_ARN` 值替换为您之前在[第 1 步：创建活动](#create-activity-state-machine-step-1)中创建的活动任务的 ARN。

## 第 4 步：运行状态机
<a name="create-activity-state-machine-step-4"></a>

开始执行状态机时，您的工作线程在 Step Functions 中轮询活动，执行其工作（使用您提供的输入）并返回其结果。

1. 在***ActivityStateMachine***页面上，选择**开始执行**。

   随即显示**启动执行**对话框。

1. 在**启动执行**对话框中，执行以下操作：

   1. （可选）输入自定义执行名称，以便覆盖生成的默认执行名称。
**非 ASCII 名称和日志记录**  
Step Functions 对于状态机、执行、活动和标签接受包含非 ASCII 字符的名称。由于此类字符会 CloudWatch 阻止亚马逊记录数据，因此我们建议您仅使用 ASCII 字符，这样您就可以跟踪 Step Functions 的指标。

   1. 在**输入**框中，输入以下 JSON 输入，运行您的工作流。

      ```
      {
        "who": "AWS Step Functions"
      }
      ```

   1. 选择**启动执行**。

   1. Step Functions 控制台会将您引导到一个以您的执行 ID 为标题的页面。该页面被称为*执行详细信息*页面。在此页面上，您可以随着执行的进展或者在执行完成后查看执行结果。

      要查看执行结果，请在**图表视图**上选择各个状态，然后在[步骤详细信息](concepts-view-execution-details.md#exec-details-intf-step-details)窗格中选择各个选项卡，分别查看每个状态的详细信息，包括输入、输出和定义。有关可在*执行详细信息*页面上查看的执行信息的详细信息，请参阅[执行详细信息概览](concepts-view-execution-details.md#exec-details-interface-overview)。

## 第 5 步：运行和停止工作线程
<a name="create-activity-state-machine-step-5"></a>

要让工作线程在您的状态机中轮询活动，必须运行工作线程。

1. 在命令行上，导航到您在其中创建 `GreeterActivities.java` 的目录。

1. 要使用 AWS SDK，请将`lib`和`third-party`目录的完整路径添加到构建文件的依赖项和 Java 中`CLASSPATH`。有关更多信息，请参阅**《适用于 Java 的 AWS SDK 开发人员指南》中的[下载和解压缩开发工具包](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/setup-install.html#download-and-extract-sdk)。

1. 编译文件。

   ```
   $ javac GreeterActivities.java
   ```

1. 运行文件。

   ```
   $ java GreeterActivities
   ```

1. 在 [Step Functions 控制台](https://console.aws.amazon.com/states/home?region=us-east-1#/)中，导航到*执行详细信息*页面。

1. 执行完成后，检查执行结果。

1. 停止工作线程。