ROSTemplateFormatVersion: '2015-09-01'
Description:
  zh-cn: 构建高性能秒杀系统。
  en: Build a high-performance flash sale system.
Parameters:
  ZoneId1:
    Type: String
    AssociationProperty: ALIYUN::ECS::Instance::ZoneId
    AssociationPropertyMetadata:
      ExclusiveTo:
        - ZoneId2
    Label:
      en: VSwitch Availability Zone 1
      zh-cn: 交换机可用区1
  ZoneId2:
    Type: String
    AssociationProperty: ALIYUN::ECS::Instance::ZoneId
    AssociationPropertyMetadata:
      ExclusiveTo:
        - ZoneId1
    Label:
      en: VSwitch Availability Zone 2
      zh-cn: 交换机可用区2
  ECSInstanceType1:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例规格
    Description:
      en: <font color='blue'><b>Before selecting a model, please confirm whether the model is in stock in the current availability zone. To save testing costs, it is recommended to use a model with 2 cores and 4G memory, for example:ecs.c7.large</b></font>.
      zh-cn: <font color='blue'><b>选择机型前请先确认当前可用区下该机型是否有库存，为节省测试成本，推荐使用2核4G内存的规格，例如：ecs.c7.large</b></font>。
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      Constraints:
        Memory:
          - 4
          - 6
          - 8
          - 10
          - 12
          - 14
          - 16
          - 18
          - 20
          - 22
          - 24
          - 26
          - 28
          - 30
          - 32
      ZoneId: ZoneId1
  ECSInstanceType2:
    Type: String
    Label:
      en: Instance Type
      zh-cn: 实例规格
    Description:
      en: <font color='blue'><b>Before selecting a model, please confirm whether the model is in stock in the current availability zone. To save testing costs, it is recommended to use a model with 2 cores and 4G memory, for example:ecs.c7.large</b></font>.
      zh-cn: <font color='blue'><b>选择机型前请先确认当前可用区下该机型是否有库存，为节省测试成本，推荐使用2核4G内存的规格，例如：ecs.c7.large</b></font>。
    AssociationProperty: ALIYUN::ECS::Instance::InstanceType
    AssociationPropertyMetadata:
      Constraints:
        Memory:
          - 4
          - 6
          - 8
          - 10
          - 12
          - 14
          - 16
          - 18
          - 20
          - 22
          - 24
          - 26
          - 28
          - 30
          - 32
      ZoneId: ZoneId2
  ECSPassword:
    Type: String
    Label:
      en: root account password
      zh-cn: root 账号密码
    Description:
      en: Server root account login password, Length 8-30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in).
      zh-cn: 服务器root账号登录密码,长度8-30，必须包含三项（大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号）。
    ConstraintDescription:
      en: Length 8-30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in).
      zh-cn: 长度8-30，必须包含三项（大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号）。
    AllowedPattern: '[0-9A-Za-z\_\-\&:;''<>,=%`~!@#\(\)\$\^\*\+\|\{\}\[\]\.\?\/]+$'
    MinLength: 8
    MaxLength: 30
    NoEcho: true
  PolarDBNodeClass:
    Type: String
    Label:
      en: PolarDB Node Class
      zh-cn: PolarDB Mysql资源规格大小
    Description:
      en: <font color='blue'><b>Before selecting a model, please confirm whether the model is in stock in the current availability zone. To save testing costs, it is recommended to use a model with 2 cores and 4G memory, for example:polar.mysql.g2.medium</b></font>, see detail:<a href='https://help.aliyun.com/zh/polardb/polardb-for-mysql/how-to-select-generic-and-exclusive-specifications' target='_blank'><b><font color='red'>Specification inquiry</font></b></a>.
      zh-cn: <font color='blue'><b>选择机型前请先确认当前可用区下该机型是否有库存，为节省测试成本，推荐使用2核4G内存的规格，例如：polar.mysql.g2.medium</b></font>，<a href='https://help.aliyun.com/zh/polardb/polardb-for-mysql/how-to-select-generic-and-exclusive-specifications' target='_blank'><b><font color='red'>规格查询</font></b></a>。
    AssociationProperty: ALIYUN::POLARDB::DBCluster::DBNodeClass
    AssociationPropertyMetadata:
      ZoneId: ZoneId1
      CommodityCode: polardb_payg
    Default: polar.mysql.g2.medium
  PolarAccountName:
    Type: String
    Label:
      en: Database Account
      zh-cn: 数据库账号
    Description:
      en: Maximum 16 characters,Consists of a lowercase letter, a number, an underscore, a letter beginning, a letter or a number ending.<br><b>note： <font color='blue'>Keywords cannot be used, e.g.:admin/root</font></b>
      zh-cn: 最长16个字符, 由小写字母，数字、下划线组成、字母开头，字母或数字结尾。<br><b>注： <font color='blue'>关键字不能用，如：admin/root</font></b>
    Default: polardb_admin
  DBAccountPassword:
    Type: String
    Label:
      en: Account password
      zh-cn: 数据库账号密码
    Description:
      en: Length 8-32 characters, can contain size letters, Numbers and special symbols (including:!@#$%^&*-+=_).
      zh-cn: 长度8-32个字符,可包含大小字母、数字及特殊符号（包含：!@#$%^&*-+=_）。
    ConstraintDescription:
      en: Length 8-32, can contain size letters, Numbers and special symbols (including:!@#$%^&*-+=_).
      zh-cn: 长度8-32,可包含大小字母、数字及特殊符号（包含：!@#$%^&*-+=_）。
    MinLength: '8'
    MaxLength: '32'
    NoEcho: true
  RedisClass:
    Type: String
    Label:
      en: Tair Specifications
      zh-cn: Tair 规格
    Description:
      en: <font color='blue'><b>Before selecting a model, please confirm whether the model is in stock in the current availability zone. To save testing costs, it is recommended to use a model with 2G memory, for example:tair.rdb.2g</b></font>, see detail:<a href='https://help.aliyun.com/zh/redis/product-overview/enhanced-performance' target='_blank'><b><font color='red'>Specification inquiry</font></b></a>.
      zh-cn: <font color='blue'><b>选择机型前请先确认当前可用区下该机型是否有库存，为节省测试成本，推荐使用2GB的规格，例如：tair.rdb.2g</b></font>，<a href='https://help.aliyun.com/zh/redis/product-overview/enhanced-performance' target='_blank'><b><font color='red'>规格查询</font></b></a>。
    AssociationProperty: ALIYUN::REDIS::Instance::InstanceClass
    AssociationPropertyMetadata:
      Engine: Redis
      ProductType: Tair_rdb
      InstanceChargeType: PostPaid
      ZoneId: ${ZoneId1}
      OrderType: BUY
    Default: tair.rdb.2g
  RedisAccountPassword:
    NoEcho: true
    Type: String
    Description:
      en: RDS user password, Length 8-30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in)
      zh-cn: 数据库账号密码，长度8-30，必须包含三项（大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号）
    Label:
      en: Tair Instance Password
      zh-cn: Tair 数据库密码
    ConstraintDescription:
      en: Length 8-30, must contain three(Capital letters, lowercase letters, numbers, ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ Special symbol in)
      zh-cn: 必须包含三种及以上类型：大写字母、小写字母、数字、特殊符号。长度为8～32位。特殊字符包括!@#$%^&*()_+-=
    AssociationProperty: ALIYUN::RDS::Instance::AccountPassword
  MseLicenseKey:
    Type: String
    Label:
      en: Current region License Key
      zh-cn: 当前环境 License Key
    Description:
      en: Log on <a href="https://mse.console.aliyun.com/#/msc/home" target="_blank"><b><font color='blue'>MSE console </font></a>, Click Governance Center > Overview in the left navigation bar. On the top menu bar, select the region where the ROS resource stack is created. Click the License Key link in the upper right corner of the overview page to obtain the MSE License Key.
      zh-cn: 登录<a href='https://mse.console.aliyun.com/#/msc/home' target='_blank'><b><font color='blue'>微服务引擎MSE控制台</font></a>，点击左侧导航栏治理中心 > 概览，在顶部菜单栏，选择创建ROS资源栈对应的地域, 在概览页面点击右上角查看 License Key 链接，获取MSE License Key。
Resources:
  VPC:
    Type: ALIYUN::ECS::VPC
    Properties:
      CidrBlock: 192.168.0.0/16
      VpcName:
        Fn::Sub: VPC-${ALIYUN::StackId}
  VSwitch:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId1
      VSwitchName:
        Fn::Sub: vsw_001-${ALIYUN::StackId}
      VpcId:
        Ref: VPC
      CidrBlock: 192.168.1.0/24
  VSwitch2:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId2
      VSwitchName:
        Fn::Sub: vsw_002-${ALIYUN::StackId}
      VpcId:
        Ref: VPC
      CidrBlock: 192.168.2.0/24
  VSwitch3:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId1
      VSwitchName:
        Fn::Sub: vsw_003-${ALIYUN::StackId}
      VpcId:
        Ref: VPC
      CidrBlock: 192.168.3.0/24
  VSwitch4:
    Type: ALIYUN::ECS::VSwitch
    Properties:
      ZoneId:
        Ref: ZoneId2
      VSwitchName:
        Fn::Sub: vsw_004-${ALIYUN::StackId}
      VpcId:
        Ref: VPC
      CidrBlock: 192.168.4.0/24
  SecurityGroup:
    Type: ALIYUN::ECS::SecurityGroup
    Properties:
      VpcId:
        Ref: VPC
      SecurityGroupName:
        Fn::Sub: SecurityGroup_1-${ALIYUN::StackId}
  ECSInstanceGroup1:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      ZoneId:
        Ref: ZoneId1
      VpcId:
        Ref: VPC
      VSwitchId:
        Ref: VSwitch
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: aliyun_3_9_x64_20G_alibase_
      InstanceType:
        Ref: ECSInstanceType1
      IoOptimized: optimized
      MaxAmount: 1
      Password:
        Ref: ECSPassword
      SystemDiskCategory: cloud_essd
      SystemDiskSize: 40
      InternetMaxBandwidthOut: 5
  ECSInstanceGroup2:
    Type: ALIYUN::ECS::InstanceGroup
    Properties:
      ZoneId:
        Ref: ZoneId2
      VpcId:
        Ref: VPC
      VSwitchId:
        Ref: VSwitch2
      SecurityGroupId:
        Ref: SecurityGroup
      ImageId: aliyun_3_9_x64_20G_alibase_
      InstanceType:
        Ref: ECSInstanceType2
      IoOptimized: optimized
      MaxAmount: 1
      Password:
        Ref: ECSPassword
      SystemDiskCategory: cloud_essd
      SystemDiskSize: 40
      InternetMaxBandwidthOut: 5
  PolarDBCluster:
    Type: ALIYUN::POLARDB::DBCluster
    Properties:
      ZoneId:
        Ref: ZoneId1
      VpcId:
        Ref: VPC
      VSwitchId:
        Ref: VSwitch3
      DBType: MySQL
      SecurityGroupIds:
        - Ref: SecurityGroup
      ClusterNetworkType: VPC
      DBVersion: '8.0'
      DBMinorVersion: 8.0.2
      ProxyType: EXCLUSIVE
      DBNodeClass:
        Ref: PolarDBNodeClass
      HotStandbyCluster: 'ON'
      SecurityIPList: 192.168.0.0/16
      PayType: Postpaid
  PolarDBInstance:
    Type: ALIYUN::POLARDB::DBInstance
    Properties:
      CharacterSetName: utf8
      DBClusterId:
        Ref: PolarDBCluster
      DBName: flashsale
    DependsOn:
      - PolarDBCluster
  PolarDBAccount:
    Type: ALIYUN::POLARDB::Account
    Properties:
      AccountName:
        Ref: PolarAccountName
      AccountPassword:
        Ref: DBAccountPassword
      AccountType: Normal
      DBClusterId:
        Ref: PolarDBCluster
      DBName: flashsale
    DependsOn: PolarDBInstance
  PolarDBAccountPrivilege:
    Type: ALIYUN::POLARDB::AccountPrivilege
    Properties:
      DBClusterId:
        Ref: PolarDBCluster
      AccountPrivilege: ReadWrite
      DBName: flashsale
      AccountName:
        Ref: PolarAccountName
    DependsOn:
      - PolarDBInstance
      - PolarDBAccount
  LoadBalancer:
    Type: ALIYUN::ALB::LoadBalancer
    Properties:
      LoadBalancerName:
        Fn::Sub: ALB-${ALIYUN::StackId}
      LoadBalancerEdition: Basic
      AddressIpVersion: IPv4
      AddressAllocatedMode: Fixed
      VpcId:
        Ref: VPC
      LoadBalancerBillingConfig:
        PayType: PostPay
      ZoneMappings:
        - ZoneId:
            Ref: ZoneId1
          VSwitchId:
            Ref: VSwitch
        - ZoneId:
            Ref: ZoneId2
          VSwitchId:
            Ref: VSwitch2
      AddressType: Internet
  ServerGroup:
    Type: ALIYUN::ALB::ServerGroup
    Properties:
      VpcId:
        Ref: VPC
      ServerGroupType: Instance
      Protocol: HTTP
      ServerGroupName:
        Fn::Sub: ServerGroup-${ALIYUN::StackId}
      Scheduler: Wrr
      HealthCheckConfig:
        HealthCheckEnabled: true
      StickySessionConfig:
        StickySessionEnabled: false
  BackendServerAttachment:
    Type: ALIYUN::ALB::BackendServerAttachment
    Properties:
      ServerGroupId:
        Ref: ServerGroup
      Servers:
        - ServerType: Ecs
          ServerId:
            Fn::Select:
              - 0
              - Fn::GetAtt:
                  - ECSInstanceGroup1
                  - InstanceIds
          Port: 80
          Weight: 100
        - ServerType: Ecs
          ServerId:
            Fn::Select:
              - 0
              - Fn::GetAtt:
                  - ECSInstanceGroup2
                  - InstanceIds
          Port: 80
          Weight: 100
  Listener:
    Type: ALIYUN::ALB::Listener
    Properties:
      DefaultActions:
        - Type: ForwardGroup
          ForwardGroupConfig:
            ServerGroupTuples:
              - ServerGroupId:
                  Ref: ServerGroup
      LoadBalancerId:
        Ref: LoadBalancer
      ListenerProtocol: HTTP
      ListenerPort: 80
  RedisInstance:
    Type: ALIYUN::REDIS::Instance
    Properties:
      EngineVersion: '7.0'
      ZoneId:
        Ref: ZoneId1
      VpcId:
        Ref: VPC
      VSwitchId:
        Ref: VSwitch
      InstanceClass:
        Ref: RedisClass
      InstanceName:
        Fn::Sub: flashsale_redis-${ALIYUN::StackId}
      Password:
        Ref: RedisAccountPassword
      ProductType: Tair_rdb
      DeletionForce: true
      ShardCount: 3
      TairConfig:
        Storage: 1
        ShardCount: 2
      SecondaryZoneId:
        Ref: ZoneId2
      NodeType: MASTER_SLAVE
  RedisWhitelist:
    Type: ALIYUN::REDIS::Whitelist
    Properties:
      InstanceId:
        Ref: RedisInstance
      SecurityIps: 192.168.0.0/16
  MseMicroRegistryInstance:
    Type: ALIYUN::MSE::Cluster
    Properties:
      VpcId:
        Ref: VPC
      VSwitchId:
        Ref: VSwitch
      ClusterAliasName:
        Fn::Sub: my_nacos-${ALIYUN::StackId}
      MseVersion: mse_dev
      InstanceCount: 1
      ClusterVersion: NACOS_2_0_0
      ClusterType: Nacos-Ans
      ClusterSpecification: MSE_SC_1_2_60_c
      NetType: privatenet
      PubNetworkFlow: 0
  AutoEnableService:
    Type: 'ALIYUN::ROS::AutoEnableService'
    Properties:
      ServiceName: RocketMQ
  RocketMQ:
    Type: ALIYUN::ROCKETMQ5::Instance
    Properties:
      ProductInfo:
        MsgProcessSpec: rmq.s2.2xlarge
      InternetInfo:
        InternetSpec: disable
        FlowOutType: uninvolved
      InstanceName:
        Fn::Sub: ROCKETMQ5-${ALIYUN::StackId}
      SubSeriesCode: cluster_ha
      SeriesCode: standard
      PaymentType: PayAsYouGo
      VpcInfo:
        VpcId:
          Ref: VPC
        VSwitchId:
          Ref: VSwitch
    DependsOn:
      - AutoEnableService
  Topic1:
    Type: ALIYUN::ROCKETMQ5::Topic
    Properties:
      InstanceId:
        Ref: RocketMQ
      Remark: 预扣库存成功后订单创建失败
      MessageType: NORMAL
      TopicName: order-fail-after-pre-deducted-inventory
  Topic2:
    Type: ALIYUN::ROCKETMQ5::Topic
    Properties:
      InstanceId:
        Ref: RocketMQ
      Remark: 库存系统扣减库存成功后订单创建失败
      MessageType: NORMAL
      TopicName: order-fail-after-deducted-inventory
  Topic3:
    Type: ALIYUN::ROCKETMQ5::Topic
    Properties:
      InstanceId:
        Ref: RocketMQ
      Remark: 订单创建成功
      MessageType: TRANSACTION
      TopicName: order-success
  ConsumerGroup:
    Type: ALIYUN::ROCKETMQ5::ConsumerGroup
    Properties:
      ConsumerGroupId: flashsale-service-consumer-group
      InstanceId:
        Ref: RocketMQ
      ConsumeRetryPolicy:
        RetryPolicy: DefaultRetryPolicy
        MaxRetryTimes: 5
      DeliveryOrderType: Concurrently
  WaitCondition:
    Type: ALIYUN::ROS::WaitCondition
    Properties:
      Count: 1
      Handle:
        Ref: WaitConditionHandle
      Timeout: 1200
    DependsOn:
      - ConsumerGroup
      - MseMicroRegistryInstance
      - PolarDBCluster
      - RedisInstance
      - Topic1
      - Topic2
      - Topic3
  WaitConditionHandle:
    Type: ALIYUN::ROS::WaitConditionHandle
  InstanceRunCommand:
    Type: ALIYUN::ECS::RunCommand
    Properties:
      InstanceIds:
        - Fn::Select:
            - 0
            - Fn::GetAtt:
                - ECSInstanceGroup1
                - InstanceIds
        - Fn::Select:
            - 0
            - Fn::GetAtt:
                - ECSInstanceGroup2
                - InstanceIds
      CommandContent:
        Fn::Sub: |-
          #!/bin/bash
          cat << EOF >> ~/.bash_profile
          export DB_URL="${PolarDBCluster.PrimaryConnectionString}:3306/flashsale"
          export DB_USERNAME="${PolarAccountName}"
          export DB_PASSWORD="${DBAccountPassword}"
          export REDIS_HOST="${RedisInstance.ConnectionDomain}"
          export REDIS_PASSWORD="${RedisAccountPassword}"
          export NACOS_URL="${MseMicroRegistryInstance.IntranetDomain}:8848"
          export ROCKETMQ_ENDPOINT="${RocketMQ.VpcEndpoint}"
          export ROCKETMQ_USERNAME="${RocketMQ.UserName}"
          export ROCKETMQ_PASSWORD="${RocketMQ.Password}"
          export MSE_LICENSE_KEY="${MseLicenseKey}"
          export ROS_DEPLOY=true
          EOF
          
          source ~/.bash_profile
          
          curl -fsSL https://help-static-aliyun-doc.aliyuncs.com/install-script/flashsale/install.sh | bash
          
          # 执行成功回调WaitCondition结束waitCondition的等待
          ${WaitConditionHandle.CurlCli} -d "{\"Data\" : \"Success\", \"status\" : \"SUCCESS\"}"
      Type: RunShellScript
      Timeout: '1200'
    DependsOn:
      - Topic1
      - Topic2
      - Topic3
Outputs:
  ALBPublicIPAddress:
    Description:
      zh-cn: 秒杀系统商品详情页面地址。
      en: Flash sale system product details page address.
    Value:
      Fn::Sub:
        - http://${IP}/product/1001.htm
        - IP:
            Fn::Jq:
              - First
              - .[0].LoadBalancerAddresses | .[0].Address
              - Fn::GetAtt:
                  - LoadBalancer
                  - ZoneMappings
  EcsLoginAddress:
    Description:
      en: Ecs login address, used for concurrent requests and pressure testing.
      zh-cn: ECS登录地址，用于并发请求与压力测试。
    Value:
      Fn::Sub:
        - https://ecs-workbench.aliyun.com/?from=EcsConsole&instanceType=ecs&regionId=${Region}&instanceId=${InstanceId}
        - InstanceId:
            Fn::Select:
              - 0
              - Fn::GetAtt:
                  - ECSInstanceGroup1
                  - InstanceIds
          Region:
            Ref: ALIYUN::Region
Metadata:
  'ALIYUN::ROS::Interface':
    Outputs:
      - ALBPublicIPAddress
      - EcsLoginAddress
    ParameterGroups:
      - Parameters:
          - ZoneId1
          - ECSInstanceType1
          - ZoneId2
          - ECSInstanceType2
          - ECSPassword
        Label:
          default:
            zh-cn: ECS 配置
            en: ECS Configuration
      - Parameters:
          - PolarDBNodeClass
          - PolarAccountName
          - DBAccountPassword
        Label:
          default:
            zh-cn: PolarDB 配置
            en: PolarDB Configuration
      - Parameters:
          - RedisClass
          - RedisAccountPassword
        Label:
          default:
            zh-cn: Tair 配置
            en: Tair Configuration
      - Parameters:
          - MseLicenseKey
        Label:
          default:
            zh-cn: Mse 配置
            en: Mse Configuration
    TemplateTags:
      - acs:technical-solution:high-availability-architecture:构建高性能秒杀系统-tech_solu_181