栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

分布式唯一id生成 - 补充中没写完

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

分布式唯一id生成 - 补充中没写完

文章目录
  • 前言
  • 一、推特SnowFlake
  • 二、百度UidGenerator
  • 三、美团Leaf
  • 四、Mongo - ObjectId
  • 总结

前言

各式各样的雪花算法 - Java实现

一、推特SnowFlake
public final class SnowFlake {
    // 起始的时间戳
    private final static long START_STAMP = 1577808000000L; //2020-01-01
    // 每一部分占用的位数,就三个
    private final static long SEQUENCE_BIT = 12; //序列号占用的位数
    private final static long MACHINE_BIT = 5; //机器标识占用的位数
    private final static long DATACENTER_BIT = 5; //数据中心占用的位数
    // 每一部分最大值
    private final static long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
    private final static long MAX_MACHINE_NUM = ~(-1L << MACHINE_BIT);
    private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BIT);
    // 每一部分向左的位移
    private final static long MACHINE_LEFT = SEQUENCE_BIT;
    private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
    private final static long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
    private long datacenterId; //数据中心
    private long machineId; //机器标识
    private long sequence = 0L; //序列号
    private long lastStamp = -1L; //上一次时间戳

    
    public SnowFlake(long datacenterId, long machineId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }
        if (machineId > MAX_MACHINE_NUM || machineId < 0) {
            throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }
        this.datacenterId = datacenterId;
        this.machineId = machineId;
    }

    
    public synchronized long nextId() {
        long curryStamp = timeGen();
        if (curryStamp < lastStamp) {
            throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }

        if (curryStamp == lastStamp) {
            // if条件里表示当前调用和上一次调用落在了相同毫秒内,只能通过第三部分,序列号自增来判断为唯一,所以+1.
            sequence = (sequence + 1) & MAX_SEQUENCE;
            //同一毫秒的序列数已经达到最大,只能等待下一个毫秒
            if (sequence == 0L) {
                curryStamp = getNextMill();
            }
        } else {
            // 不同毫秒内,序列号置为0
            // 执行到这个分支的前提是currTimestamp > lastTimestamp,说明本次调用跟上次调用对比,已经不再同一个毫秒内了,这个时候序号可以重新回置0了。
            sequence = 0L;
        }
        lastStamp = curryStamp;
        // 就是用相对毫秒数、机器ID和自增序号拼接
        return (curryStamp - START_STAMP) << TIMESTAMP_LEFT // 时间戳部分
                | datacenterId << DATACENTER_LEFT           // 数据中心部分
                | machineId << MACHINE_LEFT                 // 机器标识部分
                | sequence;                                 // 序列号部分
    }

    
    private long getNextMill() {
        long mill = timeGen();
        while (mill <= lastStamp) {
            mill = timeGen();
        }
        return mill;
    }

    
    private long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(2, 3);
        for (int i = 0; i < 10; i++) {
            System.out.println(snowFlake.nextId());
        }
    }

}

有一个缺点,在高并发场景下如果不稍加处理还是会出现重复的id。

二、百度UidGenerator
文章目录
GitHub - baidu/uid-generator: UniqueID generator
生成全局唯一ID之UidGenerator_三刻的博客-CSDN博客_uidgenerator

基于雪花算法进行二次改进,克服了雪花算法的并发限制数问题。

三、美团Leaf
文章目录
Leaf/README_CN.md at master · Meituan-Dianping/Leaf · GitHub
Leaf——美团点评分布式ID生成系统 - 美团技术团队 (meituan.com)
四、Mongo - ObjectId

我测试了一下单机环境的毫秒级并发还是很难吃的住这个唯一性,但是非高并发场景下的唯一id是没什么问题。

import java.io.Serializable;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;


public final class NewMongoUidGeneratorUtil implements Comparable, Serializable {

    private static final long serialVersionUID = 3670079982654483072L;

    private static final int OBJECT_ID_LENGTH = 12;
    private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff;

    // Use primitives to represent the 5-byte random value.
    private static final int RANDOM_VALUE1;
    private static final short RANDOM_VALUE2;

    private static final AtomicInteger NEXT_COUNTER = new AtomicInteger(new SecureRandom().nextInt());

