1 public class IdWorker
2 {
3 //基准时间
4 public const long Twepoch = 1288834974657L;
5
6 //机器标识位数
7 private const int WorkerIdBits = 6;
8
9 //数据标志位数
10 private const int DatacenterIdBits = 6;
11
12 //序列号识位数
13 private const int SequenceBits = 10;
14
15 //机器ID最大值
16 private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
17
18 //数据标志ID最大值
19 private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
20
21 //序列号ID最大值
22 private const long SequenceMask = -1L ^ (-1L << SequenceBits);
23
24 //机器ID偏左移10位
25 private const int WorkerIdShift = SequenceBits;
26
27 //数据ID偏左移15位
28 private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
29
30 //时间毫秒左移20位
31 public const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
32
33 private static readonly DateTime Jan1St1970 = new DateTime
34 (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
35
36 private readonly object _lock = new object();
37 private long _lastTimestamp = -1L;
38
39 public IdWorker(long workerId, long datacenterId, long sequence = 0L)
40 {
41 // 如果超出范围就抛出异常
42 if (workerId > MaxWorkerId || workerId < 0)
43 throw new ArgumentException(string.Format("worker Id 必须大于0,且不能大于MaxWorkerId: {0}", MaxWorkerId));
44
45 if (datacenterId > MaxDatacenterId || datacenterId < 0)
46 throw new ArgumentException(string.Format("datacenterId Id 必须大于0,且不能大于MaxWorkerId: {0}",
47 MaxDatacenterId));
48
49 //先检验再赋值
50 WorkerId = workerId;
51 DatacenterId = datacenterId;
52 Sequence = sequence;
53 }
54
55 public long WorkerId { get; protected set; }
56 public long DatacenterId { get; protected set; }
57
58 public long Sequence { get; internal set; }
59
60 public virtual long NextId(long dataNode=0)
61 {
62 lock (_lock)
63 {
64 if (dataNode > MaxDatacenterId || dataNode < 0)
65 throw new ArgumentException($"dataNode 必须大于0,且不能大于MaxWorkerId: {MaxDatacenterId}");
66 if (dataNode == 0)
67 dataNode = DatacenterId;
68 var timestamp = TimeGen();
69 if (timestamp < _lastTimestamp)
70 throw new Exception($"时间戳必须大于上一次生成ID的时间戳. 拒绝为{_lastTimestamp - timestamp}毫秒生成id");
71
72 //如果上次生成时间和当前时间相同,在同一毫秒内
73 if (_lastTimestamp == timestamp)
74 {
75 //sequence自增,和sequenceMask相与一下,去掉高位
76 Sequence = (Sequence + 1) & SequenceMask;
77 //判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0
78 if (Sequence == 0)
79 timestamp = TilNextMillis(_lastTimestamp);
80 }
81 else
82 {
83 //如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,
84 //为了保证尾数随机性更大一些,最后一位可以设置一个随机数
85 Sequence = 0; //new Random().Next(10);
86 }
87
88 _lastTimestamp = timestamp;
89 return ((timestamp - Twepoch) << TimestampLeftShift) | (dataNode << DatacenterIdShift) |
90 (WorkerId << WorkerIdShift) | Sequence;
91 }
92 }
93
94 /// <summary>
95 /// 防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势.
96 /// </summary>
97 /// <param name="lastTimestamp"></param>
98 /// <returns></returns>
99 protected virtual long TilNextMillis(long lastTimestamp)
100 {
101 var timestamp = TimeGen();
102 while (timestamp <= lastTimestamp)
103 timestamp = TimeGen();
104 return timestamp;
105 }
106
107 /// <summary>
108 /// 获取当前的时间戳
109 /// </summary>
110 /// <returns></returns>
111 protected virtual long TimeGen()
112 {
113 return (long) (DateTime.UtcNow - Jan1St1970).TotalMilliseconds;
114 }
115 }
1 public void GetId()
2 {
3 new IdWorker(1, 1).NextId();
4 }