本文介绍如何通过阿里云ECS Java SDK调用DescribeRecommendInstanceType查询推荐或备选的实例规格信息,并提供了RunInstances与DescribeRecommendInstanceType搭配使用的最佳实践。

前提条件

您使用的aliyun-java-sdk-ecs版本必须大于等于4.23.8。

使用场景

说明 各个场景提供的代码示例仅供参考,您在实际调用过程中,请根据自身需求调整相应的参数配置。

指定vCPU核数和内存获取期望的实例规格

该场景中,将在杭州地域的可用区g下查询推荐的实例规格,且实例规格需要属于2 vCPU、4 GiB的企业级实例规格族。具体的void方法代码示例如下所示:

public void getRecommendInstanceTypeWithCoresAndMemory() throws ClientException {
    // 创建DefaultAcsClient实例并初始化。
    DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyID>", "<yourAccessKeySecret>");
    IAcsClient client = new DefaultAcsClient(profile);

    // 创建API请求,并设置参数。
    DescribeRecommendInstanceTypeRequest request = new DescribeRecommendInstanceTypeRequest();
    // 选择VPC。
    request.setNetworkType("vpc");
    // 选择I/O优化实例。
    request.setIoOptimized("optimized");
    // 选择按量付费的计费方式。
    request.setInstanceChargeType("PostPaid");
    // 竞价策略选择正常按量付费实例。
    request.setSpotStrategy("NoSpot");
    // 期望推荐的实例规格的vCPU核数和内存信息。
    request.setCores(2);
    request.setMemory(4.0f);
    // 选择企业级实例规格族。
    request.setInstanceFamilyLevel("EnterpriseLevel");
    // 限制在指定的实例规格族下推荐实例规格。
    request.setInstanceTypeFamilys(Arrays.asList("ecs.c6","ecs.c5","ecs.hfc5","ecs.hfc6","ecs.sn1ne"));
    // 期望的目标可用区,与创建实例时交换机(VSwitchId)所在可用区相同。
    request.setZoneId("cn-hangzhou-g");
    // 取值为Strict时,表示推荐实例规格限制在目标可用区下。
    // 取值为Include时,表示对目标可用区无要求时,优先在ZoneId指定可用区下推荐,若无合适结果,推荐范围扩大到地域下所有可用区。
    request.setZoneMatchMode("Strict");
    // 按照资源的库存充足程度排序,如需选择价格最合适的规格,可以传递PriceFirst参数值。
    request.setPriorityStrategy("InventoryFirst");

    //接收响应结果。
    DescribeRecommendInstanceTypeResponse response = client.getAcsResponse(request);
    DescribeRecommendInstanceTypeResponse.RecommendInstanceType recommendType = null;
    if(response != null && response.getData().size() != 0){
        recommendType = response.getData().get(0);
    }
    if(recommendType != null) {
        //如果有匹配的实例规格信息,返回实例规格、可用区、地域、计费方式、竞价策略信息。
        System.out.println("recommend instanceType: " + recommendType.getInstanceType().getInstanceType());
        System.out.println("recommend zoneId: " + recommendType.getZoneId());
        System.out.println("recommend regionId: " + recommendType.getRegionId());
        System.out.println("recommend chargeType: " + recommendType.getInstanceChargeType());
        System.out.println("recommend spotStrategy: " + recommendType.getSpotStrategy());
    }else {
        //如果没有匹配的实例规格信息,返回无可用资源的相关提示。
        System.out.println("no available instanceType");
    }
}
通过 main方法调用 getRecommendInstanceTypeWithCoresAndMemory方法,返回符合条件的实例规格信息。返回值示例如下: DescribeRecommendInstanceType

指定实例规格获取一个或多个备选的实例规格

该场景中,将在杭州地域的可用区g下,查询可替代ecs.c6.large实例规格的库存充足的备选实例规格,且推荐的备选实例规格需要在指定的目标实例规格族下。具体的void方法代码示例如下所示。