    private static final char[] HEX_CHARS = new char[]{
            '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private final int timestamp;
    private final int counter;
    private final int randomValue1;
    private final short randomValue2;

    
    public static NewMongoUidGeneratorUtil get() {
        return new NewMongoUidGeneratorUtil();
    }

    
    public static boolean isValid(final String hexString) {
        if (hexString == null) {
            throw new IllegalArgumentException();
        }

        int len = hexString.length();
        if (len != 24) {
            return false;
        }

        for (int i = 0; i < len; i++) {
            char c = hexString.charAt(i);
            if (c >= '0' && c <= '9') {
                continue;
            }
            if (c >= 'a' && c <= 'f') {
                continue;
            }
            if (c >= 'A' && c <= 'F') {
                continue;
            }

            return false;
        }

        return true;
    }

    
    public NewMongoUidGeneratorUtil() {
        this(new Date());
    }

    
    public NewMongoUidGeneratorUtil(final Date date) {
        this(dateToTimestampSeconds(date), NEXT_COUNTER.getAndIncrement() & LOW_ORDER_THREE_BYTES, false);
    }

    
    public NewMongoUidGeneratorUtil(final Date date, final int counter) {
        this(dateToTimestampSeconds(date), counter, true);
    }

    
    @Deprecated
    public NewMongoUidGeneratorUtil(final Date date, final int machineIdentifier, final short processIdentifier, final int counter) {
        this(dateToTimestampSeconds(date), machineIdentifier, processIdentifier, counter);
    }

    
    @Deprecated
    public NewMongoUidGeneratorUtil(final int timestamp, final int machineIdentifier, final short processIdentifier, final int counter) {
        this(timestamp, machineIdentifier, processIdentifier, counter, true);
    }

    
    public NewMongoUidGeneratorUtil(final int timestamp, final int counter) {
        this(timestamp, counter, true);
    }

    private NewMongoUidGeneratorUtil(final int timestamp, final int counter, final boolean checkCounter) {
        this(timestamp, RANDOM_VALUE1, RANDOM_VALUE2, counter, checkCounter);
    }

    private NewMongoUidGeneratorUtil(final int timestamp, final int randomValue1, final short randomValue2, final int counter,
                                     final boolean checkCounter) {
        if ((randomValue1 & 0xff000000) != 0) {
            throw new IllegalArgumentException("The machine identifier must be between 0 and 16777215 (it must fit in three bytes).");
        }
        if (checkCounter && ((counter & 0xff000000) != 0)) {
            throw new IllegalArgumentException("The counter must be between 0 and 16777215 (it must fit in three bytes).");
        }
        this.timestamp = timestamp;
        this.counter = counter & LOW_ORDER_THREE_BYTES;
        this.randomValue1 = randomValue1;
        this.randomValue2 = randomValue2;
    }

    
    public NewMongoUidGeneratorUtil(final String hexString) {
        this(parseHexString(hexString));
    }

    
    public NewMongoUidGeneratorUtil(final byte[] bytes) {
        this(ByteBuffer.wrap(bytes));
    }

    
    NewMongoUidGeneratorUtil(final int timestamp, final int machineAndProcessIdentifier, final int counter) {
        this(legacyToBytes(timestamp, machineAndProcessIdentifier, counter));
    }

    
    public NewMongoUidGeneratorUtil(final ByteBuffer buffer) {
        if (buffer == null) {
            throw new IllegalArgumentException("buffer can not be null");
        }
        if (!(buffer.remaining() >= OBJECT_ID_LENGTH)) {
            throw new IllegalArgumentException("state should be: buffer.remaining() >=12");
        }

        // Note: Cannot use ByteBuffer.getInt because it depends on tbe buffer's byte order
        // and ObjectId's are always in big-endian order.
        timestamp = makeInt(buffer.get(), buffer.get(), buffer.get(), buffer.get());
        randomValue1 = makeInt((byte) 0, buffer.get(), buffer.get(), buffer.get());
        randomValue2 = makeShort(buffer.get(), buffer.get());
        counter = makeInt((byte) 0, buffer.get(), buffer.get(), buffer.get());
    }

    private static byte[] legacyToBytes(final int timestamp, final int machineAndProcessIdentifier, final int counter) {
        byte[] bytes = new byte[OBJECT_ID_LENGTH];
        bytes[0] = int3(timestamp);
        bytes[1] = int2(timestamp);
        bytes[2] = int1(timestamp);
        bytes[3] = int0(timestamp);
        bytes[4] = int3(machineAndProcessIdentifier);
        bytes[5] = int2(machineAndProcessIdentifier);
        bytes[6] = int1(machineAndProcessIdentifier);
        bytes[7] = int0(machineAndProcessIdentifier);
        bytes[8] = int3(counter);
        bytes[9] = int2(counter);
        bytes[10] = int1(counter);
        bytes[11] = int0(counter);
        return bytes;
    }

    
    public byte[] toByteArray() {
        ByteBuffer buffer = ByteBuffer.allocate(OBJECT_ID_LENGTH);
        putToByteBuffer(buffer);
        return buffer.array();  // using .allocate ensures there is a backing array that can be returned
    }

    
    public void putToByteBuffer(final ByteBuffer buffer) {
        if (buffer == null) {
            throw new IllegalArgumentException("buffer can not be null");
        }
        if (!(buffer.remaining() >= OBJECT_ID_LENGTH)) {
            throw new IllegalArgumentException("state should be: buffer.remaining() >=12");
        }

        buffer.put(int3(timestamp));
        buffer.put(int2(timestamp));
        buffer.put(int1(timestamp));
        buffer.put(int0(timestamp));
        buffer.put(int2(randomValue1));
        buffer.put(int1(randomValue1));
        buffer.put(int0(randomValue1));
        buffer.put(short1(randomValue2));
        buffer.put(short0(randomValue2));
        buffer.put(int2(counter));
        buffer.put(int1(counter));
        buffer.put(int0(counter));
    }

    
    public int getTimestamp() {
        return timestamp;
    }

    
    public Date getDate() {
        return new Date((timestamp & 0xFFFFFFFFL) * 1000L);
    }

    
    public String toHexString() {
        char[] chars = new char[OBJECT_ID_LENGTH * 2];
        int i = 0;
        for (byte b : toByteArray()) {
            chars[i++] = HEX_CHARS[b >> 4 & 0xF];
            chars[i++] = HEX_CHARS[b & 0xF];
        }
        return new String(chars);
    }

    @Override
    public boolean equals(final Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        NewMongoUidGeneratorUtil objectId = (NewMongoUidGeneratorUtil) o;

        if (counter != objectId.counter) {
            return false;
        }
        if (timestamp != objectId.timestamp) {
            return false;
        }

        if (randomValue1 != objectId.randomValue1) {
            return false;
        }

        if (randomValue2 != objectId.randomValue2) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = timestamp;
        result = 31 * result + counter;
        result = 31 * result + randomValue1;
        result = 31 * result + randomValue2;
        return result;
    }

    @Override
    public int compareTo(final NewMongoUidGeneratorUtil other) {
        if (other == null) {
            throw new NullPointerException();
        }

        byte[] byteArray = toByteArray();
        byte[] otherByteArray = other.toByteArray();
        for (int i = 0; i < OBJECT_ID_LENGTH; i++) {
            if (byteArray[i] != otherByteArray[i]) {
                return ((byteArray[i] & 0xff) < (otherByteArray[i] & 0xff)) ? -1 : 1;
            }
        }
        return 0;
    }

    @Override
    public String toString() {
        return toHexString();
    }

    // Deprecated methods

    
    @Deprecated
    public static NewMongoUidGeneratorUtil createFromLegacyFormat(final int time, final int machine, final int inc) {
        return new NewMongoUidGeneratorUtil(time, machine, inc);
    }

    
    @Deprecated
    public static int getCurrentCounter() {
        return NEXT_COUNTER.get() & LOW_ORDER_THREE_BYTES;
    }

    
    @Deprecated
    public static int getGeneratedMachineIdentifier() {
        return RANDOM_VALUE1;
    }

    
    @Deprecated
    public static int getGeneratedProcessIdentifier() {
        return RANDOM_VALUE2;
    }

    
    @Deprecated
    public int getMachineIdentifier() {
        return randomValue1;
    }

    
    @Deprecated
    public short getProcessIdentifier() {
        return randomValue2;
    }

    
    @Deprecated
    public int getCounter() {
        return counter;
    }

    
    @Deprecated
    public int getTimeSecond() {
        return timestamp;
    }

    
    @Deprecated
    public long getTime() {
        return (timestamp & 0xFFFFFFFFL) * 1000L;
    }

    
    @Deprecated
    public String toStringMongod() {
        return toHexString();
    }

    static {
        try {
            SecureRandom secureRandom = new SecureRandom();
            RANDOM_VALUE1 = secureRandom.nextInt(0x01000000);
            RANDOM_VALUE2 = (short) secureRandom.nextInt(0x00008000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static byte[] parseHexString(final String s) {
        if (!isValid(s)) {
            throw new IllegalArgumentException("invalid hexadecimal representation of an ObjectId: [" + s + "]");
        }

        byte[] b = new byte[OBJECT_ID_LENGTH];
        for (int i = 0; i < b.length; i++) {
            b[i] = (byte) Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16);
        }
        return b;
    }

    private static int dateToTimestampSeconds(final Date time) {
        return (int) (time.getTime() / 1000);
    }

    // Big-Endian helpers, in this class because all other BSON numbers are little-endian

    private static int makeInt(final byte b3, final byte b2, final byte b1, final byte b0) {
        // CHECKSTYLE:OFF
        return (((b3) << 24) |
                ((b2 & 0xff) << 16) |
                ((b1 & 0xff) << 8) |
                ((b0 & 0xff)));
        // CHECKSTYLE:ON
    }

    private static short makeShort(final byte b1, final byte b0) {
        // CHECKSTYLE:OFF
        return (short) (((b1 & 0xff) << 8) | ((b0 & 0xff)));
        // CHECKSTYLE:ON
    }

    private static byte int3(final int x) {
        return (byte) (x >> 24);
    }

    private static byte int2(final int x) {
        return (byte) (x >> 16);
    }

    private static byte int1(final int x) {
        return (byte) (x >> 8);
    }

    private static byte int0(final int x) {
        return (byte) (x);
    }

    private static byte short1(final short x) {
        return (byte) (x >> 8);
    }

    private static byte short0(final short x) {
        return (byte) (x);
    }

    
    public static void main(String[] args) {
        String string = new NewMongoUidGeneratorUtil().toHexString();
    }
}

原理回头再细细研究。

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/857607.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号