MSHA控制台提供在切流前或者切流结束后添加自定义动作的功能,支持切流前或者切流结束后进行业务自定义接口的回调,即支持添加自定义回调接口的前置或者后置动作。本文介绍如何添加前置或者后置的自定义动作。

使用限制

一个多活命名空间下,最多允许配置5个自定义动作。

操作步骤

  1. 登录AHAS控制台
  2. 在控制台左侧导航栏中选择多活容灾
  3. 在左侧导航栏选择切流 > 自定义动作,并在顶部选择目标命名空间。
  4. 自定义动作页面,单击添加,配置以下参数。
    参数 描述
    回调URL 回调接口的URL:
    • 管控与此URL根IP具备网络连通性,建议测试网络连通性后再进行配置。
    • URL需要包含协议头,例如:http://xx.domain.com/yy。
    前置/后置 回调接口的自定义切流动作,包括:
    • 前置动作:阻塞回调接口的动作。当满足一定的阻塞条件时,允许回调该接口后再进行切流动作,需要配置阻塞条件。
    • 后置动作:触发回调接口的动作。当满足一定的触发条件时,允许等待切流动作结束后再回调该接口,需要配置触发条件。

    一个多活命名空间下,既可以配置前置动作,也可以配置后置动作。

    阻塞配置/触发条件

    阻塞配置:若选择的是前置自定义切流动作,需要选择阻塞配置。

    • 等待成功:http接口返回2xx后才继续执行切流。
    • 等待结束:http接口只要返回结果就继续执行切流,不管结果成败。
    • 等待失败:http接口返回2xx以外结果后才继续执行切流。
    • 忽略结果:异步调用http接口后立即继续执行切流,忽略结果。

    触发条件:若选择的是后置自定义切流步骤,需要选择触发条件。

    • 切流成功:切流成功才调用此接口。
    • 切流结束:切流结束就调用此接口(无论成功还是异常)。
    • 切流失败:切流失败才调用此接口,仅切流异常结束后触发。
    • 忽略结果:异步调用http接口后立即继续执行切流,忽略切流结果。
    HTTP请求方法 回调采用的HTTP/HTTPS请求方法。枚举值为:GET、POST。
    安全摘要盐值 盐,用于对回调请求生成安全摘要。
    生效 是否执行回调接口的自定义切流动作。包括:
    • 是:执行当前的自定义动作。
    • 否:暂不执行当前的自定义动作。
    应用架构 当前命名空间中包含的应用架构。包括:
    • 异地双活
    • 异地双读
    • 异地应用双活
  5. 单击保存,在弹出的对话框中单击确认
    其他操作:
    • 删除:单击操作列的删除,可以删除该自定义动作。
    • 修改:单击操作列的修改,可以修改该自定义动作。

规范

使用自定义动作功能时,回调接口需要满足以下规范。

  • 回调接口需是HTTP/HTTPS协议。
  • 回调接口需要免登,即不做登录态校验。
  • 回调接口不能有CSRF Token校验。
  • 回调接口参数名需要遵循以下名称规范。MSHA控制台发起回调时,用户需要勾选以下名称的参数,包含了切流工单基本信息和安全校验摘要。
    参数 参数描述
    mshaTenantId 多活命名空间ID。
    id 切流工单ID。
    name 切流工单名称。
    sourceUnitFlag 切流源单元标识,表示从sourceUnitFlag->向targetUnitFlag切流。
    targetUnitFlag 切流目的单元,表示从sourceUnitFlag->向targetUnitFlag切流。
    status

    切流结束状态。

    • 正常结束状态枚举值为:
      • complete:正常结束。
      • canceled:人为取消终止。
    • 异常结束状态枚举值为:
      • autoCanceled:异常后自动取消终止(回滚完成)。
      • cancelFailed:取消失败。
      • closed:强制关闭(不继续执行也不回滚)。
      • fail:其他情况异常终止。
    completeTime 切流结束时间。
    changeTokenRange

    当范围类型切流时,切流变更的范围区间。当非范围类型切流时,值为空字符串("")。

    格式:[$区间开始,$区间结束],其中 [] 括号表示数学上的闭区间(固定为闭区间,不会有开区间或半开区间的情况)。

    Java语言赋值样例:String changeTokenRange="[1,9999]";

    changeTokenList

    当精准类型切流时,切流变更的精准名单。当非精准类型切流时,值为空字符串("")。

    格式:以英文半角逗号隔开的精准名单。

    Java语言赋值样例:String changeTokenList="11,22,33";

    digest 安全摘要,用于验证调用是否可信以及参数防篡改。

