@@ -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) { | ||||