本文介绍了如何通过阿里云ECS Java SDK调用DescribeAvailableResource和DescribeSpotPriceHistory查询抢占式实例的供应情况和历史低价,再调用CreateInstance创建抢占式ECS实例。

前提条件

  • 您使用的aliyun-java-sdk-ecs版本必须大于等于4.2.0。
  • 创建抢占式ECS实例的镜像必须包含了实例运行的所以环境依赖。建议您使用自定义镜像创建实例,保证抢占式ECS实例能快速处理业务数据。假设为m-bp146shijn7hujkui9***
  • 调用DescribeRegions查询ECS实例启动的阿里云地域,假设为cn-hangzhou
  • 调用DescribeInstanceTypes查询ECS实例使用的实例规格,假设为ecs.g5.large。详情请参见实例规格族

背景信息

抢占式实例能够帮助您建立低价处理业务数据的方案。创建抢占式实例时,您需要指定出价模式,当指定的实例规格当前市场价格低于您的出价时,您就能成功创建抢占式实例,并按当前市场价格计费。抢占式实例常被运用于控制计算成本、适用于无状态的应用场景。更多详情,请参见什么是抢占式实例

操作步骤

  1. 封装一个ApiCaller.java类,完成初始化profile和client,以及错误处理等功能。
    public class ApiCaller {
        IClientProfile profile;
        IAcsClient client;
    
        public ApiCaller() {
            profile = DefaultProfile.getProfile("cn-hangzhou", AKSUtil.accessKeyId, AKSUtil.accessKeySecret);
            client = new DefaultAcsClient(profile);
        }
    
        public  <T extends AcsResponse> T doAction(AcsRequest<T> var1) {
            try {
                return  client.getAcsResponse(var1);
            } catch (ServerException e) {
                e.printStackTrace();
            } catch (ClientException e) {
                System.out.println("ErrCode:" + e.getErrCode());
                System.out.println("ErrMsg:" + e.getErrMsg());
                System.out.println("RequestId:" + e.getRequestId());
            }
        }
    
     }
  2. 调用DescribeAvailableResource查询指定地域中的抢占式实例提供的实例规格。
    public class DescribeAvailableResourceSample {
    
        public static void main(String[] args) {
    
            ApiCaller caller = new ApiCaller();
            DescribeAvailableResourceRequest request = new DescribeAvailableResourceRequest();
            // 通过DescribeRegionsRequest获取地域ID。
            request.setRegionId("cn-hangzhou");
            request.setDestinationResource("InstanceType");
            // 设置为按量付费,必填。
            request.setInstanceChargeType("PostPaid");
            // 查询是否可以购买抢占式实例,必填。
            request.setSpotStrategy("SpotAsPriceGo");
            // 不设置可用区ID则默认查询所有可以创建抢占式实例的可用区。
            request.setZoneId("cn-hangzhou-h");
            // 不设置实例规格则默认查询所有可以创建抢占式实例的实例规格。
            request.setInstanceType("ecs.g5.large");
            request.setSystemDiskCategory("cloud_ssd");
            request.setNetworkCategory("vpc");
    
            DescribeAvailableResourceResponse response = caller.doAction(request);
            System.out.println(JSON.toJSONString(response));
        }
    }
    输出结果示例:
    {
        "RequestId": "D8491D5E-AB8A-4E22-BDB4-EEEE1F1C8241",
        "AvailableZones": {
            "AvailableZone": [
                {
                    "Status": "Available",
                    "RegionId": "cn-hangzhou",
                    "AvailableResources": {
                        "AvailableResource": [
                            {
                                "Type": "InstanceType",
                                "SupportedResources": {
                                    "SupportedResource": [
                                        {
                                            "Status": "Available",
                                            "Value": "ecs.g5.large",
                                            "StatusCategory": "WithStock"
                                        }
                                    ]
                                }
                            }
                        ]
                    },
                    "ZoneId": "cn-hangzhou-h",
                    "StatusCategory": "WithStock"
                }
            ]
        }
    }
  3. (可选)调用DescribeSpotPriceHistory查询抢占式实例历史价格变化。
    说明 DescribeSpotPriceHistory最多允许获取30天内的价格变化。
    public class DescribeSpotPriceHistorySample {
    
        public static void main(String[] args) {
    
            ApiCaller caller = new ApiCaller();
            List<DescribeSpotPriceHistoryResponse.SpotPriceType> result = new ArrayList<DescribeSpotPriceHistoryResponse.SpotPriceType>();
            int offset = 0;
            while (true) {
                DescribeSpotPriceHistoryRequest request = new DescribeSpotPriceHistoryRequest();
                request.setRegionId("cn-hangzhou");// 地域必填。
                request.setZoneId("cn-hangzhou-b");// 可用区必填。
                request.setInstanceType("ecs.g5.large");// 实例规格必填。
                request.setNetworkType("vpc");// 网络类型必填。
                // request.setStartTime("2017-09-20T08:45:08Z");// 价格开始时间,选填,默认3天内数据。
                // request.setEndTime("2017-09-28T08:45:08Z");// 价格结束时间,选填。
                request.setOffset(offset);
                DescribeSpotPriceHistoryResponse response = caller.doAction(request);
                if (response != null && response.getSpotPrices() != null) {
                    result.addAll(response.getSpotPrices());
                }
                if (response.getNextOffset() == null || response.getNextOffset() == 0) {
                    break;
                } else {
                    offset = response.getNextOffset();
                }
            }
            if (!result.isEmpty()) {
                for (DescribeSpotPriceHistoryResponse.SpotPriceType spotPriceType : result) {
                    System.out.println(spotPriceType.getTimestamp() + "--->spotPrice:" + spotPriceType.getSpotPrice() + "---->originPrice:" + spotPriceType.getOriginPrice());
                }
                System.out.println(result.size());
            } else {
            }
    
        }
    
    }
    输出结果示例:
    2017-09-26T06:28:55Z--->spotPrice:0.24---->originPrice:1.2
    2017-09-26T14:00:00Z--->spotPrice:0.36---->originPrice:1.2
    2017-09-26T15:00:00Z--->spotPrice:0.24---->originPrice:1.2
    2017-09-27T14:00:00Z--->spotPrice:0.36---->originPrice:1.2
    2017-09-27T15:00:00Z--->spotPrice:0.24---->originPrice:1.2
    2017-09-28T14:00:00Z--->spotPrice:0.36---->originPrice:1.2
    2017-09-28T15:00:00Z--->spotPrice:0.24---->originPrice:1.2
    2017-09-29T06:28:55Z--->spotPrice:0.24---->originPrice:1.2
  4. 调用CreateInstances创建一台抢占式实例。

    如果您需要批量创建多台抢占式实例,可以调用RunInstance。详情请参见批量创建ECS实例

    public class CreateInstancesSample {
    
        public static void main(String[] args) {
            ApiCaller caller = new ApiCaller();
            CreateInstancesRequest request = new CreateInstancesRequest();
            request.setRegionId("cn-hangzhou");// 地域ID。
            request.setZoneId("cn-hangzhou-h"); // 可用区ID。
            request.setSecurityGroupId("sg-bp11nhf94ivkdxwb2***");// 安全组ID。
            request.setImageId("m-bp146shijn7hujkui9***");// 建议您使用自定义镜像,可以快速承载业务。
            request.setVSwitchId("vsw-bp164cyonthfudn9kj***");// 与安全组同属于一个专有网络VPC的虚拟交换机ID。
    
            request.setInstanceType("ecs.g5.large"); // 填入您询价后需要购买的规格。
            request.setSystemDiskCategory("cloud_ssd");// 系统盘类型,支持cloud_essd,cloud_ssd,cloud_efficiency,cloud。
            request.setSystemDiskSize(40);
    
            request.setInstanceChargeType("PostPaid");// 设置为按量付费,必填。
            request.setSpotStrategy("SpotWithPriceLimit");// SpotWithPriceLimit出价模式,SpotAsPriceGo不用出价,最高按量付费价格。
            request.setSpotPriceLimit(0.25F);// SpotWithPriceLimit出价模式生效,您能接受的最高价格,必须高于市场成交价才能运行实例。
            // request.setAmount(2);// 创建两台抢占式实例,不设置该参数则默认创建一台实例。
    
            CreateInstanceResponse response = caller.doAction(request);
            System.out.println(response.getInstanceId());
    
        }
    }
  5. 回收抢占式实例。
    说明 由于抢占式实例的生命周期较短,您需要在成功创建抢占式实例的一小时内完成处理中断的工作,避免抢占式实例被释放带来的不便。
    1. 在ECS实例操作系统内运行以下命令,通过实例元数据查询抢占式实例的释放时间。
      curl 'http://100.100.100.200/latest/meta-data/instance/spot/termination-time'
      说明 如果返回信息为空,说明抢占式实例可以继续被使用。如果返回类似2015-01-05T18:02:00Z格式的信息(UTC+0时间),说明抢占式实例将于该时间点被回收。
    2. 调用DescribeInstances根据返回的OperationLocks判断实例是否进入待回收状态。
      public class DescribeInstancesSample {
        public static void main(String[] args) throws InterruptedException {
            ApiCaller caller = new ApiCaller();
            JSONArray allInstances = new JSONArray();
            allInstances.addAll(Arrays.asList("i-bp18hgfai8ekoqwo0***", "i-bp1ecbyds24ij63w1***"));
            while (!allInstances.isEmpty()) {
                DescribeInstancesRequest request = new DescribeInstancesRequest();
                request.setRegionId("cn-hangzhou");
                request.setInstanceIds(allInstances.toJSONString());// 指定实例ID,提高效率。
                DescribeInstancesResponse response = caller.doAction(request);
                List<DescribeInstancesResponse.Instance> instanceList = response.getInstances();
                if (instanceList != null && !instanceList.isEmpty()) {
                    for (DescribeInstancesResponse.Instance instance : instanceList) {
                        System.out.println("result:instance:" + instance.getInstanceId() + ",was created in zone:" + instance.getZoneId());
                        if (instance.getOperationLocks() != null) {
                            for (DescribeInstancesResponse.Instance.LockReason lockReason : instance.getOperationLocks()) {
                                System.out.println("instance: " + instance.getInstanceId() + "-->lockReason:" + lockReason.getLockReason() + ",vmStatus:" + instance.getStatus());
                                if ("Recycling".equals(lockReason.getLockReason())) {
                                    // 输出回收信息并从实例ID列表中删除被回收的实例ID。
                                    System.out.println("Preemptible instance will be recycled immediately, instance id: " + instance.getInstanceId());
                                    allInstances.remove(instance.getInstanceId());
                                }
                            }
                        }
                    }
                    System.out.print("Try describeInstancesRequest again later...");
                    Thread.sleep(2 * 60 * 1000);
                } else {
                    break;
                }
            }
        }
      }

      触发回收抢占式实例输出结果如下:

      instance: i-bp1ecbyds24ij63w1***-->lockReason:Recycling,vmStatus:Stopped
      Preemptible instance will be recycled immediately, instance id: i-bp1ecbyds24ij63w1***