说明 返回的备选实例规格的vCPU( Cores)和内存( Memory)配置可能与指定的原实例规格不一致。例如:ecs.mn4.small(1 vCPU、4 GiB)实例规格库存不足时,可能会返回ecs.n4.small(1 vCPU、2 GiB)作为替代规格。
public void getRecommendInstanceType() throws ClientException {
    // 创建DefaultAcsClient实例并初始化。
    DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyID>", "<yourAccessKeySecret>");
    IAcsClient client = new DefaultAcsClient(profile);

    // 创建API请求,并设置参数。
    DescribeRecommendInstanceTypeRequest request = new DescribeRecommendInstanceTypeRequest();
    // 选择VPC。
    request.setNetworkType("vpc");
    // 选择I/O优化实例。
    request.setIoOptimized("optimized");
    // 选择按量付费的计费方式。
    request.setInstanceChargeType("PostPaid");
    // 竞价策略选择正常按量付费实例。
    request.setSpotStrategy("NoSpot");
    // 原实例规格信息。
    request.setInstanceType("ecs.c6.large");
    // 限制在指定的实例规格族下推荐实例规格。
    request.setInstanceTypeFamilys(Arrays.asList("ecs.c6","ecs.c5","ecs.hfc5","ecs.hfc6","ecs.sn1ne"));
    // 期望的目标可用区,与创建实例时交换机(VSwitchId)所在可用区相同。
    request.setZoneId("cn-hangzhou-g");
    // 取值为Strict时,表示推荐实例规格限制在目标可用区下。
    // 取值为Include时,表示对目标可用区无要求时,优先在ZoneId指定可用区下推荐,若无合适结果,推荐范围扩大到地域下所有可用区。
    request.setZoneMatchMode("Strict");
    // 按照资源的库存充足程度排序,如需选择价格最合适的规格,可以传递PriceFirst参数值。
    request.setPriorityStrategy("InventoryFirst");

    //接收响应结果。
    DescribeRecommendInstanceTypeResponse response = client.getAcsResponse(request);
    DescribeRecommendInstanceTypeResponse.RecommendInstanceType recommendType = null;
    if(response != null && response.getData().size() != 0){
        recommendType = response.getData().get(0);
    }
    if(recommendType != null) {
        //如果有匹配的实例规格信息,返回实例规格、可用区、地域、计费方式、竞价策略信息。
        System.out.println("recommend instanceType: " + recommendType.getInstanceType().getInstanceType());
        System.out.println("recommend zoneId: " + recommendType.getZoneId());
        System.out.println("recommend regionId: " + recommendType.getRegionId());
        System.out.println("recommend chargeType: " + recommendType.getInstanceChargeType());
        System.out.println("recommend spotStrategy: " + recommendType.getSpotStrategy());
    }else {
        //如果没有匹配的实例规格信息,返回无可用资源的相关提示。
        System.out.println("no available instanceType");
    }
}
通过 main方法调用 getRecommendInstanceType方法,返回符合条件的实例规格信息。返回值示例如下: DescribeRecommendInstanceType

通过备选实例规格创建ECS实例最佳实践

