Skip to content

业务相关参数-idempotentId

idempotentId 幂等Id生成器,每个重试请求中,其中默认的实现方法为SimpleIdempotentIdGenerate,我们一起来看一下这个方法是如何实现的。

java
public class SimpleIdempotentIdGenerate implements IdempotentIdGenerate {
    @Override
    public String idGenerate(IdempotentIdGenerateEntity idempotentIdGenerateEntity) throws Exception {
        return SecureUtil.md5(idempotentIdGenerateEntity.toString());
    }
}
   /**
     * 参数列表为IdempotentIdGenerateEntity包含了四个对象, 下面说明每一个下标代表的数据含义
     * 场景名称: scene(String)
     * 执行器名称: targetClassName(String)
     * 参数列表: args(Object[])
     * 执行的方法名称: methodName(String)
     * scene, targetClassName, args, executorMethod.getName()
     *
     * @param t 参数列表
     * @return idempotentId
     * @throws Exception
     */
public interface IdempotentIdGenerate {
    String idGenerate(IdempotentIdGenerateEntity idempotentIdGenerateEntity) throws Exception;
}

可以看到,默认实现中我们取scene场景名称,targetClassName执行器名称,args参数列表,methodName执行的方法名称来进行了一个md5运算作为重试任务的幂等Id,由此来保证任务是唯一的,这种实现方式下,对象中的四个参数中有一个变化都会新建一个重试任务。 当然,在一些特殊的业务场景下我们可以重写IdempotentIdGenerate来实现幂等Id的自定义。 接下来我们再来看一个案例。这次我们来模拟一个业务场景,举个例子,一个商品下单的场景,用户购买某个限购的商品,此时用户可以通过手机下单,也可以通过PC下单,此时两个请求的订单号相同,一端下单成功后,另一端不可重复下单。此时假设下单服务发生异常,我们仅需产生一次重试任务即可,无需根据客户端不同发生多次请求。

那么怎么来实现这个场景呢?我们首先来定义一个商品的VO,为了简化表述,我们在其中仅仅放入订单ID和订单来源信息两个字段。

java
@Data
public class OrderVo {
    private String orderId; // 订单ID,用于唯一标识订单的编号
    private Integer source; // 订单来源信息,1-手机端下单 2-PC端下单
}

然后我们自定义一个幂等Id生成类

java
/**
 * 使用自定义的幂等Id生成类 OrderIdempotentIdGenerate
 */
@Retryable(scene = "remoteRetryWithIdempotentId",retryStrategy = RetryType.ONLY_REMOTE,
            idempotentId = OrderIdempotentIdGenerate.class)
public boolean remoteRetryWithIdempotentId(OrderVo orderVo){
    double i = 1 / 0;
    return true;
}

在其中我们指定了幂等Id的生成规则为自定义的生成规则,然后我们在代码中继承IdempotentIdGenerate类,给出一个OrderIdempotentIdGenerate类,我们从IdempotentIdContext取出args参数,这个参数代表了我们从接口传入的参数,在这个接口中,我们取出args参数数组中下标为0的参数,转换为OrderVo类型,然后指定幂等Id的生成规则是取出orderId求md5。

java
public class OrderIdempotentIdGenerate implements IdempotentIdGenerate {

    @Override
    public String idGenerate(IdempotentIdContext idempotentIdContext) throws Exception {
        Object[] args = idempotentIdContext.getArgs();
        OrderVo orderVo = (OrderVo) args[0];
        return SecureUtil.md5(orderVo.getOrderId());
    }

}

接下来我们测试看看效果吧。首先我们给出三个Post请求,其中请求1是原始请求,请求2中orderId和请求1一致仅仅修改source,请求3中source保持不变,仅仅修改orderId。

依次执行三个请求我们可以观察到,请求1和请求2执行之后仅有一条数据生成,幂等Id没有变化,也没有生成新的重试任务;而执行请求3之后生成了一条新的重试任务。