回调接口样例

以下是Java语言版本,使用Spring MVC框架的编写的回调接口样例:

@RestController
@RequestMapping("/demo")
@Slf4j
public class DemoController {

    @RequestMapping(path = "/switchEndCallback", method = {RequestMethod.POST, RequestMethod.GET})
    public void switchEndCallback(  @RequestParam String mshaTenantId,
                                    @RequestParam String id,
                                    @RequestParam String name,
                                    @RequestParam String sourceUnitFlag,
                                    @RequestParam String targetUnitFlag,
                                    @RequestParam String status,
                                    @RequestParam String completeTime,
                                    @RequestParam String changeTokenRange,
                                    @RequestParam String changeTokenList,
                                    @RequestParam String digest) throws Exception {

        //do something
    }

}     

校验调用来源是否可信

MSHA控制台接口回调时,会带上digest安全摘要参数,用于验证调用来源是否可信以及参数防篡改。校验流程如下:

  1. 自定义一个digestSalt值,并将digestSalt配置到MSHA控制台切流结束回调接口信息中。
  2. 切流结束后,接口调用者(MSHA控制台)将发起接口回调,使用digestSalt和回调参数生成摘要digest,并在回调时带上该digest参数。
  3. 接口提供者(业务应用/业务系统)接收到回调接口时,使用跟步骤2一样的方式生成期望的摘要expectedDigest,跟回调参数中的摘要digest做比较。若完全相同表示验证通过,调用来源是否可信且参数没有被篡改。
    说明 仅当接口调用者和接口提供者都使用相同盐值和参数生成的摘要才会验证通过。

使用示例如下:

@RestController
@RequestMapping("/demo")
@Slf4j
public class DemoController {


    @RequestMapping(path = "/switchEndCallback", method = {RequestMethod.POST, RequestMethod.GET})
    public Object switchEndCallback(@RequestParam String mshaTenantId,
                                    @RequestParam String id,
                                    @RequestParam String name,
                                    @RequestParam String sourceUnitFlag,
                                    @RequestParam String targetUnitFlag,
                                    @RequestParam String status,
                                    @RequestParam String completeTime,
                                    @RequestParam String changeTokenRange,
                                    @RequestParam String changeTokenList,
                                    @RequestParam String digest) throws Exception {

        //使用TreeMap保证后续拼接的StringBuilder是固定顺序的。
        TreeMap<String, String> sortedParamMap = new TreeMap<>();
        sortedParamMap.put("mshaTenantId", mshaTenantId);
        sortedParamMap.put("id", id);
        sortedParamMap.put("name", name);
        sortedParamMap.put("sourceUnitFlag", sourceUnitFlag);
        sortedParamMap.put("targetUnitFlag", targetUnitFlag);
        sortedParamMap.put("status", status);
        sortedParamMap.put("completeTime", completeTime);
        sortedParamMap.put("changeTokenRange", changeTokenRange);
        sortedParamMap.put("changeTokenList", changeTokenList);

        log.info("switchEndCallback params:" + sortedParamMap);

        //盐值,在请求参数生成安全摘要时使用,用于验证调用来源是否可信。
        String digestSalt = "kbBO1nD1BM_Ymr76XOoZkbJ72k4";

        StringBuilder paramsStringBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : sortedParamMap.entrySet()) {
            paramsStringBuilder.append(entry.getValue());
        }
        paramsStringBuilder.append(digestSalt);

        //推荐使用commons-codec:commons-codec:jar:1.10(或以上版本)DigestUtils来生成md5哈希摘要。
        String expectedDigest = org.apache.commons.codec.digest.DigestUtils.md5Hex(paramsStringBuilder.toString().getBytes("utf-8"));
        //只有接口调用者和接口提供者,都使用相同盐值、相同的参数生成的摘要才会验证通过。
        boolean digestCheckPassed = digest.equalsIgnoreCase(expectedDigest);
        log.info("switchEndCallback digestCheckPassed:{}", digestCheckPassed);
        return "switchEndCallback returned success";
    }

}