You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

80 lines
3.2 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DSWeb.Common.Helper
{
public class IdHelper
{
//机器ID
private static long workerId=1;
private static long twepoch = 1565667764198;//2019-08-13为基准
private static long sequence = 0L;
private static int workerIdBits = 1; //机器码字节数
public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID
private static int sequenceBits = 4; //计数器字节数
private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数
public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
private long lastTimestamp = -1L;
public IdHelper()
{
}
public long nextId()
{
lock (this)
{
long timestamp = timeGen();
if (this.lastTimestamp == timestamp)
{ //同一微妙中生成ID
IdHelper.sequence = (IdHelper.sequence + 1) & IdHelper.sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
if (IdHelper.sequence == 0)
{
//一微妙内产生的ID计数已达上限等待下一微妙
timestamp = tillNextMillis(this.lastTimestamp);
}
}
else
{ //不同微秒生成ID
IdHelper.sequence = 0; //计数清0
}
if (timestamp < lastTimestamp)
{ //如果当前时间戳比上一次生成ID时时间戳还小抛出异常因为不能保证现在生成的ID之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
this.lastTimestamp - timestamp));
}
this.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
long nextId = (timestamp - twepoch << timestampLeftShift) | IdHelper.workerId << IdHelper.workerIdShift | IdHelper.sequence;
return nextId;
}
}
/// <summary>
/// 获取下一微秒时间戳
/// </summary>
/// <param name="lastTimestamp"></param>
/// <returns></returns>
private long tillNextMillis(long lastTimestamp)
{
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = timeGen();
}
return timestamp;
}
/// <summary>
/// 生成当前时间戳
/// </summary>
/// <returns></returns>
private long timeGen()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
}
}
}