
# 使用函数工作流自动检查并重启失败的迁移任务
#### 方案概述
通过API调用启动迁移任务/迁移任务组后，由于网络波动或其他原因导致迁移失败，可以通过以下方式自动重启迁移任务或迁移任务组：
- 手动调用API重新启动迁移任务或迁移任务组。
- 在业务代码中自动检测并调用API重新启动迁移任务或迁移任务组。
- 使用函数工作流自动检查并重启失败的迁移任务或迁移任务组。
本方案指导用户使用函数工作流检查指定任务id的迁移任务，若该任务迁移失败则自动重启。
![](https://support.huaweicloud.com/bestpractice-oms/public_sys-resources/note_3.0-zh-cn.png)
该方案只提供参考，需要根据实际情况做出调整，函数工作流的使用方式请参见[函数工作流 FunctionGraph](https://support.huaweicloud.com/usermanual-functiongraph/functiongraph_01_0110.html)。
#### 逻辑架构
使用函数工作流自动检查并重启失败的迁移任务的逻辑架构图，如[图1]所示。
图1逻辑架构图   
![](https://support.huaweicloud.com/bestpractice-oms/zh-cn_image_0000002670600567.png "点击放大")
启动OMS迁移任务之后，触发函数工作流循环检查该迁移任务的执行状态，当迁移任务状态为失败时：
- 如果该迁移任务为单个迁移任务，函数工作流调用OMS的API重启该迁移任务。
- 如果该迁移任务为迁移任务组中的任务，函数工作流调用OMS的API检查迁移任务组的状态，直至迁移任务组的状态为失败，重新启动迁移任务。
当迁移成功或重试次数超过指定次数，则停止检查。
#### 前提条件
已[创建迁移任务](https://support.huaweicloud.com/usermanual-oms/topic_0000001089828284.html)，或已[创建迁移任务组](https://support.huaweicloud.com/usermanual-oms/oms_01_0017.html)并启动相应任务。
#### 操作流程
配置函数工作流的完成操作流程，请参见[表1]。
 表1配置函数工作流操作流程 
| 步骤                                                | 说明                                                                         |
|:---|:---|
| [步骤一：创建程序包]   | 使用Java 8语言编写定时检查并重启失败迁移任务的函数代码。                                            |
| [步骤二：创建函数]      | 在函数工作流控制台中创建Java 8运行时的函数，上传步骤一编译生成的JAR包作为函数代码，并配置内存、执行超时时间及函数执行入口等基本参数。    |
| [步骤三：设置环境变量] | 为函数配置运行所需的环境变量，包括目的端区域ID、迁移任务ID、最大重试次数，以及源端和目的端的AK/SK等认证信息，供函数运行时动态读取。     |
| [步骤四：配置事件源]   | 为函数添加定时触发器作为事件源，通过配置固定频率或Cron表达式设定检查周期，使函数按指定时间间隔自动执行，定期检查迁移任务状态并在失败时触发重启。 |
   
 #### 步骤一：创建程序包
本示例使用Java 8语言实现定时检查并重启失败迁移任务的功能，有关函数开发的过程请参考[Java函数开发](https://support.huaweicloud.com/devg-functiongraph/functiongraph_devg_02_0003.html)。
图2目录结构   
![](https://support.huaweicloud.com/bestpractice-oms/zh-cn_image_0000001829392898.png "点击放大")
pom.xml文件的依赖：
```
<dependency>
  <groupId>Runtime</groupId>
  <artifactId>Runtime</artifactId>
  <version>2.0.5</version>
</dependency>
<dependency>
  <groupId>com.huaweicloud.sdk</groupId>
  <artifactId>huaweicloud-sdk-oms</artifactId>
  <version>3.1.86</version>
</dependency>
```
样例代码：
```
package com.huawei.demo;
import com.huawei.services.runtime.Context;
import com.huawei.services.runtime.RuntimeLogger;
import com.huawei.services.runtime.entity.timer.TimerTriggerEvent;
import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.core.auth.ICredential;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
import com.huaweicloud.sdk.core.exception.ServiceResponseException;
import com.huaweicloud.sdk.oms.v2.OmsClient;
import com.huaweicloud.sdk.oms.v2.model.RetryTaskGroupReq;
import com.huaweicloud.sdk.oms.v2.model.RetryTaskGroupRequest;
import com.huaweicloud.sdk.oms.v2.model.ShowTaskGroupRequest;
import com.huaweicloud.sdk.oms.v2.model.ShowTaskGroupResponse;
import com.huaweicloud.sdk.oms.v2.model.ShowTaskRequest;
import com.huaweicloud.sdk.oms.v2.model.ShowTaskResponse;
import com.huaweicloud.sdk.oms.v2.model.StartTaskReq;
import com.huaweicloud.sdk.oms.v2.model.StartTaskRequest;
import com.huaweicloud.sdk.oms.v2.region.OmsRegion;
public class RetryTask {
    private RuntimeLogger logger;
    private String dstAk;
    private String dstSk;
    private String srcAk;
    private String srcSk;
    public String checkAndRetryTask(TimerTriggerEvent event, Context context) {
        logger = context.getLogger();
        String region = context.getUserData("region_id");
        String taskId = context.getUserData("task_id");
        int maxRetryCount = Integer.parseInt(context.getUserData("retry_count"));
        srcAk = context.getUserData("src_ak");
        srcSk = context.getUserData("src_sk");
        dstAk = context.getUserData("dst_ak");
        dstSk = context.getUserData("dst_sk");
        String ak = context.getAccessKey();
        String sk = context.getSecretKey();
        ICredential auth = new BasicCredentials()
                .withAk(ak)
                .withSk(sk);
        OmsClient client = OmsClient.newBuilder()
                .withCredential(auth)
                .withRegion(OmsRegion.valueOf(region))
                .build();
        ShowTaskRequest request = new ShowTaskRequest();
        request.withTaskId(taskId);
        int retryCount = 0;
        try {
            while (true) {
                ShowTaskResponse taskResponse = client.showTask(request);
                Integer status = taskResponse.getStatus();
                logger.info("task status: "+status);
                if (retryCount >= maxRetryCount || status == 5) {
                    break;
                }
                if ("NORMAL_TASK".equals(taskResponse.getGroupType().getValue()) && status == 4) {
                    retryTask(client, taskId);
                    logger.info("task restart:" + taskId);
                    retryCount++;
                    continue;
                }
                if ("GROUP_TASK".equals(taskResponse.getGroupType().getValue()) && status == 4) {
                    retryTasksGroup(client, taskResponse.getGroupId());
                    logger.info("task group restart:" + taskResponse.getGroupId());
                    retryCount++;
                }
            }
            return "ok";
        } catch (ConnectionException e) {
            logger.error("Failed to restart the task, error_msg: " + e.getMessage());
        } catch (RequestTimeoutException e) {
            logger.error("Time out, failed to restart the task, error_msg: " + e.getMessage());
        } catch (ServiceResponseException e) {
            logger.error("Failed to restart the task, error_code: " + e.getErrorCode() + ", error_msg: " + e.getErrorMsg());
        }
        return "fail";
    }
    private void retryTasksGroup(OmsClient client, String groupId) {
        ShowTaskGroupRequest taskGroupRequest = new ShowTaskGroupRequest();
        taskGroupRequest.withGroupId(groupId);
        ShowTaskGroupResponse taskGroupResponse = client.showTaskGroup(taskGroupRequest);
        if (taskGroupResponse.getStatus() == 5) {
            RetryTaskGroupRequest retryTaskGroupRequest = new RetryTaskGroupRequest();
            retryTaskGroupRequest.withGroupId(groupId);
            RetryTaskGroupReq body = new RetryTaskGroupReq();
            body.withMigrateFailedObject(false);
            body.withDstAk(dstAk);
            body.withDstSk(dstSk);
            body.withSrcAk(srcAk);
            body.withSrcSk(srcSk);
            retryTaskGroupRequest.withBody(body);
            client.retryTaskGroup(retryTaskGroupRequest);
        }
    }
    private void retryTask(OmsClient client, String taskId) {
        StartTaskRequest startTaskRequest = new StartTaskRequest();
        StartTaskReq body = new StartTaskReq();
        body.withMigrateFailedObject(false);
        body.withDstAk(dstAk);
        body.withDstSk(dstSk);
        body.withSrcAk(srcAk);
        body.withSrcSk(srcSk);
        startTaskRequest.withBody(body);
        startTaskRequest.withTaskId(taskId);
        client.startTask(startTaskRequest);
    }
}
```
 #### 步骤二：创建函数
1. 登录[函数工作流控制台](https://console.huaweicloud.com/functiongraph/?locale=zh-cn)。
2. 在左侧导航栏选择"函数 \> 函数列表"，进入函数列表界面。
3. 单击"创建函数"，进入创建函数流程。
4. 填写函数配置信息。输入基础配置信息，完成后单击"创建函数"。 
   - 函数名称：输入"check_retry_task"。
   
   - 委托名称：选择"未使用任何委托"。
   
   - 运行时语言：选择"Java 8"。
   
   
   
   
5. 单击"check_retry_task"函数名称，进入函数详情页。
6. 在"代码"页签，选择"上传代码 \> JAR文件"，上传样例代码编译后的jar包，完成后单击"确定"。
7. 在"设置"页签的"常规设置"页面，设置如下信息，完成后单击"保存"。 
   - 内存：选择"512"。
   
   - 执行超时时间：输入"60"。
   
   - 函数执行入口：输入"com.huawei.demo.RetryTask.checkAndRetryTask"。
     ![](https://support.huaweicloud.com/bestpractice-oms/public_sys-resources/note_3.0-zh-cn.png)
     函数执行入口格式为：\[包名\].\[类名\].\[执行函数名\]，根据自己上传的代码修改。
     
   
   - 所属应用：默认"default"。
   
   - 描述：输入自定义描述内容。
   
   
   
   
 
 #### 步骤三：设置环境变量
1. 进入"check_retry_task"函数详情页，在"设置"页签，选择"环境变量"，单击"编辑环境变量"。
2. 在"编辑环境变量"弹窗中单击"添加环境变量"，根据[表2]环境变量参数说明，添加和配置环境变量。完成后单击"保存"。
   
    表2环境变量参数说明 
   | 环境变量            | **说明**                                  |
   |:---|:---|
   | region_id       | 目的端桶所在区域，例如华北-北京4的region_id为cn-north-4。 |
   | task_id         | 指定迁移任务id。                               |
   | max_retry_count | 重试次数。                                   |
   | dst_ak          | 目标端账号的ak（加密参数）。                         |
   | dst_sk          | 目标端账号的sk（加密参数）。                         |
   | src_ak          | 源端账号的ak（加密参数）。                          |
   | src_sk          | 源端账号的sk（加密参数）。                          |
      
   
   
3. 配置完成后，单击"确定"保存环境变量。
 
 #### 步骤四：配置事件源
函数创建完成后，为函数添加事件源，可以通过添加定时触发器定时检查指定任务的迁移状态并重启。
1. 进入"check_retry_task"函数详情页，在"设置"页签，选择"触发器"。
2. 单击"创建触发器"，弹出"创建触发器"界面。
3. 设置以下信息： 
   - **触发器类型**：选择"定时触发器 (TIMER)"。
   
   - **定时器名称**：您自定义的定时器名称，例如：Timer。
   
   - **触发规则** ：固定频率和Cron表达式。
     - **固定频率**：固定时间间隔触发函数，该类型下支持配置单位为分、时、天，每种类型仅支持整数配置，其中分钟支持范围(0，60\]，小时支持范围(0，24\]，天支持范围(0，30\]。
     
     - **Cron表达式** ：设置更为复杂的函数执行计划，例如：周一到周五上午08:30:00执行函数等，具体请参见[函数定时触发器Cron表达式规则](https://support.huaweicloud.com/usermanual-functiongraph/functiongraph_01_0207.html#section3)。
      
   
   - **是否开启**：可选择是否开启定时触发器。
   
   - **附加信息** ：如果用户配置了触发事件，会将该事件填写到TIMER事件源的"user_event"字段，详情请参见[支持的事件源](https://support.huaweicloud.com/devg-functiongraph/functiongraph_02_0102.html)。
   
   
   ![](https://support.huaweicloud.com/bestpractice-oms/zh-cn_image_0000001876232129.png "点击放大")
   
   
4. 配置完成后，单击"确定"，完成触发器创建。
 
