U8SDK——集群环境下U8Server唯一订单号生成规则
U8Server支持分布式和集群部署之后, 我们还有一个问题需要解决, 那就是集群环境下,唯一订单号生成问题。 之前U8Server的订单号生成规则是 32位时间戳+32位序号,最终生成一个64位的long类型的订单号。
之前也考虑过,使用UUID等方式, 但是综合考虑下来, 我们决定还是让订单号使用数字类型(好排序),同时让订单号逐渐递增,并具有一定的自解释语义(这里主要指的是订单的产生时间)
支持分布式部署之后,我们对订单号的生成规则,做如下调整:
1 2 3 4 5 6 |
1、32位时间戳不变(精确到秒,最多支持生成64年的订单号) 2、中间加10位唯一U8Server实例ID,从0开始,也就是最多支持1024个部署节点。 3、后22位序列号(不同秒之后,序号重置,从0开始) |
具体的占位说明如下图:
我们接下来看看订单号生成代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
public class IDGenerator { private static IDGenerator instance; private int serverID = 0; private long currOrderSequence = 0L; private long lastTimeStamp = -1L; private long sequenceMask = (1<<22); private IDGenerator(){ GlobalConfig config = (GlobalConfig) UApplicationContext.getBean("globalConfig"); if(config == null){ Log.e("GlobalConfig is not exists. deployID not config?"); return; } serverID = config.getDeployID(); } public static synchronized IDGenerator getInstance(){ if(instance == null){ instance = new IDGenerator(); } return instance; } public synchronized long nextOrderID(){ Calendar can = Calendar.getInstance(); int year = can.get(Calendar.YEAR) - 2013; int month = can.get(Calendar.MONTH) + 1; int day = can.get(Calendar.DAY_OF_MONTH); int hour = can.get(Calendar.HOUR_OF_DAY); int min = can.get(Calendar.MINUTE); int sec = can.get(Calendar.SECOND); long req = year; req = req << 4 | month; req = req << 5 | day; req = req << 5 | hour; req = req << 6 | min; req = req << 6 | sec; if(serverID >= 1024){ Log.e("U8Server deploy_id must be in 0(include) and 1024(exclude)"); return -1; } long currTime = req; if(req == lastTimeStamp){ this.currOrderSequence = this.currOrderSequence + 1; if(this.currOrderSequence >= sequenceMask){ this.currOrderSequence = sequenceMask; Log.e("WOW!!! u8server had generate more than %s orders per seconds. I'm sure you now have enough money to redevelop u8server to fix the problem", sequenceMask); return -1; } }else{ this.currOrderSequence = 0L; lastTimeStamp = currTime; } req = req << 10| serverID; req = req << 22| this.currOrderSequence; return req; } } |
我们对上面的代码,做一个简单的解释。
首先取到当前时间戳,就是当前年,月,日,小时,分钟,秒。 我们只精确到秒。对于月,天,小时,分钟,秒几个值,他的表示需要的最大的位数是固定的,比如,月,一年最大12个月,所以,我们用4位即可表示(1<<4=16),同理,对于天,一个月最大31天,用5位即可表示;对于小时,一天最多24小时,用5位即可表示;对于分和秒,最大都是60,我们用6位即可表示。
所以月日时分秒,几个总占位为4+5+5+6+6=26位(总时间戳预留是32位,所以表示年份的位数为6位,所以上面说最多可以生成64年的订单号)
时间戳之后, 再加上10位U8Server唯一实例ID,这个我们放在jdbc.properties中u8server.deploy_id进行配置,每部署一个实例,这个ID不能重复,从0开始,最大1023。
最后再加上22位序号。这个序号,每秒会进行重置。也就是说同一秒中, 单台U8Server实例最多可以生成4194304(1<<22)个订单,如果真的超出了,那么真的要恭喜你了。。。
这样最终就生成一个long类型的全局唯一的订单号了。
本文出自 U8SDK技术博客,转载时请注明出处及相应链接。
本文永久链接: http://www.uustory.com/?p=2146