ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

雪花ID

雪花ID

雪花ID

unit core.Snowflake;interfaceusesSysUtils, SyncObjs;const// 最大可用69年MaxYears = 69;// 机器id所占的位数WorkerIdBits = 5;// 数据标识id所占的位数DatacenterIdBits = 5;// 序列在id中占的位数SequenceBits = 12;// 机器ID向左移12位WorkerIdShift = SequenceBits;// 数据标识id向左移17位(12+5)DatacenterIdShift = SequenceBits + WorkerIdBits;// 时间截向左移22位(5+5+12)TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;TicksPerMillisecond = 10000;
{$WARNINGS OFF}// 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)SequenceMask = -1 xor (-1 shl SequenceBits);// 支持的最大机器idMaxWorkerId = -1 xor (-1 shl WorkerIdBits);// 支持的最大数据标识id,结果是 31MaxDatacenterId = -1 xor (-1 shl DatacenterIdBits);
{$WARNINGS ON}typeTWorkerID = 0 .. MaxWorkerId;TDatacenterId = 0 .. MaxDatacenterId;typeTSnowflakeIdWorker = class(TObject)privateFWorkerID: TWorkerID;FDatacenterId: TDatacenterId;FEpoch: Int64;FSequence: Int64;FLastTimeStamp: Int64;FStartTimeStamp: Int64;FUnixTimestamp: Int64;FIsHighResolution: Boolean;/// <summary>/// 阻塞到下一个毫秒,直到获得新的时间戳/// </summary>/// <param name="ATimestamp ">上次生成ID的时间截</param>/// <returns>当前时间戳 </returns>
    function WaitUntilNextTime(ATimestamp: Int64): Int64;/// <summary>/// 返回以毫秒为单位的当前时间/// </summary>/// <remarks>/// 时间的表达格式为当前计算机时间和1970年1月1号0时0分0秒所差的毫秒数/// </remarks>
    function CurrentMilliseconds: Int64;function CurrentTimeStamp: Int64;function ElapsedMilliseconds: Int64;privateclass function GetInstance: TSnowflakeIdWorker;constructor Create;destructor Destroy;protectedfunction GetEpoch: TDateTime;procedure SetEpoch(const Value: TDateTime);public/// <summary>/// 获得下一个ID (该方法是线程安全的)/// </summary>
    function NextId: Int64;/// <summary>/// 工作机器ID(0~31)/// </summary>
    property WorkerID: TWorkerID read FWorkerID write FWorkerID;/// <summary>/// 数据中心ID(0~31)/// </summary>
    property DatacenterId: TDatacenterId read FDatacenterId write FDatacenterId;/// <summary>/// 开始时间/// </summary>
    property Epoch: TDateTime read GetEpoch write SetEpoch;end;varFLock: TCriticalSection;FInstance: TSnowflakeIdWorker = nil;_ServerDateTime: TDateTime;      //服务器时间_ServerTimeStamp: Integer = 0;   //服务器时间跟本地时间间隔,毫秒constERROR_CLOCK_MOVED_BACKWARDS = 'Clock moved backwards. Refusing to generate id for %d milliseconds';ERROR_EPOCH_INVALID         = 'Epoch can not be greater than current';//todo: 电脑时间比正常时间慢n年,出现负数问题
function IdGenerator(): TSnowflakeIdWorker;implementationusesMath,
//  TimeSpan,
  Windows,DateUtils;function IdGenerator: TSnowflakeIdWorker;
