雪花ID
雪花ID
unit core.Snowflake; interface uses SysUtils, 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); // 支持的最大机器id MaxWorkerId = -1 xor (-1 shl WorkerIdBits); // 支持的最大数据标识id,结果是 31 MaxDatacenterId = -1 xor (-1 shl DatacenterIdBits); {$WARNINGS ON} type TWorkerID = 0 .. MaxWorkerId; TDatacenterId = 0 .. MaxDatacenterId; type TSnowflakeIdWorker = class(TObject) private FWorkerID: 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; private class function GetInstance: TSnowflakeIdWorker; constructor Create; destructor Destroy; protected function 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; var FLock: TCriticalSection; FInstance: TSnowflakeIdWorker = nil; _ServerDateTime: TDateTime; //服务器时间 _ServerTimeStamp: Integer = 0; //服务器时间跟本地时间间隔,毫秒 const ERROR_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; implementation uses Math, // TimeSpan, Windows, DateUtils; function IdGenerator: TSnowflakeIdWorker; begin Result := TSnowflakeIdWorker.GetInstance; end; { TSnowflakeIdWorker } constructor TSnowflakeIdWorker.Create; var Frequency: Int64; begin inherited; 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; begin FreeAndNil(FInstance); end; class function TSnowflakeIdWorker.GetInstance: TSnowflakeIdWorker; begin FLock.Enter; try if FInstance = nil then FInstance := TSnowflakeIdWorker.Create; Result := FInstance; finally FLock.Leave; end; end; function TSnowflakeIdWorker.CurrentTimeStamp: Int64; begin if FIsHighResolution then QueryPerformanceCounter(Result) else Result := GetTickCount * Int64(TicksPerMillisecond); end; function TSnowflakeIdWorker.ElapsedMilliseconds: Int64; begin Result := (CurrentTimeStamp - FStartTimeStamp) div TicksPerMillisecond; end; function TSnowflakeIdWorker.GetEpoch: TDateTime; begin Result := UnixToDateTime(FEpoch div MSecsPerSec); end; function TSnowflakeIdWorker.NextId: Int64; var Offset: Integer; Timestamp: Int64; begin FLock.Enter; try Timestamp := CurrentMilliseconds(); // 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 if (Timestamp < FLastTimeStamp) then begin Offset := FLastTimeStamp - Timestamp; if Offset <= 5 then begin // 时间偏差大小小于5ms,则等待两倍时间 Sleep(Offset shr 1); Timestamp := CurrentMilliseconds(); // 还是小于,抛异常并上报 if Timestamp < FLastTimeStamp then raise Exception.CreateFmt(ERROR_CLOCK_MOVED_BACKWARDS, [FLastTimeStamp - Timestamp]); end; end; // 如果是同一时间生成的,则进行毫秒内序列 if (FLastTimeStamp = Timestamp) then begin FSequence := (FSequence + 1) and SequenceMask; // 毫秒内序列溢出 if (FSequence = 0) then // 阻塞到下一个毫秒,获得新的时间戳 Timestamp := WaitUntilNextTime(FLastTimeStamp); end // 时间戳改变,毫秒内序列重置 else FSequence := 0; // 上次生成ID的时间截 FLastTimeStamp := Timestamp; // 移位并通过或运算拼到一起组成64位的ID Result := ((Timestamp - FEpoch) shl TimestampLeftShift) or (DatacenterId shl DatacenterIdShift) or (WorkerID shl WorkerIdShift) or FSequence; finally FLock.Leave; end; end; function TSnowflakeIdWorker.WaitUntilNextTime(ATimestamp: Int64): Int64; var Timestamp: Int64; begin Timestamp := CurrentMilliseconds(); while Timestamp <= ATimestamp do Timestamp := CurrentMilliseconds(); Result := Timestamp; end; procedure TSnowflakeIdWorker.SetEpoch(const Value: TDateTime); begin if Value > Now then raise Exception.Create(ERROR_EPOCH_INVALID); if YearsBetween(Now, Value) <= MaxYears then FEpoch := DateTimeToUnix(Value) * MSecsPerSec; end; function TSnowflakeIdWorker.CurrentMilliseconds: Int64; begin Result := FUnixTimestamp + ElapsedMilliseconds; end; initialization FLock := TCriticalSection.Create(); finalization FLock.Free; if Assigned(FInstance) then FInstance.Free; end.
procedure TSnowService.SnowId(const ARequest: THttpRequest; const AResponse: THttpResponse); var LRequestData, LResponseData: TData; LSnowFlake: TSnowflakeIdWorker; begin LRequestData := DataPool.Lock; LResponseData := DataPool.Lock; try try TRequestFunc.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); except on E: Exception do begin LResponseData.B[TConst.Success] := False; LResponseData.S[TConst.Msg] := E.Message; TResponseFunc.Send(AResponse, LResponseData); WriteLog('TSnowService.SnowId()' + E.Message); end; end; finally DataPool.Unlock(LRequestData); end; end;
本文来自博客园,作者:{咏南中间件},转载请注明原文链接:https://www.cnblogs.com/hnxxcxg/p/19556887

浙公网安备 33010602011771号