订单号生成逻辑,C#和JAVA双版
五年没写过博客了,倒是天天在看
转来转去,又转回技术
原来一直在使用微软爸爸的东西,最近一两年开始玩android,玩java,还有PostgreSQL
都有些应用了,倒是可以整理些随笔出来,这就是其中一篇吧
c#是java的优雅版本,java的linq就是一坨那啥,嗯!觉得不爽就别看了
订单号这个玩意我想有几点得保证
1,有顺序但不连续
2,不能太长,最好15位以内
3,最好全数字
目前来说谷歌和百度出来的感觉都达不到我要的这些要求,不是太长超过20位,就是会加上字母,再就是会连续,否则就没顺序,没有办法只有自己想
本来没想自己干的,这玩意绝对是个老问题,估计大家都有好办法,藏着了吧
开干,思路如下:
得有时间在里面,这样有顺序
不连续好办,加上随机尾数
不能太长也好办,取一部分时间戳,或者是当前时间减去过去一个固定时间的差值
OK,上代码,先JAVA
1 import java.io.*;2 import java.util.UUID;
3 import java.util.concurrent.atomic.AtomicLong;
4
5 /**
6 * Created by 黑曜石 on 2017/5/19.
7 */
8 public class TradeNoGenerator {
9 //基准时间截 (2017-01-01)
10 private static final long initStamp=1483200000000L;
11 //每秒基准容量1000个
12 private static final int secondsSpace=1000;
13 //基准容量扩容倍数
14 private static final int secondsSpaceMulti=10;
15 //多少秒不用,重新设定原子初始值
16 private static final int secondsRefresh=60;
17 //原子初始值
18 private AtomicLong atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
19
20 private TradeNoGenerator() {
21 }
22
23 private static class TradeNoGeneratorHolder{
24 private static TradeNoGenerator instance = new TradeNoGenerator();
25 }
26
27 public static TradeNoGenerator getInstance() {
28 return TradeNoGeneratorHolder.instance;
29 }
30
31 //线程安全
32 /**
33 * 用这个,用这个
34 * @return
35 * 生成结果例:11983017246298
36 * 由三段组成,11983017,2462,98
37 * 第一段11983017,位数会随着时间增长增加,计算方式为与initStamp相加,结果为到秒的时间戳
38 * 第二段,2462,看容量决定位数,容量=secondsSpace*secondsSpaceMulti,原子增长字段
39 * 第三段98,固定2位,随机数
40 */
41 public synchronized String getNo(){
42 long no=atomicLong.getAndIncrement();
43 //当前秒时间戳与基准时间戳秒级数量
44 long currentSecond = (System.currentTimeMillis()-initStamp) / 1000;
45 //计数超过60秒未使用过,则重新给定初始值
46 if (currentSecond-no/(secondsSpace*secondsSpaceMulti)>=secondsRefresh){
47 //重置原子计数原始值
48 atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
49 no=atomicLong.getAndIncrement();
50 }
51 //计算容量是否超出
52 if (no-currentSecond*secondsSpace*secondsSpaceMulti>=secondsSpace*secondsSpaceMulti){
53 //超出则等待下一秒的到来,这里计算到下一整数秒的微秒差
54 long millisWithinSecond = System.currentTimeMillis() % 1000;
55 try {
56 Thread.sleep(1000 - millisWithinSecond);
57 } catch (InterruptedException e) {
58 e.printStackTrace();
59 }
60
61 System.out.println(">>> new second . "+no +" "+Thread.currentThread().getName());
62
63 //重置原子计数原始值
64 atomicLong=new AtomicLong((System.currentTimeMillis()-initStamp)*secondsSpaceMulti);
65 no=atomicLong.getAndIncrement();
66 }
67
68 int hashCode= UUID.randomUUID().hashCode();
69 hashCode=hashCode<0?-hashCode:hashCode;
70 String randomStr=String.valueOf(hashCode).substring(0,2);
71 return no+randomStr;
72 }
73
74 /**
75 * 获取时间戳
76 * @param tradeNo
77 * @return
78 */
79 public long getTimestamp(String tradeNo){
80 int spaceLen = String.valueOf(secondsSpace*secondsSpaceMulti).length()-1;
81 int stampLen=tradeNo.length()-spaceLen-2;
82 String stampStr=tradeNo.substring(0,stampLen);
83 //右侧补齐微秒
84 long stamp=Long.parseLong(stampStr+String.format("%1$03d",0));
85 stamp+=initStamp;
86 return stamp;
87 }
88
89 public static void main(String[] args) {
90
91 File file=new File("e:/tradeno1.txt");
92 if (file.exists()){
93 file.delete();
94 }
95 try {
96 file.createNewFile();
97 } catch (IOException e) {
98 e.printStackTrace();
99 }
100 file=new File("e:/tradeno2.txt");
101 if (file.exists()){
102 file.delete();
103 }
104 try {
105 file.createNewFile();
106 } catch (IOException e) {
107 e.printStackTrace();
108 }
109
110 new Thread(new Runnable() {
111 @Override
112 public void run() {
113
114 try {
115 Writer w=new FileWriter("e:/tradeno1.txt");
116 BufferedWriter buffWriter=new BufferedWriter(w);
117
118 TimeMark timeMark=new TimeMark();
119 for (int i = 0; i < 12000; i++) {
120 String x=getInstance().getNo();
121 //System.out.println(x);
122 buffWriter.write(x+"\t\r");
123 }
124 System.out.println("]]]");
125 timeMark.simplePrint();
126
127 buffWriter.close();
128 w.close();
129 System.out.println("写入成功!");
130
131 } catch (FileNotFoundException e) {
132 System.out.println("要读取的文件不存在:"+e.getMessage());
133 } catch (IOException e) {
134 System.out.println("文件读取错误:"+e.getMessage());
135 }
136 }
137 }).start();
138
139 new Thread(new Runnable() {
140 @Override
141 public void run() {
142 try {
143 Writer w=new FileWriter("e:/tradeno2.txt");
144 BufferedWriter buffWriter=new BufferedWriter(w);
145
146 TimeMark timeMark=new TimeMark();
147 for (int i = 0; i < 24000; i++) {
148 String x=getInstance().getNo();
149 //System.out.println(x);
150 buffWriter.write(x+"\t\r");
151 }
152 System.out.println("]]]");
153 timeMark.simplePrint();
154
155 buffWriter.close();
156 w.close();
157 System.out.println("写入成功!");
158
159 } catch (FileNotFoundException e) {
160 System.out.println("要读取的文件不存在:"+e.getMessage());
161 } catch (IOException e) {
162 System.out.println("文件读取错误:"+e.getMessage());
163 }
164 }
165 }).start();
166 }
167 }
接下来是C#
public class TradeNoGenerator{
//基准时间截 (2017-01-01)
private static DateTime initDateTime = new DateTime(2017, 1, 1);
//每秒基准容量1000个
private static int secondsSpace = 1000;
//基准容量扩容倍数
private static int secondsSpaceMulti = 10;
//多少秒不用,重新设定原子初始值
private static int secondsRefresh = 60;
//原子初始值
private static long initStamp = (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
static TradeNoGenerator __TradeNoGenerator;
private TradeNoGenerator()
{
}
public static TradeNoGenerator GetInstance()
{
if (__TradeNoGenerator == null)
{
__TradeNoGenerator = new TradeNoGenerator();
}
return __TradeNoGenerator;
}
//线程安全
/**
* 用这个,用这个
* @return
* 生成结果例:11983017246298
* 由三段组成,11983017,2462,98
* 第一段11983017,位数会随着时间增长增加,计算方式为与initStamp相加,结果为到秒的时间戳
* 第二段,2462,看容量决定位数,容量=secondsSpace*secondsSpaceMulti,原子增长字段
* 第三段98,固定2位,随机数
*/
public String GetNo()
{
long no = Interlocked.Increment(ref initStamp);
//当前秒时间戳与基准时间戳秒级数量
double currentSecond = DateTime.UtcNow.Subtract(initDateTime).TotalSeconds;
//计数超过60秒未使用过,则重新给定初始值
if (currentSecond - no / (secondsSpace * secondsSpaceMulti) >= secondsRefresh)
{
//重置原子计数原始值
initStamp= (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
no = Interlocked.Increment(ref initStamp);
}
//计算容量是否超出
if (no - currentSecond * secondsSpace * secondsSpaceMulti >= secondsSpace * secondsSpaceMulti)
{
//超出则等待下一秒的到来,这里计算到下一整数秒的微秒差
int millisWithinSecond = DateTime.UtcNow.Millisecond % 1000;
try
{
Thread.Sleep(1000 - millisWithinSecond);
}
catch (Exception ex)
{
}
//重置原子计数原始值
initStamp = (long)DateTime.UtcNow.Subtract(initDateTime).TotalSeconds * secondsSpace * secondsSpaceMulti;
no = Interlocked.Increment(ref initStamp);
}
int hashCode = Guid.NewGuid().GetHashCode();
hashCode = hashCode < 0 ? -hashCode : hashCode;
String randomStr = hashCode.ToString().Substring(0, 2);
return no + randomStr;
}
}
注释写得很清楚啦
1秒基准容量是1000个,可以以10的倍数来扩大(只能是10的倍数),一般扩大10倍,1秒容量到10000个,可以解决很多问题啦
速度你们可以自己测试,一秒内的速度是毫秒级别的
超出秒容量的话,那就得等待下一秒的到来
还有个问题就是当前不支持分布式
无论是JAVA还是C#基本是利用原子自增方式解决并发和效率问题
以上是 订单号生成逻辑,C#和JAVA双版 的全部内容, 来源链接: utcz.com/z/391127.html