本文题目来自于知识星球,后台回复 “知识星球” 可参与问答。
前两日得到一个朋友的交流,他们有一个产生唯一订单号的功能,把代码单独提出来了,问这个方法有什么问题吗?改怎么测试?
先把代码放出来,如下:
/**
* 生产唯一的交易订单号
*
* @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 钟只能下一单)。