本文题目来自于知识星球,后台回复 “知识星球” 可参与问答。

前两日得到一个朋友的交流,他们有一个产生唯一订单号的功能,把代码单独提出来了,问这个方法有什么问题吗?改怎么测试?
先把代码放出来,如下:

/**
   * 生产唯一的交易订单号
   *
   * @return
   */
  public static String createUniqueOrderNo() {
      SimpleDateFormat nyrsfm = new SimpleDateFormat("yyyyMMddHHmmss");
      return nyrsfm.format(new Date()) + getRandomLengthCode(4);
  }


  /**
   * 获取随机的短信验证码
   *
   * @return
   */
  public static String getRandomLengthCode(int length) {
      return String.valueOf((int) ((Math.random() * 9 + 1) * Math.pow(10, length - 1)));
  }

第一个是生产订单号的,第二个是产生一个四位随机数的方法。先说第一个方法的思路:订单号分两部分,一是时间(按照这种yyyyMMddHHmmss格式的),第二部分就是四位随机数。第二个方法:产生一个 [0,1) 之间的 double 类型的数字,然后通过一个算式(*9+1)得到一个 [1,10) 之间的数字,然后在乘以 10 的某次幂(这里是 3),得到一个 [1000,10000) 的数字,然后强转成 int 类型,得到 [1000,10000) 范围的四位整数。

解释完毕,下面分享一下我对这个方法的认识。一开始我俩讨论的中心问题是一个:会不会产生重复的订单号。答案显而易见,解释起来如下:时间精确到秒一级,然后随机数范围粗略计算就是 9000 个,如果在一秒钟以内生成了 9000+ 个订单,那么肯定会有重复的。

那么如何测试呢?或者说改怎么提这个 BUG 呢?

我提出了两个方案:一是口头或者文字解释,如上内容;二是通过测试产生重复订单号。

方案一:

看人,看事儿,事实证明,这个方法不太管用。
主要原因倒不是开发不承认,而是不觉得有必要改,原因有两点,一是业务量没有那么多,二是发生概率太小了。

方案二:

通过压测产生重复订单号,管用很麻烦,试了 N 多次也没产生重复订单号。
主要原因是测试环境性能太差,大概就是几十 QPS(整个订单接口),过长时间压测会导致服务不稳定,脏数据太多。

下面是知识星球的合伙人给的方案:

年月日时分秒加➕短信验证码构成唯一的订单号。隐患:如果在特定环境,多台服务器一起跑,可能出现相同的订单编号。建议设定用户的唯一值,例如名称。建议使用 UUID。代码如下:

/**
   * 生产唯一的交易订单号
   *
   * @return
   */
  public static String createUniqueOrderNo() {
      return UUID.random.toString;
  }

更多的办法还是把这个方法拿出来单独进行测试,不需要使用多线程。一个 for 循环遍历记录很快的,分享一下我的测试代码:

public static void main(String[] args) {
       List<String> list = new ArrayList<>();
       range(250).forEach(x->
       {
           String randomLengthCode = getRandomLengthCode(4);
           if (list.contains(randomLengthCode))
               output(x);
           else
               list.add(randomLengthCode);
       });
   }

抛砖引玉,如果有更好的办法,欢迎留言或者去知识星球一起交流。

其次我还关注到了性能问题,这个有机会再细聊。

解决方案比较多,很多框架都是支持的,一般也都会跟用户的身份 ID 相关。说一个比较简单的:订单号多加一条唯一标签如用户 ID,然后接口限制用户产生订单频率,(比如 5s 钟只能下一单)。


技术类文章精选

非技术文章精选


↙↙↙阅读原文可查看相关链接,并与作者交流