美文网首页
leaf SnowflakeIDGenImpl 源码解析

leaf SnowflakeIDGenImpl 源码解析

作者: pcgreat | 来源:发表于2020-09-22 10:30 被阅读0次

    leaf SnowflakeIDGenImpl

    1. leaf SnowflakeIDGenImpl 对应的是Leaf-snowflake的实现 ,源码位置
      https://github.com/Meituan-Dianping/Leaf/blob/0dc819c9fd8505bf0ec8e23b40ab3465c451c28b/leaf-core/src/main/java/com/sankuai/inf/leaf/snowflake/SnowflakeIDGenImpl.java
    2. Leaf-snowflake设计详情可以看美团同学的文章
      https://tech.meituan.com/2017/04/21/mt-leaf.html

    snowflake 优点

    • 全局唯一性
    • 有序性
    • 高效性

    snowflake 缺点

    • 时钟回拨
    • 业务团队不好设置workerid

    美团leaf 针对于两个snowflake 缺点做了优化
    从SnowflakeIDGenImpl的get方法 可以看出 id生成逻辑比较简单
    1 . 判断是否发生时钟回拨 ,如果发生 ,如果offset 超过5ms ,返回new Result(-3, Status.EXCEPTION); ,如果 5ms 之内 则 wait 左移一位时间。重新判断是否发生时钟回拨 ,是 ,抛出异常 。否 进入正常逻辑 。

    1. sequence 是全局变量的,记录上次序列id,以及本次序列id ,他的生成逻辑很简单,具体看下面的源码 , 他和key查询参数毫无关联 , 可以对方法级别synchronize 做优化 ,但是snowflake协议 需要加上 key 对应的业务id 。可以作为优化的点,提升并发度 ,但是会破坏协议 ,新的业务可以用,不兼容原本snowflake
    2. workerID 是从Zookeeper拿取workerID,每个leaf instance 会拿到自己的workerid , 而且动态扩展,无需开发人员配置的 。并且会在本机文件系统上缓存一个workerID文件 。
    3. id长度最少23bit =
      long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
    package com.sankuai.inf.leaf.snowflake;
    
    import com.google.common.base.Preconditions;
    import com.sankuai.inf.leaf.IDGen;
    import com.sankuai.inf.leaf.common.Result;
    import com.sankuai.inf.leaf.common.Status;
    import com.sankuai.inf.leaf.common.Utils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Random;
    
    public class SnowflakeIDGenImpl implements IDGen {
    
        @Override
        public boolean init() {
            return true;
        }
    
        private static final Logger LOGGER = LoggerFactory.getLogger(SnowflakeIDGenImpl.class);
    
        private final long twepoch;
        private final long workerIdBits = 10L;
        private final long maxWorkerId = ~(-1L << workerIdBits);//最大能够分配的workerid =1023
        private final long sequenceBits = 12L;
        private final long workerIdShift = sequenceBits;
        private final long timestampLeftShift = sequenceBits + workerIdBits;
        private final long sequenceMask = ~(-1L << sequenceBits);
        private long workerId;
        private long sequence = 0L;
        private long lastTimestamp = -1L;
        private static final Random RANDOM = new Random();
    
        public SnowflakeIDGenImpl(String zkAddress, int port) {
            //Thu Nov 04 2010 09:42:54 GMT+0800 (中国标准时间) 
            this(zkAddress, port, 1288834974657L);
        }
    
        /**
         * @param zkAddress zk地址
         * @param port      snowflake监听端口
         * @param twepoch   起始的时间戳
         */
        public SnowflakeIDGenImpl(String zkAddress, int port, long twepoch) {
            this.twepoch = twepoch;
            Preconditions.checkArgument(timeGen() > twepoch, "Snowflake not support twepoch gt currentTime");
            final String ip = Utils.getIp();
            SnowflakeZookeeperHolder holder = new SnowflakeZookeeperHolder(ip, String.valueOf(port), zkAddress);
            LOGGER.info("twepoch:{} ,ip:{} ,zkAddress:{} port:{}", twepoch, ip, zkAddress, port);
            boolean initFlag = holder.init();
            if (initFlag) {
                workerId = holder.getWorkerID();
                LOGGER.info("START SUCCESS USE ZK WORKERID-{}", workerId);
            } else {
                Preconditions.checkArgument(initFlag, "Snowflake Id Gen is not init ok");
            }
            Preconditions.checkArgument(workerId >= 0 && workerId <= maxWorkerId, "workerID must gte 0 and lte 1023");
        }
    
        @Override
        public synchronized Result get(String key) {
            long timestamp = timeGen();
            if (timestamp < lastTimestamp) {
                long offset = lastTimestamp - timestamp;
                if (offset <= 5) {
                    try {
                        wait(offset << 1);
                        timestamp = timeGen();
                        if (timestamp < lastTimestamp) {
                            return new Result(-1, Status.EXCEPTION);
                        }
                    } catch (InterruptedException e) {
                        LOGGER.error("wait interrupted");
                        return new Result(-2, Status.EXCEPTION);
                    }
                } else {
                    return new Result(-3, Status.EXCEPTION);
                }
            }
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                if (sequence == 0) {
                    //seq 为0的时候表示是下一毫秒时间开始对seq做随机
                    sequence = RANDOM.nextInt(100);
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                //如果是新的ms开始
                sequence = RANDOM.nextInt(100);
            }
            lastTimestamp = timestamp;
            long id = ((timestamp - twepoch) << timestampLeftShift) | (workerId << workerIdShift) | sequence;
            return new Result(id, Status.SUCCESS);
    
        }
    
        protected long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        protected long timeGen() {
            return System.currentTimeMillis();
        }
    
        public long getWorkerId() {
            return workerId;
        }
    
    }
    

    相关文章

      网友评论

          本文标题:leaf SnowflakeIDGenImpl 源码解析

          本文链接:https://www.haomeiwen.com/subject/zfoiyktx.html