U8Server——分布式环境下唯一订单号生成规则

U8Server支持分布式部署之后, 我们还有一个问题需要解决, 那就是分布式环境下,唯一订单号生成问题。 之前U8Server的订单号生成规则是 32位时间戳+32位序号,最终生成一个64位的long类型的订单号。

之前也考虑过,使用UUID等方式, 但是综合考虑下来, 我们决定还是让订单号使用数字类型(好排序),同时让订单号逐渐递增,并具有一定的自解释语义(这里主要指的是订单的产生时间)

支持分布式部署之后,我们对订单号的生成规则,做如下调整:

1、32位时间戳不变(精确到秒,最多支持生成64年的订单号)

2、中间加10位唯一U8Server实例ID,从0开始,也就是最多支持1024个部署节点。

3、后22位序列号(不同秒之后,序号重置,从0开始)

具体的占位说明如下图:

订单号生成规则

我们接下来看看订单号生成代码:

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 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 技术博客

展开阅读全文

没有更多推荐了,返回首页