该场景中,在调用RunInstances创建ECS实例时判断是否发生库存不足等错误,如果发生错误,将调用DescribeRecommendInstanceType查询备选实例,然后通过备选实例规格重新创建ECS实例。
注意 返回的备选实例规格的vCPU( Cores)和内存( Memory)配置可能与指定的原实例规格不一致。您可以通过指定 InstanceTypeFamily来约束备选实例规格族的范围。例如:ecs.mn4.small(1 vCPU、4 GiB)实例规格库存不足时,可能会返回ecs.n4.small(1 vCPU、2 GiB)作为替代规格。请确保您的实际需求对实例规格没有严格限制。
代码示例中,创建ECS实例时的部分配置说明如下:
  • 网络计费类型:按使用流量计费
  • 实例的付费方式:按量付费
  • 实例的网络类型:VPC,即需要指定交换机(VSwitchId
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.alibaba.fastjson.JSON;
import java.util.*;
import java.util.UUID;
import com.aliyuncs.ecs.model.v20140526.*;
public class RunInstances {
    public static void main(String[] args) throws ClientException {
        // 创建DefaultAcsClient实例并初始化。
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "<yourAccessKeyID>", "<yourAccessKeySecret>");
        IAcsClient client = new DefaultAcsClient(profile);

        // 创建API请求,并设置参数。
        RunInstancesRequest request = new RunInstancesRequest();
        request.setRegionId("cn-hangzhou");
        request.setImageId("centos_8_2_x64_20G_alibase_20201120.vhd");
        request.setInstanceType("ecs.hfg5.large");
        request.setSecurityGroupId("sg-bp17lchxr133z9gt****");
        //交换机ID,该代码示例中未设置可用区ID,则创建实例时默认选择交换机所在的可用区。
        request.setVSwitchId("vsw-bp1hl0stgh7jq4rsm****");
        request.setInstanceName("MyFirstEcsInstance");
        request.setDescription("MyFirstEcsInstance");
        request.setInternetMaxBandwidthOut(2);
        request.setInternetChargeType("PayByTraffic");
        request.setClientToken(UUID.randomUUID().toString());
        // 添加一块数据盘,数据盘类型为SSD云盘,容量为100 GiB,并开启了随ECS实例释放。
        List<RunInstancesRequest.DataDisk> dataDiskList = new ArrayList<RunInstancesRequest.DataDisk>();
        RunInstancesRequest.DataDisk dataDisk1 = new RunInstancesRequest.DataDisk();
        dataDisk1.setSize(100);
        dataDisk1.setCategory("cloud_efficiency");
        dataDisk1.setDeleteWithInstance(true);
        dataDiskList.add(dataDisk1);
        request.setDataDisks(dataDiskList);
        // 批量创建五台ECS实例,如果不设置该参数,默认创建一台ECS实例。
        // request.setAmount(5);
        // 如果缺少库存可以接受的最低创建数量。
        // request.setMinAmount(2);
        // 为ECS实例绑定标签。
        List<RunInstancesRequest.Tag> tagList = new ArrayList<RunInstancesRequest.Tag>();
        RunInstancesRequest.Tag tag1 = new RunInstancesRequest.Tag();
        tag1.setKey("TestKey");
        tag1.setValue("TestValue");
        tagList.add(tag1);
        request.setTags(tagList);
        // 打开预检请求功能,此时不会直接创建ECS实例,只检查参数正确性、请求格式、业务限制和ECS库存等问题。
        // 设置了DryRun参数后,Amount参数必须为1,MinAmount参数必须为空,本文代码示例中仅预检请求,您可以根据实际需求修改代码。
        request.setDryRun(true);
        request.setInstanceChargeType("PostPaid");

        // 发起请求并处理返回或异常信息。
        RunInstancesResponse response;
        try {
            response = client.getAcsResponse(request);
            System.out.println(JSON.toJSONString(response));
        } 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());
            // 原实例规格创建ECS实例失败,需要寻找替代实例规格的错误码。
            List<String> noStockCode = Arrays.asList("OperationDenied.NoStock","Zone.NotOnSale","InvalidResourceType.NotSupported","InstanceType.Offline");
            // 如果匹配错误码,将查询推荐的备选实例规格信息。
            if(noStockCode.contains(e.getErrCode())){
                DescribeRecommendInstanceTypeRequest request1 = new DescribeRecommendInstanceTypeRequest();
                // 目标网络类型。当指定VswitchId时,为VPC网络类型。
                if((request.getVSwitchId()).length()!=0){
                    request1.setNetworkType("vpc");
                }
                // 默认按照资源的库存充足的程度排序。
                request1.setPriorityStrategy("InventoryFirst");
                // 实例相关的参数信息与创建ECS实例时保持一致。
                request1.setIoOptimized(request.getIoOptimized());
                request1.setInstanceChargeType(request.getInstanceChargeType());
                request1.setSpotStrategy(request.getSpotStrategy());
                request1.setMaxPrice(request.getSpotPriceLimit());
                request1.setInstanceType(request.getInstanceType());
                // 限制推荐的实例规格族的范围。
                request1.setInstanceTypeFamilys(Arrays.asList("ecs.g5","ecs.g6","ecs.g6e","ecs.hfc5","ecs.hfg7","ecs.sn1ne"));
                String zoneId = request.getZoneId();
                // 根据VSwitch所在可用区选择ZoneId。
                if((request.getVSwitchId()).length()!=0){
                    DescribeVSwitchesRequest request2 = new DescribeVSwitchesRequest();
                    request2.setVSwitchId(request.getVSwitchId());
                    request2.setRegionId(request.getRegionId());
                    DescribeVSwitchesResponse response2 = client.getAcsResponse(request2);
                    if(response2 != null){
                        zoneId = response2.getVSwitches().get(0).getZoneId();
                    }
                }
                request1.setZoneId(zoneId);
                // 如果需要限制在原可用区下推荐备选的实例规格,则需要设置可用区的匹配模式为Strict。
                if((zoneId).length()!=0){
                    request1.setZoneMatchMode("Strict");
                }

                //接收响应结果。
                DescribeRecommendInstanceTypeResponse response1 = client.getAcsResponse(request1);
                DescribeRecommendInstanceTypeResponse.RecommendInstanceType recommendType = null;
                if(response1 != null && response1.getData().size() != 0){
                    // 查询原实例规格的详细信息。
                    DescribeInstanceTypesRequest typeRequest = new DescribeInstanceTypesRequest();
                    typeRequest.setInstanceTypess(Arrays.asList(request.getInstanceType()));
                    typeRequest.setRegionId(request.getRegionId());
                    DescribeInstanceTypesResponse typeResponse = client.getAcsResponse(typeRequest);
                    DescribeInstanceTypesResponse.InstanceType originType = null;
                    if(typeResponse != null && typeResponse.getInstanceTypes().size() != 0){
                        originType = typeResponse.getInstanceTypes().get(0);
                    }
                    for(DescribeRecommendInstanceTypeResponse.RecommendInstanceType type: response1.getData()){
                        if(request.getInstanceType().equals(type.getInstanceType())){
                            continue;
                        }
                        // 您可以增加一些实例规格过滤条件,比如要求备选的目标实例规格与原实例规格有相同的核数和内存大小。
                        if(originType != null && originType.getCpuCoreCount().equals(type.getInstanceType().getCores())
                                && type.getInstanceType().getMemory().equals(originType.getMemorySize() * 1024)){
                            recommendType = type;
                            break;
                        }
                    }
                }
                // 输出备选方案信息。
                System.out.println("备选方案:");
                System.out.println("recommend instanceType: " + recommendType.getInstanceType().getInstanceType());
                System.out.println("recommend zoneId: " + recommendType.getZoneId());
                System.out.println("recommend regionId: " + recommendType.getRegionId());
                System.out.println("recommend chargeType: " + recommendType.getInstanceChargeType());
                System.out.println("recommend spotStrategy: " + recommendType.getSpotStrategy());
                // 替换创建ECS实例请求中的实例规格信息。
                request.setInstanceType(recommendType.getInstanceType().getInstanceType());
                // 替换创建ECS实例请求中的可用区信息。
                request.setZoneId(recommendType.getZoneId());
                request.setSystemDiskCategory("cloud_essd");

                response = client.getAcsResponse(request);
                System.out.println("使用备选方案创建实例的JSON返回结果:");
                System.out.println(JSON.toJSONString(response));
            }
        }
    }
}
  • 预检此次请求(DryRun=true)的返回值示例如下所示:DescribeRecommendInstanceType2
  • 不预检此次请求(DryRun=false),正常调用RunInstances并通过备选实例规格创建ECS实例的返回值示例如下所示:runinstances