0%

根据twitter的snowflake算法生成唯一ID - 梁照彬 - 博客园

Excerpt

C#版本 JAVA版本


C#版本

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> 根据twitter的snowflake算法生成唯一ID
</span><span>///</span><span> snowflake算法 64 位
</span><span>///</span><span> 0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
</span><span>///</span><span> 第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
</span><span>///</span><span> 其中datacenter标识位起始是机器位,机器ID其实是线程标识,可以同一一个10位来表示不同机器
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>public</span> <span>class</span><span> IdWorker
{
</span><span>//</span><span>机器ID</span>
<span>private</span> <span>static</span> <span>long</span> workerId = <span>1</span><span>;
</span><span>private</span> <span>static</span> <span>long</span> twepoch = <span>687888001020L</span>; <span>//</span><span>唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳</span>
<span>private</span> <span>static</span> <span>long</span> sequence = <span>0L</span><span>;
</span><span>private</span> <span>static</span> <span>int</span> workerIdBits = <span>4</span>; <span>//</span><span>机器码字节数。4个字节用来保存机器码</span>
<span>public</span> <span>static</span> <span>long</span> maxWorkerId = -<span>1L</span> ^ -<span>1L</span> &lt;&lt; workerIdBits; <span>//</span><span>最大机器ID</span>
<span>private</span> <span>static</span> <span>int</span> sequenceBits = <span>10</span>; <span>//</span><span>计数器字节数,10个字节用来保存计数码</span>
<span>private</span> <span>static</span> <span>int</span> workerIdShift = sequenceBits; <span>//</span><span>机器码数据左移位数,就是后面计数器占用的位数</span>
<span>private</span> <span>static</span> <span>int</span> timestampLeftShift = sequenceBits + workerIdBits; <span>//</span><span>时间戳左移动位数就是机器码和计数器总字节数</span>
<span>public</span> <span>static</span> <span>long</span> sequenceMask = -<span>1L</span> ^ -<span>1L</span> &lt;&lt; sequenceBits; <span>//</span><span>一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成</span>
<span>private</span> <span>long</span> lastTimestamp = -<span>1L</span><span>;

</span><span>public</span> <span>long</span><span> nextId()
{
</span><span>lock</span> (<span>this</span><span>)
{
</span><span>long</span> timestamp =<span> timeGen();
</span><span>if</span> (<span>this</span>.lastTimestamp ==<span> timestamp)
{ </span><span>//</span><span>同一微妙中生成ID</span>
IdWorker.sequence = (IdWorker.sequence + <span>1</span>) &amp; IdWorker.sequenceMask; <span>//</span><span>用&amp;运算计算该微秒内产生的计数是否已经到达上限</span>
<span>if</span> (IdWorker.sequence == <span>0</span><span>)
{
</span><span>//</span><span>一微妙内产生的ID计数已达上限,等待下一微妙</span>
timestamp = tillNextMillis(<span>this</span><span>.lastTimestamp);
}
}
</span><span>else</span><span>
{ </span><span>//</span><span>不同微秒生成ID</span>
IdWorker.sequence = <span>0</span>; <span>//</span><span>计数清0</span>
<span> }
</span><span>if</span> (timestamp &lt;<span> lastTimestamp)
{ </span><span>//</span><span>如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过</span>
<span>throw</span> <span>new</span> Exception(<span>string</span>.Format(<span>"</span><span>Clock moved backwards. Refusing to generate id for {0} milliseconds</span><span>"</span><span>,
</span><span>this</span>.lastTimestamp -<span> timestamp));
}
</span><span>this</span>.lastTimestamp = timestamp; <span>//</span><span>把当前时间戳保存为最后生成ID的时间戳</span>
<span>long</span> nextId = (timestamp - twepoch &lt;&lt; timestampLeftShift) | IdWorker.workerId &lt;&lt; IdWorker.workerIdShift |<span> IdWorker.sequence;
</span><span>return</span><span> nextId;
}
}

</span><span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> 获取下一微秒时间戳
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>///</span> <span>&lt;param name="lastTimestamp"&gt;&lt;/param&gt;</span>
<span>///</span> <span>&lt;returns&gt;&lt;/returns&gt;</span>
<span>private</span> <span>long</span> tillNextMillis(<span>long</span><span> lastTimestamp)
{
</span><span>long</span> timestamp =<span> timeGen();
</span><span>while</span> (timestamp &lt;=<span> lastTimestamp)
{
timestamp </span>=<span> timeGen();
}
</span><span>return</span><span> timestamp;
}

</span><span>///</span> <span>&lt;summary&gt;</span>
<span>///</span><span> 生成当前时间戳
</span><span>///</span> <span>&lt;/summary&gt;</span>
<span>///</span> <span>&lt;returns&gt;&lt;/returns&gt;</span>
<span>private</span> <span>long</span><span> timeGen()
{
</span><span>return</span> (<span>long</span>)(DateTime.UtcNow - <span>new</span> DateTime(<span>1970</span>, <span>1</span>, <span>1</span>, <span>0</span>, <span>0</span>, <span>0</span><span>, DateTimeKind.Utc)).TotalMilliseconds;
}
}</span>