beginResult := TSnowflakeIdWorker.GetInstance;
end;{ TSnowflakeIdWorker }constructor TSnowflakeIdWorker.Create;
varFrequency: Int64;
begininherited;FIsHighResolution := QueryPerformanceFrequency(Frequency);FSequence      := 0;FWorkerID      := 1;FDatacenterId  := 1;FLastTimeStamp := -1;FEpoch         := DateTimeToUnix(EncodeDate(2019, 12, 12)) * MSecsPerSec;FUnixTimestamp := DateTimeToUnix(Now) * MSecsPerSec;FStartTimeStamp:= CurrentTimeStamp;FInstance := nil;
end;destructor TSnowflakeIdWorker.Destroy;
beginFreeAndNil(FInstance);
end;class function TSnowflakeIdWorker.GetInstance: TSnowflakeIdWorker;
beginFLock.Enter;tryif FInstance = nil thenFInstance := TSnowflakeIdWorker.Create;Result := FInstance;finallyFLock.Leave;end;
end;function TSnowflakeIdWorker.CurrentTimeStamp: Int64;
beginif FIsHighResolution thenQueryPerformanceCounter(Result)elseResult := GetTickCount * Int64(TicksPerMillisecond);
end;function TSnowflakeIdWorker.ElapsedMilliseconds: Int64;
beginResult := (CurrentTimeStamp - FStartTimeStamp) div TicksPerMillisecond;
end;function TSnowflakeIdWorker.GetEpoch: TDateTime;
beginResult := UnixToDateTime(FEpoch div MSecsPerSec);
end;function TSnowflakeIdWorker.NextId: Int64;
varOffset: Integer;Timestamp: Int64;
beginFLock.Enter;tryTimestamp := CurrentMilliseconds();// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (Timestamp < FLastTimeStamp) thenbeginOffset := FLastTimeStamp - Timestamp;if Offset <= 5 thenbegin// 时间偏差大小小于5ms,则等待两倍时间Sleep(Offset shr 1);Timestamp := CurrentMilliseconds();// 还是小于,抛异常并上报if Timestamp < FLastTimeStamp thenraise Exception.CreateFmt(ERROR_CLOCK_MOVED_BACKWARDS, [FLastTimeStamp - Timestamp]);end;end;// 如果是同一时间生成的,则进行毫秒内序列if (FLastTimeStamp = Timestamp) thenbeginFSequence := (FSequence + 1) and SequenceMask;// 毫秒内序列溢出if (FSequence = 0) then// 阻塞到下一个毫秒,获得新的时间戳Timestamp := WaitUntilNextTime(FLastTimeStamp);end// 时间戳改变,毫秒内序列重置elseFSequence := 0;// 上次生成ID的时间截FLastTimeStamp := Timestamp;// 移位并通过或运算拼到一起组成64位的IDResult := ((Timestamp - FEpoch) shl TimestampLeftShift)or (DatacenterId shl DatacenterIdShift)or (WorkerID shl WorkerIdShift)or FSequence;finallyFLock.Leave;end;
end;function TSnowflakeIdWorker.WaitUntilNextTime(ATimestamp: Int64): Int64;
varTimestamp: Int64;
beginTimestamp := CurrentMilliseconds();while Timestamp <= ATimestamp doTimestamp := CurrentMilliseconds();Result := Timestamp;
end;procedure TSnowflakeIdWorker.SetEpoch(const Value: TDateTime);
beginif Value > Now thenraise Exception.Create(ERROR_EPOCH_INVALID);if YearsBetween(Now, Value) <= MaxYears thenFEpoch := DateTimeToUnix(Value) * MSecsPerSec;
end;function TSnowflakeIdWorker.CurrentMilliseconds: Int64;
beginResult := FUnixTimestamp + ElapsedMilliseconds;
end;initializationFLock := TCriticalSection.Create();
finalizationFLock.Free;if Assigned(FInstance) thenFInstance.Free;end.
procedure TSnowService.SnowId(const ARequest: THttpRequest; const AResponse: THttpResponse);
varLRequestData, LResponseData: TData;LSnowFlake: TSnowflakeIdWorker;
beginLRequestData := DataPool.Lock;LResponseData := DataPool.Lock;trytryTRequestFunc.UnMarshal(ARequest, LRequestData);LSnowFlake := IdGenerator;LSnowFlake.WorkerID := LRequestData.I[TConst.WorkerId];LSnowFlake.DatacenterId := LRequestData.I[TConst.DataCenterId];LResponseData.I64['snowflake'] := LSnowFlake.NextId;LResponseData.B[TConst.Success] := True;TResponseFunc.Send(AResponse, LResponseData);excepton E: Exception dobeginLResponseData.B[TConst.Success] := False;LResponseData.S[TConst.Msg] := E.Message;TResponseFunc.Send(AResponse, LResponseData);WriteLog('TSnowService.SnowId()' + E.Message);end;end;finallyDataPool.Unlock(LRequestData);end;
end;

 

返回列表