| @@ -1,7 +1,7 @@ | |||||
| ## Ignore Visual Studio temporary files, build results, and | ## Ignore Visual Studio temporary files, build results, and | ||||
| ## files generated by popular Visual Studio add-ons. | ## files generated by popular Visual Studio add-ons. | ||||
| ./build/* | |||||
| build/* | |||||
| # User-specific files | # User-specific files | ||||
| *.suo | *.suo | ||||
| @@ -1,9 +1,9 @@ | |||||
| .DS_Store | .DS_Store | ||||
| ./test/test | |||||
| ./libyitterd.a | |||||
| ./dub.*.json | |||||
| ./.dub/* | |||||
| ./test/.dub/* | |||||
| ./test/dub.*.json | |||||
| test/test | |||||
| libyitterd.a | |||||
| dub.*.json | |||||
| .dub/* | |||||
| test/.dub/* | |||||
| test/dub.*.json | |||||
| @@ -22,8 +22,7 @@ class IdGeneratorOptions { | |||||
| * 基础时间(ms单位) | * 基础时间(ms单位) | ||||
| * 不能超过当前系统时间 | * 不能超过当前系统时间 | ||||
| */ | */ | ||||
| // long BaseTime = 1582136402000L; | |||||
| SysTime BaseTime; | |||||
| long BaseTime = 1582136402000L; | |||||
| /** | /** | ||||
| * 机器码 | * 机器码 | ||||
| @@ -62,11 +61,9 @@ class IdGeneratorOptions { | |||||
| short TopOverCostCount = 2000; | short TopOverCostCount = 2000; | ||||
| this() { | this() { | ||||
| BaseTime = SysTime(DateTime(2020, 2, 20, 2, 20, 2)); | |||||
| } | } | ||||
| this(short workerId) { | this(short workerId) { | ||||
| WorkerId = workerId; | WorkerId = workerId; | ||||
| BaseTime = SysTime(DateTime(2020, 2, 20, 2, 20, 2)); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,173 @@ | |||||
| module yitter.core.DateTimeHelper; | |||||
| import core.stdc.time; | |||||
| import std.datetime : convert; | |||||
| import std.string; | |||||
| enum TimeUnit : string { | |||||
| Year = "years", | |||||
| Month = "months", | |||||
| Week = "weeks", | |||||
| Day = "days", | |||||
| Hour = "hours", | |||||
| Second = "seconds", | |||||
| Millisecond = "msecs", | |||||
| Microsecond = "usecs", | |||||
| HectoNanosecond = "hnsecs", | |||||
| Nanosecond = "nsecs" | |||||
| } | |||||
| /** | |||||
| * | |||||
| */ | |||||
| class DateTimeHelper { | |||||
| /** | |||||
| * Returns the current time in milliseconds. Note that | |||||
| * while the unit of time of the return value is a millisecond, | |||||
| * the granularity of the value depends on the underlying | |||||
| * operating system and may be larger. For example, many | |||||
| * operating systems measure time in units of tens of | |||||
| * milliseconds. | |||||
| * | |||||
| * <p> See the description of the class {@code Date} for | |||||
| * a discussion of slight discrepancies that may arise between | |||||
| * "computer time" and coordinated universal time (UTC). | |||||
| * | |||||
| * @return the difference, measured in milliseconds, between | |||||
| * the current time and midnight, January 1, 1970 UTC. | |||||
| */ | |||||
| static long currentTimeMillis() @trusted @property { | |||||
| return currentTime!(TimeUnit.Millisecond)(); | |||||
| } | |||||
| static long currentTimeNsecs() @trusted @property { | |||||
| return currentTime!(TimeUnit.Nanosecond)(); | |||||
| } | |||||
| static long currentUnixTime() @trusted @property { | |||||
| return currentTime!(TimeUnit.Second)(); | |||||
| } | |||||
| alias currentTimeSecond = currentUnixTime; | |||||
| /** | |||||
| * | |||||
| */ | |||||
| static long currentTime(TimeUnit targetUnit)() @trusted @property { | |||||
| version (Windows) { | |||||
| import core.sys.windows.winbase; | |||||
| import core.sys.windows.winnt; | |||||
| /** | |||||
| http://www.frenk.com/2009/12/convert-filetime-to-unix-timestamp/ | |||||
| https://stackoverflow.com/questions/10849717/what-is-the-significance-of-january-1-1601 | |||||
| https://stackoverflow.com/questions/1090869/why-is-1-1-1970-the-epoch-time | |||||
| https://www.unixtimestamp.com/ | |||||
| */ | |||||
| FILETIME fileTime; | |||||
| GetSystemTimeAsFileTime(&fileTime); | |||||
| ULARGE_INTEGER date, adjust; | |||||
| date.HighPart = fileTime.dwHighDateTime; | |||||
| date.LowPart = fileTime.dwLowDateTime; | |||||
| // 100-nanoseconds = milliseconds * 10000 | |||||
| adjust.QuadPart = 11644473600000 * 10000; | |||||
| // removes the diff between 1970 and 1601 | |||||
| date.QuadPart -= adjust.QuadPart; | |||||
| // converts back from 100-nanoseconds to milliseconds | |||||
| return convert!(TimeUnit.HectoNanosecond, targetUnit)(date.QuadPart); | |||||
| } else version (Posix) { | |||||
| import core.sys.posix.signal : timespec; | |||||
| version (OSX) { | |||||
| import core.sys.posix.sys.time : gettimeofday, timeval; | |||||
| timeval tv = void; | |||||
| // Posix gettimeofday called with a valid timeval address | |||||
| // and a null second parameter doesn't fail. | |||||
| gettimeofday(&tv, null); | |||||
| return convert!(TimeUnit.Second, targetUnit)(tv.tv_sec) + | |||||
| convert!(TimeUnit.Microsecond, targetUnit)(tv.tv_usec); | |||||
| } else version (linux) { | |||||
| import core.sys.linux.time : CLOCK_REALTIME_COARSE; | |||||
| import core.sys.posix.time : clock_gettime, CLOCK_REALTIME; | |||||
| timespec ts = void; | |||||
| immutable error = clock_gettime(CLOCK_REALTIME, &ts); | |||||
| // Posix clock_gettime called with a valid address and valid clock_id is only | |||||
| // permitted to fail if the number of seconds does not fit in time_t. If tv_sec | |||||
| // is long or larger overflow won't happen before 292 billion years A.D. | |||||
| static if (ts.tv_sec.max < long.max) { | |||||
| if (error) | |||||
| throw new TimeException("Call to clock_gettime() failed"); | |||||
| } | |||||
| return convert!(TimeUnit.Second, targetUnit)(ts.tv_sec) + | |||||
| convert!(TimeUnit.Nanosecond, targetUnit)(ts.tv_nsec); | |||||
| } else version (FreeBSD) { | |||||
| import core.sys.freebsd.time : clock_gettime, CLOCK_REALTIME; | |||||
| timespec ts = void; | |||||
| immutable error = clock_gettime(CLOCK_REALTIME, &ts); | |||||
| // Posix clock_gettime called with a valid address and valid clock_id is only | |||||
| // permitted to fail if the number of seconds does not fit in time_t. If tv_sec | |||||
| // is long or larger overflow won't happen before 292 billion years A.D. | |||||
| static if (ts.tv_sec.max < long.max) { | |||||
| if (error) | |||||
| throw new TimeException("Call to clock_gettime() failed"); | |||||
| } | |||||
| return convert!(TimeUnit.Second, targetUnit)(ts.tv_sec) + | |||||
| convert!(TimeUnit.Nanosecond, targetUnit)(ts.tv_nsec); | |||||
| } else version (NetBSD) { | |||||
| import core.sys.netbsd.time : clock_gettime, CLOCK_REALTIME; | |||||
| timespec ts = void; | |||||
| immutable error = clock_gettime(CLOCK_REALTIME, &ts); | |||||
| // Posix clock_gettime called with a valid address and valid clock_id is only | |||||
| // permitted to fail if the number of seconds does not fit in time_t. If tv_sec | |||||
| // is long or larger overflow won't happen before 292 billion years A.D. | |||||
| static if (ts.tv_sec.max < long.max) { | |||||
| if (error) | |||||
| throw new TimeException("Call to clock_gettime() failed"); | |||||
| } | |||||
| return convert!(TimeUnit.Second, targetUnit)(ts.tv_sec) + | |||||
| convert!(TimeUnit.Nanosecond, targetUnit)(ts.tv_nsec); | |||||
| } else version (DragonFlyBSD) { | |||||
| import core.sys.dragonflybsd.time : clock_gettime, CLOCK_REALTIME; | |||||
| timespec ts = void; | |||||
| immutable error = clock_gettime(CLOCK_REALTIME, &ts); | |||||
| // Posix clock_gettime called with a valid address and valid clock_id is only | |||||
| // permitted to fail if the number of seconds does not fit in time_t. If tv_sec | |||||
| // is long or larger overflow won't happen before 292 billion years A.D. | |||||
| static if (ts.tv_sec.max < long.max) { | |||||
| if (error) | |||||
| throw new TimeException("Call to clock_gettime() failed"); | |||||
| } | |||||
| return convert!(TimeUnit.Second, targetUnit)(ts.tv_sec) + | |||||
| convert!(TimeUnit.Nanosecond, targetUnit)(ts.tv_nsec); | |||||
| } else version (Solaris) { | |||||
| import core.sys.solaris.time : clock_gettime, CLOCK_REALTIME; | |||||
| timespec ts = void; | |||||
| immutable error = clock_gettime(CLOCK_REALTIME, &ts); | |||||
| // Posix clock_gettime called with a valid address and valid clock_id is only | |||||
| // permitted to fail if the number of seconds does not fit in time_t. If tv_sec | |||||
| // is long or larger overflow won't happen before 292 billion years A.D. | |||||
| static if (ts.tv_sec.max < long.max) { | |||||
| if (error) | |||||
| throw new TimeException("Call to clock_gettime() failed"); | |||||
| } | |||||
| return convert!(TimeUnit.Second, targetUnit)(ts.tv_sec) + | |||||
| convert!(TimeUnit.Nanosecond, targetUnit)(ts.tv_nsec); | |||||
| } else | |||||
| static assert(0, "Unsupported OS"); | |||||
| } else | |||||
| static assert(0, "Unsupported OS"); | |||||
| } | |||||
| } | |||||
| @@ -4,6 +4,7 @@ | |||||
| */ | */ | ||||
| module yitter.core.SnowWorkerM1; | module yitter.core.SnowWorkerM1; | ||||
| import yitter.core.DateTimeHelper; | |||||
| import yitter.contract.ISnowWorker; | import yitter.contract.ISnowWorker; | ||||
| import yitter.contract.IdGeneratorOptions; | import yitter.contract.IdGeneratorOptions; | ||||
| import yitter.contract.OverCostActionArg; | import yitter.contract.OverCostActionArg; | ||||
| @@ -11,12 +12,13 @@ import yitter.contract.IdGeneratorException; | |||||
| import std.datetime; | import std.datetime; | ||||
| class SnowWorkerM1 : ISnowWorker { | class SnowWorkerM1 : ISnowWorker { | ||||
| /** | /** | ||||
| * 基础时间 | * 基础时间 | ||||
| */ | */ | ||||
| protected SysTime BaseTime; | |||||
| protected long BaseTime; | |||||
| /** | /** | ||||
| * 机器码 | * 机器码 | ||||
| @@ -49,7 +51,6 @@ class SnowWorkerM1 : ISnowWorker { | |||||
| protected int TopOverCostCount; | protected int TopOverCostCount; | ||||
| protected byte _TimestampShift; | protected byte _TimestampShift; | ||||
| // protected __gshared Object _SyncLock; // = new byte[0]; | |||||
| protected short _CurrentSeqNumber; | protected short _CurrentSeqNumber; | ||||
| protected long _LastTimeTick = 0; | protected long _LastTimeTick = 0; | ||||
| @@ -61,12 +62,8 @@ class SnowWorkerM1 : ISnowWorker { | |||||
| protected int _GenCountInOneTerm = 0; | protected int _GenCountInOneTerm = 0; | ||||
| protected int _TermIndex = 0; | protected int _TermIndex = 0; | ||||
| // shared static this() { | |||||
| // _SyncLock = new Object(); | |||||
| // } | |||||
| this(IdGeneratorOptions options) { | this(IdGeneratorOptions options) { | ||||
| BaseTime = options.BaseTime != SysTime.min ? options.BaseTime : SysTime(DateTime(2020, 2, 20, 2, 20, 2)); | |||||
| BaseTime = options.BaseTime != 0 ? options.BaseTime : 1582136402000L; | |||||
| WorkerIdBitLength = options.WorkerIdBitLength == 0 ? 6 : options.WorkerIdBitLength; | WorkerIdBitLength = options.WorkerIdBitLength == 0 ? 6 : options.WorkerIdBitLength; | ||||
| WorkerId = options.WorkerId; | WorkerId = options.WorkerId; | ||||
| SeqBitLength = options.SeqBitLength == 0 ? 6 : options.SeqBitLength; | SeqBitLength = options.SeqBitLength == 0 ? 6 : options.SeqBitLength; | ||||
| @@ -212,14 +209,11 @@ class SnowWorkerM1 : ISnowWorker { | |||||
| } | } | ||||
| protected long GetCurrentTimeTick() { | protected long GetCurrentTimeTick() { | ||||
| SysTime now = Clock.currTime; | |||||
| Duration dur = Clock.currTime - BaseTime; | |||||
| return dur.total!("msecs"); | |||||
| return DateTimeHelper.currentTimeMillis - BaseTime; | |||||
| } | } | ||||
| protected long GetNextTimeTick() { | protected long GetNextTimeTick() { | ||||
| long tempTimeTicker = GetCurrentTimeTick(); | long tempTimeTicker = GetCurrentTimeTick(); | ||||
| while (tempTimeTicker <= _LastTimeTick) { | while (tempTimeTicker <= _LastTimeTick) { | ||||
| tempTimeTicker = GetCurrentTimeTick(); | tempTimeTicker = GetCurrentTimeTick(); | ||||
| } | } | ||||
| @@ -227,8 +221,7 @@ class SnowWorkerM1 : ISnowWorker { | |||||
| return tempTimeTicker; | return tempTimeTicker; | ||||
| } | } | ||||
| override | |||||
| long nextId() { | |||||
| override long nextId() { | |||||
| synchronized { | synchronized { | ||||
| return _IsOverCost ? NextOverCostId() : NextNormalId(); | return _IsOverCost ? NextOverCostId() : NextNormalId(); | ||||
| } | } | ||||
| @@ -21,8 +21,7 @@ class SnowWorkerM2 : SnowWorkerM1 { | |||||
| super(options); | super(options); | ||||
| } | } | ||||
| override | |||||
| long nextId() { | |||||
| override long nextId() { | |||||
| synchronized { | synchronized { | ||||
| long currentTimeTick = GetCurrentTimeTick(); | long currentTimeTick = GetCurrentTimeTick(); | ||||
| @@ -8,6 +8,8 @@ import yitter.contract.IIdGenerator; | |||||
| import yitter.contract.ISnowWorker; | import yitter.contract.ISnowWorker; | ||||
| import yitter.contract.IdGeneratorException; | import yitter.contract.IdGeneratorException; | ||||
| import yitter.contract.IdGeneratorOptions; | import yitter.contract.IdGeneratorOptions; | ||||
| import yitter.core.DateTimeHelper; | |||||
| import yitter.core.SnowWorkerM1; | import yitter.core.SnowWorkerM1; | ||||
| import yitter.core.SnowWorkerM2; | import yitter.core.SnowWorkerM2; | ||||
| @@ -31,8 +33,7 @@ class DefaultIdGenerator : IIdGenerator { | |||||
| } | } | ||||
| // 1.BaseTime | // 1.BaseTime | ||||
| SysTime MinBaseTime = SysTime(DateTime(2020, 2, 20, 2, 20, 2)).add!"years"(-50); | |||||
| if (options.BaseTime < MinBaseTime || options.BaseTime > Clock.currTime) { | |||||
| if (options.BaseTime < 315504000000L || options.BaseTime > DateTimeHelper.currentTimeMillis) { | |||||
| throw new IdGeneratorException("BaseTime error."); | throw new IdGeneratorException("BaseTime error."); | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| module yitter; | module yitter; | ||||
| public import yitter.contract; | public import yitter.contract; | ||||
| public import yitter.core.DateTimeHelper; | |||||
| public import yitter.idgen.DefaultIdGenerator; | public import yitter.idgen.DefaultIdGenerator; | ||||
| public import yitter.idgen.YitIdHelper; | public import yitter.idgen.YitIdHelper; | ||||
| @@ -32,7 +32,7 @@ class GenTest { | |||||
| // writeln(id); | // writeln(id); | ||||
| writeln("++++++++++++++++++++++++++++++++++++++++WorkerId: " | writeln("++++++++++++++++++++++++++++++++++++++++WorkerId: " | ||||
| ~ WorkerId.to!string() ~ ", total: " ~ dur.total!("msecs").to!string() ~ " ms"); | |||||
| ~ WorkerId.to!string() ~ ", total: " ~ dur.total!("usecs").to!string() ~ " us"); | |||||
| } | } | ||||
| } | } | ||||
| @@ -27,7 +27,7 @@ void main() | |||||
| IdGeneratorOptions options = new IdGeneratorOptions(); | IdGeneratorOptions options = new IdGeneratorOptions(); | ||||
| options.Method = method; | options.Method = method; | ||||
| options.BaseTime = SysTime(DateTime(2020, 2, 20, 21, 51, 33)); | |||||
| options.BaseTime = 1582206693000L; | |||||
| options.WorkerId = 1; | options.WorkerId = 1; | ||||
| IIdGenerator idGen = new DefaultIdGenerator(options); | IIdGenerator idGen = new DefaultIdGenerator(options); | ||||
| @@ -37,13 +37,13 @@ void main() | |||||
| YitIdHelper.setIdGenerator(options); | YitIdHelper.setIdGenerator(options); | ||||
| long newId = YitIdHelper.nextId(); | long newId = YitIdHelper.nextId(); | ||||
| writeln("====================================="); | writeln("====================================="); | ||||
| writeln("这是用方法 " ~ method.to!string() ~ " 生成的 Id:" ~ newId.to!string()); | |||||
| writeln("Method " ~ method.to!string() ~ " used, the result Id:" ~ newId.to!string()); | |||||
| // 然后循环测试一下,看看并发请求时的耗时情况 | // 然后循环测试一下,看看并发请求时的耗时情况 | ||||
| try { | try { | ||||
| while (true) { | while (true) { | ||||
| genTest.GenStart(); | genTest.GenStart(); | ||||
| // Thread.sleep(200.msecs); // 每隔1秒执行一次GenStart | |||||
| Thread.sleep(1000.msecs); | |||||
| // writeln("Hello World! D"); | // writeln("Hello World! D"); | ||||
| } | } | ||||
| } catch (Exception e) { | } catch (Exception e) { | ||||