雪花算法(snowflake) :分布式环境,生成全局唯一的订单号

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fly910905/article/details/82054196

snowflake方案

这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示:

 

41-bit的时间可以表示(1L<<41)/(1000L*3600*24*365)=69年的时间,10-bit机器可以分别表示1024台机器。如果我们对IDC划分有需求,还可以将10-bit分5-bit给IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,可以根据自身需求定义。12个自增序列号可以表示2^12个ID,理论上snowflake方案的QPS约为409.6w/s,这种分配方式可以保证在任何一个IDC的任何一台机器在任意毫秒内生成的ID都是不同的。

这种方式的优缺点是:

优点:

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点:

  • 强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。

 

snowflake实现

apache.commons.lang3包
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.6</version>
</dependency>

​​​
读取配置文件:https://blog.csdn.net/fly910905/article/details/78737323

工具类

package com.datalook.util.common;

import org.apache.commons.lang3.time.DateFormatUtils;


import java.util.Date;
/**
 * 
 * @Title:  订单号生成
 * @ClassName:OrderIdUtils.java
 * @Description:
 *
 * @Copyright 2016-2017  - Powered By 研发中心
 * @author: 王延飞
 * @date:2018年3月22日 下午7:43:30
 * @version V1.0
 */
public class OrderIdUtils {

    // 最近的时间戳
    private long lastTimestamp=0;
    //机器id 2位
    private final String machineId;
    // 0,并发控制
    private long sequence = 0L;
    // 序列号的最大值
    private final int sequenceMax = 9999;


    public OrderIdUtils(String machineId) {
        this.machineId = machineId;
    }

    /**
     * 生成订单号
     */
    public synchronized String nextId(){
        Date now=new Date();
        String time= DateFormatUtils.format(now,"yyMMddHHmmssSSS");
        long timestamp = now.getTime();
        if (this.lastTimestamp == timestamp) {
            // 如果上一个timestamp与新产生的相等,则sequence加一(0-4095循环);
            // 对新的timestamp,sequence从0开始
            this.sequence = this.sequence + 1 % this.sequenceMax;
            if (this.sequence == 0) {
                // 重新生成timestamp
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }
        this.lastTimestamp= timestamp;
        StringBuilder sb=new StringBuilder(time).append(machineId).append(leftPad(sequence,4));
        return sb.toString();
    }

    /**
     * 补码
     * @param i
     * @param n
     * @return
     */
    private String leftPad(long i,int n){
        String s = String.valueOf(i);
        StringBuilder sb=new StringBuilder();
        int c=n-s.length();
        c=c<0?0:c;
        for (int t=0;t<c;t++){
            sb.append("0");
        }
        return sb.append(s).toString();
    }

    /**
     * 等待下一个毫秒的到来, 保证返回的毫秒数在参数lastTimestamp之后
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    // 这里读取的是配置文件
    // 机器id(我这里是01,正式环境建议使用机器IP)
    // 注意:分布式环境,注意每台机器的id要保证不同;也可以使用机器ip,映射成一个数字编号(如01:192.168.55.12)
    private static String myid= SysConstant.LOCAL_MACHINE_ID;

    // 示例
    private static OrderIdUtils instance = new OrderIdUtils(myid);
    public static OrderIdUtils getInstance() {
        return instance;
    }
    
    
    
    /**
     * 
     * @Title: 获取订单号
     * @return String
     * @Description:
     *
     * @author: 王延飞
     * @date: 2018年3月22日 下午7:56:56
     */
    public static  String getOrderNumber() {

        OrderIdUtils orderId = OrderIdUtils.getInstance();
        String nextId = orderId.nextId();

        return nextId;
    }

    /**
     * 调用
     */
    public static void main(String[] args) {
        OrderIdUtils orderId= OrderIdUtils.getInstance();
        String nextId = orderId.nextId();
        int length = nextId.length();
        System.out.println(nextId);
        System.out.println(length);

    }
}

 

展开阅读全文

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