复制代码

JAVA版本

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<span>import</span><span> org.slf4j.Logger;
</span><span>import</span><span> org.slf4j.LoggerFactory;
</span><span>import</span><span> org.springframework.stereotype.Component;
</span>
<span>public</span> <span>class</span><span> IdWorker {

</span><span>protected</span> <span>static</span> <span>final</span> Logger LOG = LoggerFactory.getLogger(IdWorker.<span>class</span><span>);

</span><span>private</span> <span>long</span><span> workerId;
</span><span>private</span> <span>long</span><span> datacenterId;
</span><span>private</span> <span>long</span> sequence = 0L<span>;

</span><span>private</span> <span>long</span> twepoch = 1288834974657L<span>;

</span><span>private</span> <span>long</span> workerIdBits = 5L<span>;
</span><span>private</span> <span>long</span> datacenterIdBits = 5L<span>;
</span><span>private</span> <span>long</span> maxWorkerId = -1L ^ (-1L &lt;&lt;<span> workerIdBits);
</span><span>private</span> <span>long</span> maxDatacenterId = -1L ^ (-1L &lt;&lt;<span> datacenterIdBits);
</span><span>private</span> <span>long</span> sequenceBits = 12L<span>;

</span><span>private</span> <span>long</span> workerIdShift =<span> sequenceBits;
</span><span>private</span> <span>long</span> datacenterIdShift = sequenceBits +<span> workerIdBits;
</span><span>private</span> <span>long</span> timestampLeftShift = sequenceBits + workerIdBits +<span> datacenterIdBits;
</span><span>private</span> <span>long</span> sequenceMask = -1L ^ (-1L &lt;&lt;<span> sequenceBits);

</span><span>private</span> <span>long</span> lastTimestamp = -1L<span>;

</span><span>public</span> IdWorker(<span>long</span> workerId, <span>long</span><span> datacenterId) {
</span><span>//</span><span> sanity check for workerId</span>
<span>if</span> (workerId &gt; maxWorkerId || workerId &lt; 0<span>) {
</span><span>throw</span> <span>new</span> IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0"<span>, maxWorkerId));
}
</span><span>if</span> (datacenterId &gt; maxDatacenterId || datacenterId &lt; 0<span>) {
</span><span>throw</span> <span>new</span> IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0"<span>, maxDatacenterId));
}
</span><span>this</span>.workerId =<span> workerId;
</span><span>this</span>.datacenterId =<span> datacenterId;
LOG.info(String.format(</span>"worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d"<span>, timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId));
}

</span><span>public</span> <span>synchronized</span> <span>long</span><span> nextId() {
</span><span>long</span> timestamp =<span> timeGen();

</span><span>if</span> (timestamp &lt;<span> lastTimestamp) {
LOG.error(String.format(</span>"clock is moving backwards. Rejecting requests until %d."<span>, lastTimestamp));
</span><span>throw</span> <span>new</span> RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp -<span> timestamp));
}

</span><span>if</span> (lastTimestamp ==<span> timestamp) {
sequence </span>= (sequence + 1) &amp;<span> sequenceMask;
</span><span>if</span> (sequence == 0<span>) {
timestamp </span>=<span> tilNextMillis(lastTimestamp);
}
} </span><span>else</span><span> {
sequence </span>= 0L<span>;
}

lastTimestamp </span>=<span> timestamp;

</span><span>return</span> ((timestamp - twepoch) &lt;&lt; timestampLeftShift) | (datacenterId &lt;&lt; datacenterIdShift) | (workerId &lt;&lt; workerIdShift) |<span> sequence;
}

</span><span>protected</span> <span>long</span> tilNextMillis(<span>long</span><span> lastTimestamp) {
</span><span>long</span> timestamp =<span> timeGen();
</span><span>while</span> (timestamp &lt;=<span> lastTimestamp) {
timestamp </span>=<span> timeGen();
}
</span><span>return</span><span> timestamp;
}

</span><span>protected</span> <span>long</span><span> timeGen() {
</span><span>return</span><span> System.currentTimeMillis();
}


}</span>

复制代码