

 适用于 Java 的 Amazon SDK 1.x于2025年 end-of-support 12月31日达到。我们建议您迁移到 [Amazon SDK for Java 2.x](https://docs.amazonaws.cn/sdk-for-java/latest/developer-guide/home.html) 以继续获得新功能、可用性改进和安全更新。

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

# 教程：高级 Amazon EC2 竞价型实例请求管理
<a name="tutorial-spot-adv-java"></a>

 Amazon EC2 Spot 实例允许您对未使用的 Amazon EC2 容量出价，并在出价高于当前 *Spot 价格* 的期间运行此类实例。Amazon EC2 基于供给和需求定期更改 Spot 价格。有关竞价型实例的更多信息，请参阅《Amazon EC2 用户指南（适用于 Linux 实例）》中的[竞价型实例](https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/using-spot-instances.html)。

## 先决条件
<a name="tutor-spot-adv-java-prereq"></a>

要使用此指南，您必须已安装适用于 Java 的 Amazon SDK 并且已满足其基本安装先决条件。有关更多信息，请参阅[设置适用于 Java 的 Amazon SDK](setup-install.md)。

## 设置您的凭证
<a name="tutor-spot-adv-java-credentials"></a>

要开始使用此代码示例，您需要设置 Amazon 凭证。有关具体操作说明，请参阅[设置用于开发的 Amazon 凭证和区域](setup-credentials.md)。

**注意**  
建议您使用 IAM 用户凭证来提供这些值。有关更多信息，请参阅[注册 Amazon 并创建 IAM 用户](signup-create-iam-user.md)。

您既然已配置好了您的设置，现在就可以使用示例中的代码开始了。

## 设置安全组
<a name="tutor-spot-adv-java-sg"></a>

一个安全组可作为一个控制流量进入和流出实例组的防火墙。默认情况下，实例开始运行时没有配置任何安全组，这就意味着，从任何 TCP 端口传入的 IP 流量都将被拒绝。因此，在提交 Spot 请求前，我们会设置一个安全组，以允许必要的网络流量传入。出于本教程的目的，我们将创建一个名为“GettingStarted”的新安全组，以允许从您正在运行的应用程序的 IP 地址传入 Secure Shell (SSH) 流量。要设置一个新的安全组，需要包含或运行下列通过编程的方式来设置安全组的代码示例。

创建 `AmazonEC2` 客户端数据元之后，我们会创建一个名为“GettingStarted”的`CreateSecurityGroupRequest`数据元以及对安全组的描述。接下来，我们将调用`ec2.createSecurityGroup` API 来创建安全组。

为访问安全组，我们将使用本地电脑子网的 CIDR 表示的 IP 地址范围创建一个 `ipPermission` 数据元，IP 地址的后缀“/10”指明了该指定 IP 地址的子网。我们还为 `ipPermission` 数据元配置了 TCP 协议和端口 22 (SSH)。最后一步是使用我们的安全组名称和 `ec2 .authorizeSecurityGroupIngress` 数据元来调用 `ipPermission`。

（以下代码与我们在第一个教程中使用的代码相同。）

```
// Create the AmazonEC2Client object so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.standard()
                    .withCredentials(credentials)
                    .build();

// Create a new security group.
try {
    CreateSecurityGroupRequest securityGroupRequest =
        new CreateSecurityGroupRequest("GettingStartedGroup",
        "Getting Started Security Group");
    ec2.createSecurityGroup(securityGroupRequest);
} catch (AmazonServiceException ase) {
    // Likely this means that the group is already created, so ignore.
    System.out.println(ase.getMessage());
}

String ipAddr = "0.0.0.0/0";

// Get the IP of the current host, so that we can limit the Security Group
// by default to the ip range associated with your subnet.
try {
    // Get IP Address
    InetAddress addr = InetAddress.getLocalHost();
    ipAddr = addr.getHostAddress()+"/10";
}
catch (UnknownHostException e) {
    // Fail here...
}

// Create a range that you would like to populate.
ArrayList<String> ipRanges = new ArrayList<String>();
ipRanges.add(ipAddr);

// Open up port 22 for TCP traffic to the associated IP from
// above (e.g. ssh traffic).
ArrayList<IpPermission> ipPermissions = new ArrayList<IpPermission> ();
IpPermission ipPermission = new IpPermission();
ipPermission.setIpProtocol("tcp");
ipPermission.setFromPort(new Integer(22));
ipPermission.setToPort(new Integer(22));
ipPermission.setIpRanges(ipRanges);
ipPermissions.add(ipPermission);

try {
    // Authorize the ports to the used.
    AuthorizeSecurityGroupIngressRequest ingressRequest =
        new AuthorizeSecurityGroupIngressRequest(
            "GettingStartedGroup",ipPermissions);
    ec2.authorizeSecurityGroupIngress(ingressRequest);
}
catch (AmazonServiceException ase) {
    // Ignore because this likely means the zone has already
    // been authorized.
    System.out.println(ase.getMessage());
}
```

您可以在 `advanced.CreateSecurityGroupApp.java` 代码示例中查看整个代码示例。请注意，要创建一个新的安全组，您只需要运行一次此应用程序。

**注意**  
您还可以使用 Amazon Toolkit for Eclipse 创建安全组。有关更多信息，请参阅《Amazon Toolkit for Eclipse User Guide》中的 [Managing Security Groups from Amazon Cost Explorer](https://docs.amazonaws.cn/toolkit-for-eclipse/v1/user-guide/tke-sg.html)。

## 详细 Spot 实例请求创建选项
<a name="tutor-spot-adv-req-opts"></a>

正如我们在[教程：Amazon EC2 竞价型实例](tutorial-spot-instances-java.md)中所介绍的，您需要通过实例类型、亚马逊机器映像 (AMI) 和最高竞标价格来创建您的请求。

让我们从创建 `RequestSpotInstanceRequest` 对象开始。请求数据元需要您所需的实例数量和竞标价格。此外，我们需要为请求设置 `LaunchSpecification`，包括实例类型、AMI ID 和您需要使用的安全组。填入请求后，我们将在 `requestSpotInstances` 数据元上调用 `AmazonEC2Client` 方法。以下是如何申请 Spot 实例的一个示例。

（以下代码与我们在第一个教程中使用的代码相同。）

```
// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 1 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(1));

// Set up the specifications of the launch. This includes the
// instance type (e.g. t1.micro) and the latest Amazon Linux
// AMI id available. Note, you should always use the latest
// Amazon Linux AMI id or another of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

## 持久性请求和一次性请求
<a name="tutor-spot-adv-persist-v-one"></a>

建立一个 Spot 请求时，您可以指定几个可选参数。首先是您的请求是一次性的还是持久性的。默认情况下，一般是一次性请求。一次性请求可以只执行一次，请求实例终止后，请求将被关闭。同一请求中没有 Spot 实例运行的任何时候，持久性请求都被视为已完成。要指定请求的类型，您只需要设置 Spot 请求的类型。您可以使用以下代码完成设置。

```
// Retrieves the credentials from an AWSCredentials.properties file.
AWSCredentials credentials = null;
try {
    credentials = new PropertiesCredentials(
        GettingStartedApp.class.getResourceAsStream("AwsCredentials.properties"));
}
catch (IOException e1) {
    System.out.println(
        "Credentials were not properly entered into AwsCredentials.properties.");
    System.out.println(e1.getMessage());
    System.exit(-1);
}

// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest =
    new RequestSpotInstancesRequest();

// Request 1 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(1));

// Set the type of the bid to persistent.
requestRequest.setType("persistent");

// Set up the specifications of the launch. This includes the
// instance type (e.g. t1.micro) and the latest Amazon Linux
// AMI id available. Note, you should always use the latest
// Amazon Linux AMI id or another of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

## 限制请求的持续时间
<a name="tutor-spot-adv-validity-period"></a>

您还可以有选择地指定您的请求持续有效的时长。您可以指定有效期开始和结束的时间。默认情况下，从创建那一刻开始，系统将默认执行 Spot 请求，直到该请求完成或被取消。然而，如果您有需要，您可以限制有效期。以下代码显示了如何指定有效期的示例。

```
// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 1 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(1));

// Set the valid start time to be two minutes from now.
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, 2);
requestRequest.setValidFrom(cal.getTime());

// Set the valid end time to be two minutes and two hours from now.
cal.add(Calendar.HOUR, 2);
requestRequest.setValidUntil(cal.getTime());

// Set up the specifications of the launch. This includes
// the instance type (e.g. t1.micro)

// and the latest Amazon Linux AMI id available.
// Note, you should always use the latest Amazon
// Linux AMI id or another of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType("t1.micro");

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult = ec2.requestSpotInstances(requestRequest);
```

## 将您的 Amazon EC2 Spot 实例请求分组
<a name="tutor-spot-adv-grouping"></a>

您可以选择几种不同方法为您的 Spot 实例请求分组。我们来看看使用启动组、可用区组和置放组的好处。

如果您想要确保您的 Spot 实例全部一起启动和终止，您可以选择利用启动组。启动组是将一系列竞价分在一组的标签。启动组内的所有实例都一起启动和终止。请注意，如果启动组内的实例已经完成了，不能保证同一启动组新启动的实例也随之完成。以下代码示例显示了如何设置启动组的示例。

```
// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 5 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(5));

// Set the launch group.
requestRequest.setLaunchGroup("ADVANCED-DEMO-LAUNCH-GROUP");

// Set up the specifications of the launch. This includes
// the instance type (e.g. t1.micro) and the latest Amazon Linux
// AMI id available. Note, you should always use the latest
// Amazon Linux AMI id or another of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

如果您要确保请求中的所有实例在同一可用区内启动，而对具体哪个可用区并没有要求，您可以利用可用区组。可用区域组是将一系列同一可用区域内的实例分在一组的标签。共享同一可用区域组并同时完成的所有实例将在同一可用区域内开始运行。以下是如何设置可用区域组的一个示例。

```
// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 5 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(5));

// Set the availability zone group.
requestRequest.setAvailabilityZoneGroup("ADVANCED-DEMO-AZ-GROUP");

// Set up the specifications of the launch.  This includes the instance
// type (e.g.  t1.micro) and the latest Amazon Linux AMI id available.
// Note, you should always use the latest Amazon Linux AMI id or another
// of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

您可以为您的 Spot 实例指定一个可用区域。以下代码示例为您显示如何设置可用区域。

```
// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 1 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(1));

// Set up the specifications of the launch. This includes the instance
// type (e.g. t1.micro) and the latest Amazon Linux AMI id available.
// Note, you should always use the latest Amazon Linux AMI id or another
// of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Set up the availability zone to use. Note we could retrieve the
// availability zones using the ec2.describeAvailabilityZones() API. For
// this demo we will just use us-east-1a.
SpotPlacement placement = new SpotPlacement("us-east-1b");
launchSpecification.setPlacement(placement);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

最后，如果您使用的是高性能计算 (HPC) Spot 实例（例如，集群计算实例或集群 GPU 实例），则您可以指定一个*置放群组*。置放组为您提供更低的延迟和实例之间的高带宽连接。以下是如何设置置放组的一个示例。

```
// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 1 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(1));

// Set up the specifications of the launch. This includes the instance
// type (e.g. t1.micro) and the latest Amazon Linux AMI id available.
// Note, you should always use the latest Amazon Linux AMI id or another
// of your choosing.

LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Set up the placement group to use with whatever name you desire.
// For this demo we will just use "ADVANCED-DEMO-PLACEMENT-GROUP".
SpotPlacement placement = new SpotPlacement();
placement.setGroupName("ADVANCED-DEMO-PLACEMENT-GROUP");
launchSpecification.setPlacement(placement);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

本节所显示的所有参数都是可选的。同样重要的是要认识到，这些参数中的大多数（您的出价是一次性还是永久性出价除外）都可能降低出价实现的可能性。因此，只有当您需要的时候才利用这些选择，这很重要。所有前面的代码示例被组合成一个长代码示例，可在 `com.amazonaws.codesamples.advanced.InlineGettingStartedCodeSampleApp.java` 类别中找到。

## 中断或终止发生后，如何持久保存一个根分区
<a name="tutor-spot-adv-persist-root"></a>

管理竞价型实例中断最简单的方法之一是确保定期为您的数据执行到 Amazon Elastic Block Store (Amazon Amazon EBS) 卷的检查点操作。通过定期执行点校验，如果发生中断，您只会丢失上一次点校验后创建的数据（假设中间没有执行其他非幂等操作）。为了使这个过程变得更容易，您可以配置您的 Spot 请求，以确保中断或终止发生时您的根分区不会被删除。在以下如何实现此方案的示例中，我们插入了新代码。

在添加的代码中，我们创建了一个 `BlockDeviceMapping` 对象，并将与其相关联的 Amazon Elastic Block Store (Amazon EBS) 设置到 Amazon EBS 对象，之前我们已对此对象进行配置，如果竞价型实例被终止，此对象 `not` 随之删除。然后，我们将此 `BlockDeviceMapping` 添加到启动说明中所包含的映射的 ArrayList。

```
// Retrieves the credentials from an AWSCredentials.properties file.
AWSCredentials credentials = null;
try {
    credentials = new PropertiesCredentials(
        GettingStartedApp.class.getResourceAsStream("AwsCredentials.properties"));
}
catch (IOException e1) {
    System.out.println(
        "Credentials were not properly entered into AwsCredentials.properties.");
    System.out.println(e1.getMessage());
    System.exit(-1);
}

// Create the AmazonEC2 client so we can call various APIs.
AmazonEC2 ec2 = AmazonEC2ClientBuilder.defaultClient();

// Initializes a Spot Instance Request
RequestSpotInstancesRequest requestRequest = new RequestSpotInstancesRequest();

// Request 1 x t1.micro instance with a bid price of $0.03.
requestRequest.setSpotPrice("0.03");
requestRequest.setInstanceCount(Integer.valueOf(1));

// Set up the specifications of the launch. This includes the instance
// type (e.g. t1.micro) and the latest Amazon Linux AMI id available.
// Note, you should always use the latest Amazon Linux AMI id or another
// of your choosing.
LaunchSpecification launchSpecification = new LaunchSpecification();
launchSpecification.setImageId("ami-a9d09ed1");
launchSpecification.setInstanceType(InstanceType.T1Micro);

// Add the security group to the request.
ArrayList<String> securityGroups = new ArrayList<String>();
securityGroups.add("GettingStartedGroup");
launchSpecification.setSecurityGroups(securityGroups);

// Create the block device mapping to describe the root partition.
BlockDeviceMapping blockDeviceMapping = new BlockDeviceMapping();
blockDeviceMapping.setDeviceName("/dev/sda1");

// Set the delete on termination flag to false.
EbsBlockDevice ebs = new EbsBlockDevice();
ebs.setDeleteOnTermination(Boolean.FALSE);
blockDeviceMapping.setEbs(ebs);

// Add the block device mapping to the block list.
ArrayList<BlockDeviceMapping> blockList = new ArrayList<BlockDeviceMapping>();
blockList.add(blockDeviceMapping);

// Set the block device mapping configuration in the launch specifications.
launchSpecification.setBlockDeviceMappings(blockList);

// Add the launch specification.
requestRequest.setLaunchSpecification(launchSpecification);

// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult =
    ec2.requestSpotInstances(requestRequest);
```

如果您想在启动时重新连接该卷到您的实例中，也可以使用数据块存储设备映射设置。另外，如果您连接了一个非根分区，则可以指定您希望在竞价型实例恢复后连接到该实例的 Amazon Amazon EBS 卷。要实现此功能，您只需指定 `EbsBlockDevice` 数据元中的快照 ID 和 `BlockDeviceMapping` 数据元中的替代设备名称。通过利用数据块存储设备映射，可以让引导实例变得更加容易。

使用根分区来对您的关键数据执行点校验是管理您的实例可能性中断的好方法。有关更多管理可能性中断的方法，请参阅“[Managing Interruption](https://www.youtube.com/watch?feature=player_embedded&v=wcPNnUo60pc)”视频。

## 如何标记您的 Spot 请求和实例
<a name="tutor-spot-adv-tags"></a>

为 Amazon EC2 资源添加标签能简化您的云基础设施管理。某种形式的元数据、标记可用于创建用户友好型名称、增强搜索能力，并改善多个用户之间的协作。您也可以使用标记来自动化脚本和部分进程。要阅读有关为 Amazon EC2 资源添加标签的更多信息，请转至《Amazon EC2 用户指南（适用于 Linux 实例）》中的[使用标签](https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/Using_Tags.html)。

### 添加标签请求
<a name="tagging-requests"></a>

要为您的 Spot 请求添加标签，您需要在提交请求*之后* 为它们添加标签。来自 `requestSpotInstances()` 的返回值将为您提供一个 [RequestSpotInstancesResult](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/ec2/model/RequestSpotInstancesResult.html) 对象，此对象可用于获取 Spot 请求 ID 以便添加标签：

```
// Call the RequestSpotInstance API.
RequestSpotInstancesResult requestResult = ec2.requestSpotInstances(requestRequest);
List<SpotInstanceRequest> requestResponses = requestResult.getSpotInstanceRequests();

// A list of request IDs to tag
ArrayList<String> spotInstanceRequestIds = new ArrayList<String>();

// Add the request ids to the hashset, so we can determine when they hit the
// active state.
for (SpotInstanceRequest requestResponse : requestResponses) {
    System.out.println("Created Spot Request: "+requestResponse.getSpotInstanceRequestId());
    spotInstanceRequestIds.add(requestResponse.getSpotInstanceRequestId());
}
```

在获得 ID 后，您可以通过将请求 ID 添加到 [CreateTagsRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/ec2/model/CreateTagsRequest.html) 并调用 Amazon EC2 客户端的 `createTags()` 方法来为请求添加标签：

```
// The list of tags to create
ArrayList<Tag> requestTags = new ArrayList<Tag>();
requestTags.add(new Tag("keyname1","value1"));

// Create the tag request
CreateTagsRequest createTagsRequest_requests = new CreateTagsRequest();
createTagsRequest_requests.setResources(spotInstanceRequestIds);
createTagsRequest_requests.setTags(requestTags);

// Tag the spot request
try {
    ec2.createTags(createTagsRequest_requests);
}
catch (AmazonServiceException e) {
    System.out.println("Error terminating instances");
    System.out.println("Caught Exception: " + e.getMessage());
    System.out.println("Reponse Status Code: " + e.getStatusCode());
    System.out.println("Error Code: " + e.getErrorCode());
    System.out.println("Request ID: " + e.getRequestId());
}
```

### 标记实例
<a name="tagging-instances"></a>

与 Spot 请求本身类似，您只能在创建实例后为其添加标签，此操作将在满足 Spot 请求后 (不再处于*打开* 状态) 立即执行。

您可以通过使用 [DescribeSpotInstanceRequestsRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/ec2/model/DescribeSpotInstanceRequestsRequest.html) 对象调用 Amazon EC2 客户端的 `describeSpotInstanceRequests()` 方法来检查请求的状态。返回的 [DescribeSpotInstanceRequestsResult](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/ec2/model/DescribeSpotInstanceRequestsResult.html) 对象包含 [SpotInstanceRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/ec2/model/SpotInstanceRequest.html) 对象的列表，可使用这些对象查询 Spot 请求的状态并在其不再处于*打开* 状态后获取其实例 ID。

在 Spot 请求不在处于打开状态后，您可以通过调用其 `SpotInstanceRequest` 方法从 `getInstanceId()` 对象检索其实例 ID。

```
boolean anyOpen; // tracks whether any requests are still open

// a list of instances to tag.
ArrayList<String> instanceIds = new ArrayList<String>();

do {
    DescribeSpotInstanceRequestsRequest describeRequest =
        new DescribeSpotInstanceRequestsRequest();
    describeRequest.setSpotInstanceRequestIds(spotInstanceRequestIds);

    anyOpen=false; // assume no requests are still open

    try {
        // Get the requests to monitor
        DescribeSpotInstanceRequestsResult describeResult =
            ec2.describeSpotInstanceRequests(describeRequest);

        List<SpotInstanceRequest> describeResponses =
            describeResult.getSpotInstanceRequests();

        // are any requests open?
        for (SpotInstanceRequest describeResponse : describeResponses) {
                if (describeResponse.getState().equals("open")) {
                    anyOpen = true;
                    break;
                }
                // get the corresponding instance ID of the spot request
                instanceIds.add(describeResponse.getInstanceId());
        }
    }
    catch (AmazonServiceException e) {
        // Don't break the loop due to an exception (it may be a temporary issue)
        anyOpen = true;
    }

    try {
        Thread.sleep(60*1000); // sleep 60s.
    }
    catch (Exception e) {
        // Do nothing if the thread woke up early.
    }
} while (anyOpen);
```

现在，您可以为返回的实例添加标签：

```
// Create a list of tags to create
ArrayList<Tag> instanceTags = new ArrayList<Tag>();
instanceTags.add(new Tag("keyname1","value1"));

// Create the tag request
CreateTagsRequest createTagsRequest_instances = new CreateTagsRequest();
createTagsRequest_instances.setResources(instanceIds);
createTagsRequest_instances.setTags(instanceTags);

// Tag the instance
try {
    ec2.createTags(createTagsRequest_instances);
}
catch (AmazonServiceException e) {
    // Write out any exceptions that may have occurred.
    System.out.println("Error terminating instances");
    System.out.println("Caught Exception: " + e.getMessage());
    System.out.println("Reponse Status Code: " + e.getStatusCode());
    System.out.println("Error Code: " + e.getErrorCode());
    System.out.println("Request ID: " + e.getRequestId());
}
```

## 取消 Spot 请求并终止实例
<a name="canceling-spot-requests-and-terminating-instances"></a>

### 取消 Spot 请求
<a name="canceling-a-spot-request"></a>

要取消竞价型实例请求，请使用 [CancelSpotInstanceRequestsRequest](https://docs.amazonaws.cn/sdk-for-java/v1/reference/com/amazonaws/services/ec2/model/CancelSpotInstanceRequestsRequest.html) 对象调用 Amazon EC2 客户端上的 `cancelSpotInstanceRequests`。

```
try {
    CancelSpotInstanceRequestsRequest cancelRequest = new CancelSpotInstanceRequestsRequest(spotInstanceRequestIds);
    ec2.cancelSpotInstanceRequests(cancelRequest);
} catch (AmazonServiceException e) {
    System.out.println("Error cancelling instances");
    System.out.println("Caught Exception: " + e.getMessage());
    System.out.println("Reponse Status Code: " + e.getStatusCode());
    System.out.println("Error Code: " + e.getErrorCode());
    System.out.println("Request ID: " + e.getRequestId());
}
```

### 终止 Spot 实例
<a name="terminating-spot-instances"></a>

您可以通过将任何正在运行的竞价型实例的 ID 传递到 Amazon EC2 客户端的 `terminateInstances()` 方法来终止该实例。

```
try {
    TerminateInstancesRequest terminateRequest = new TerminateInstancesRequest(instanceIds);
    ec2.terminateInstances(terminateRequest);
} catch (AmazonServiceException e) {
    System.out.println("Error terminating instances");
    System.out.println("Caught Exception: " + e.getMessage());
    System.out.println("Reponse Status Code: " + e.getStatusCode());
    System.out.println("Error Code: " + e.getErrorCode());
    System.out.println("Request ID: " + e.getRequestId());
}
```

## 综述
<a name="tutor-spot-adv-bring-together"></a>

综述起来，我们提供一种更加以数据元为导向的方法，将此教程中所示步骤结合到一个易于使用的类别。我们将执行这些操作的一个被称为 `Requests` 的类别实例化。我们还创建了一个 `GettingStartedApp` 类，为我们执行高级函数调用提供主要方法。

可在 [GitHub](https://github.com/aws/aws-sdk-java/tree/master/src/samples/AmazonEC2SpotInstances-Advanced) 查看和下载此示例的完整源代码。

恭喜您！您已经学完了“高级请求功能”教程，了解如何使用适用于 Java 的 Amazon SDK 开发 Spot 实例软件。