Twitter-Snowflake算法产生的背景相当简单,为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的id,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的id必须不同。
Snowflake 核心代码:
1 /** 2 * 3 */ 4 package utility; 5 6 /** 7 * @author Lumin(At Home) 8 * Twitter's Concurrent Id Generator -- SnowFlake 9 * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 10 * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0; 11 * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截) 12 * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker 13 * 类的startTime属性)。41位的时间截,可以使用69年,即 T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69. 14 * 10位的数据机器位,可以部署在1024个节点,包括: 15 * 5位datacenter Id:数据中心机器号 16 * 5位worker Id:工作机器号,预分配不重复的ID 17 * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号 18 * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分), 19 * 并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。 20 */ 21 public class SnowFlakeIdGenerator { 22 //开始时间戳:2015-01-01 23 private final long twitEpoch = 1420041600000L; 24 25 //机器ID的位数:5 26 private final long workerIdBits = 5L; 27 28 //数据中心ID的位数:5 29 private final long datacenterIdBits = 5L; 30 31 //机器ID的最大值:31 32 private final long maxWorkerId = -1L ^ (-1L << workerIdBits); 33 34 //数据中心ID的最大值:31 35 private final long maxDatacenterId = -1L ^ ( - 1L << datacenterIdBits); 36 37 //序列所占的位数:12 38 private final long sequenceBits = 12L; 39 40 //机器ID的偏移位数:12 41 private final long workerIdShift = sequenceBits; 42 43 //数据中心的偏移位数:12 + 5 = 17 44 private final long datacenterIdShift = sequenceBits + workerIdBits; 45 46 //时间戳的偏移位数:12 + 5 + 5 = 22 47 private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; 48 49 //生成序列的掩码,这里为:0b111111111111=0xfff=4095 50 private final long sequenceMask = -1L ^ (-1L << sequenceBits); 51 52 //工作机器的ID 53 private long workerId; 54 55 //数据中心的ID 56 private long datacenterId; 57 58 //毫秒内的序列值 59 private long sequence = 0L; 60 61 //上一次生成序列的时间戳 62 private long lastTimestamp = -1L; 63 64 /*** 65 * 构造器 66 * @param workerId 工作机器的ID 67 * @param datacenterId 数据中心的ID 68 */ 69 public SnowFlakeIdGenerator(long workerId, long datacenterId) { 70 if (workerId > maxWorkerId || workerId < 0) { 71 throw new IllegalArgumentException(String.format("worker Id cannot be greater than %d or less than 0", maxWorkerId)); 72 } 73 if (datacenterId > maxDatacenterId || datacenterId < 0) { 74 throw new IllegalArgumentException(String.format("datacenter Id cannot be greater than %d or less than 0", maxDatacenterId)); 75 } 76 this.workerId = workerId; 77 this.datacenterId = datacenterId; 78 } 79 80 /*** 81 * 获取下一个ID(线程安全的) 82 * @return SnowFlakeID 83 */ 84 public synchronized long getNextId() { 85 //获取时间戳 86 long timestamp = getTimeStamp(); 87 88 //当前时间戳小于上一次分配时的时间戳,证明系统时钟经过了回退,此时直接抛出异常即可 89 if (timestamp < lastTimestamp) { 90 throw new RuntimeException(String.format("clock moved backwards. Refused to generate id for %d milliseconds", lastTimestamp)); 91 } 92 93 //如果是同一个时间戳内,则进行毫秒内的序列生成 94 if (lastTimestamp == timestamp) { 95 sequence = (sequence + 1) & sequenceMask; 96 if (sequence == 0) { 97 //当前毫秒内的序列号用完了,等待至下一秒再分配 98 timestamp = waitForNextMillis(lastTimestamp); 99 } 100 // 时间戳改变,重置毫秒内的序列为0 101 } else { 102 sequence = 0L; 103 } 104 105 lastTimestamp = timestamp; 106 107 //拼接ID 108 return ((timestamp - twitEpoch) << timestampLeftShift) 109 | (datacenterId << datacenterIdShift) 110 | (workerId << workerIdShift) 111 | sequence; 112 } 113 114 /*** 115 * 用循环进行当前毫秒的阻塞,直至新的时间戳 116 * @param lastTimestamp 上一次生成ID的时间戳 117 * @return 新的时间戳 118 */ 119 protected long waitForNextMillis(long lastTimestamp) { 120 long timestamp = getTimeStamp(); 121 while(timestamp <= lastTimestamp) { 122 timestamp = getTimeStamp(); 123 } 124 return timestamp; 125 } 126 127 /*** 128 * 当前时间 129 * @return 以毫秒为单位的当前时间 130 */ 131 protected long getTimeStamp() { 132 return System.currentTimeMillis() / 1000; //除以1000是为了将毫秒放大到秒,便于观察ID溢出 133 } 134 }
测试:
1 package test; 2 3 import utility.SnowFlakeIdGenerator; 4 5 public class SnowFlakeTest { 6 7 /*** 8 * 测试Snowflake 9 * @param args 10 */ 11 public static void main(String[] args) { 12 SnowFlakeIdGenerator snowFlaker = new SnowFlakeIdGenerator(22, 11); 13 for(int i = 0; i < 100000; i++) { 14 System.out.println(Long.toBinaryString(snowFlaker.getNextId())); 15 } 16 } 17 }
结果:
1010110101101110001011111011001011100001110101110110101101000011 1010110101101110001011111011001011100001110101110110101101000100 1010110101101110001011111011001011100001110101110110101101000101 1010110101101110001011111011001011100001110101110110101101000110 1010110101101110001011111011001011100001110101110110101101000111 1010110101101110001011111011001011100001110101110110101101001000 1010110101101110001011111011001011100001110101110110101101001001 1010110101101110001011111011001011100001110101110110101101001010 1010110101101110001011111011001011100001110101110110101101001011 1010110101101110001011111011001011100001110101110110101101001100 1010110101101110001011111011001011100001110101110110101101001101 1010110101101110001011111011001011100001110101110110101101001110 1010110101101110001011111011001011100001110101110110101101001111 1010110101101110001011111011001011100001110101110110101101010000 1010110101101110001011111011001011100001110101110110101101010001 1010110101101110001011111011001011100001110101110110101101010010 1010110101101110001011111011001011100001110101110110101101010011 1010110101101110001011111011001011100001110101110110101101010100 1010110101101110001011111011001011100001110101110110101101010101
...