zoukankan      html  css  js  c++  java
  • 关于Snowflake 生成53位ID

    1, bug现象: 没有经过处理的Snowflake 生成的是64位bit的唯一ID,但由于多数时候我们前台用到js,但是js只支持53位bit的数值。这样就导致了传到前台的64位的丢失精度。

          解决思路:修改SnowFlake 的算法,使它生成 53bit的唯一ID,就可以了,代码如下

    package com.wisdombud.product.configure.snowflake;
    
    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.time.LocalDate;
    import java.time.ZoneId;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class SnowflakeIdWorker {
    
        private static final Pattern PATTERN_LONG_ID = Pattern.compile("^([0-9]{15})([0-9a-f]{32})([0-9a-f]{3})$");
    
        private static final Pattern PATTERN_HOSTNAME = Pattern.compile("^.*\D+([0-9]+)$");
    
        private static final long OFFSET = LocalDate.of(2000, 1, 1).atStartOfDay(ZoneId.of("Z")).toEpochSecond();
    
        private static final long MAX_NEXT = 0b11111_11111111_111L;
    
        private static final long SHARD_ID = getServerIdAsLong();
    
        private static long offset = 0;
    
        private static long lastEpoch = 0;
    
        public static long getNextId() {
            return nextId(System.currentTimeMillis() / 1000);
        }
    
        private static synchronized long nextId(long epochSecond) {
            if (epochSecond < lastEpoch) {
                epochSecond = lastEpoch;
            }
            if (lastEpoch != epochSecond) {
                lastEpoch = epochSecond;
                reset();
            }
            offset++;
            long next = offset & MAX_NEXT;
            if (next == 0) {
                return nextId(epochSecond + 1);
            }
            return generateId(epochSecond, next, SHARD_ID);
        }
    
        private static void reset() {
            offset = 0;
        }
    
        private static long generateId(long epochSecond, long next, long shardId) {
            return ((epochSecond - OFFSET) << 21) | (next << 5) | shardId;
        }
    
        private static long getServerIdAsLong() {
            try {
                String hostname = InetAddress.getLocalHost().getHostName();
                Matcher matcher = PATTERN_HOSTNAME.matcher(hostname);
                if (matcher.matches()) {
                    long n = Long.parseLong(matcher.group(1));
                    if (n >= 0 && n < 8) {
                        return n;
                    }
                }
            } catch (UnknownHostException e) {
                
            }
            return 0;
        }
    }

    参考自: https://my.oschina.net/u/2552286/blog/3115621/print

    64位的Snowflake 的算法

    package com.wisdombud.product.configure.snowflake;
    
    public class SnowflakeIdWorker {
    
        private final long twepoch            = 1420041600000L;
    
        private final long workerIdBits       = 5L;
    
        private final long datacenterIdBits   = 5L;
    
        private final long maxWorkerId        = -1L ^ (-1L << workerIdBits);
    
        private final long maxDatacenterId    = -1L ^ (-1L << datacenterIdBits);
    
        private final long sequenceBits       = 12L;
    
        private final long workerIdShift      = sequenceBits;
    
        private final long datacenterIdShift  = sequenceBits + workerIdBits;
    
        private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    
        private final long sequenceMask       = -1L ^ (-1L << sequenceBits);
    
        private Long       workerId           = 2L;
    
        private Long       datacenterId       = 1L;
    
        private long       sequence           = 0L;
    
        private long       lastTimestamp      = -1L;
    
        public SnowflakeIdWorker() {
    
        }
    
        public synchronized long nextId() {
            long timestamp = timeGen();
    
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(String
                        .format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                                lastTimestamp - timestamp));
            }
    
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
    
                if (sequence == 0) {
    
                    timestamp = tilNextMillis(lastTimestamp);
                }
            }
    
            else {
                sequence = 0L;
            }
    
            lastTimestamp = timestamp;
    
            return ((timestamp - twepoch) << timestampLeftShift) //
                   | (datacenterId << datacenterIdShift) //
                   | (workerId << workerIdShift) //
                   | sequence;
        }
    
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
    
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        public enum SnowflakeInstance {
            INSTANCE();
    
            private SnowflakeIdWorker singleton;
    
            SnowflakeInstance() {
                singleton = new SnowflakeIdWorker();
            }
    
            public SnowflakeIdWorker getInstance() {
                return singleton;
            }
        }
        
        public static Long getNextId() {
            return SnowflakeInstance.INSTANCE.getInstance().nextId();
        }
    }
  • 相关阅读:
    使用Spring MVC统一异常处理实战(转载)
    java 异常
    Java 接口和内部类
    JAVA 继承
    Linux中profile(转载)
    java 对象与类
    Java基本的程序结构设计 数组
    Python虚拟机函数机制之位置参数(四)
    Python虚拟机函数机制之参数类别(三)
    Python虚拟机函数机制之名字空间(二)
  • 原文地址:https://www.cnblogs.com/qiaoyutao/p/11662486.html
Copyright © 2011-2022 走看看