Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
commons
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
3
Merge Requests
3
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DevOps
commons
Commits
0ab5b812
Commit
0ab5b812
authored
May 08, 2018
by
xiaoguang.xu
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'new_id_generator' into feature/0.2.0
parents
d55ba119
e745ae77
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
528 additions
and
0 deletions
+528
-0
BitsAllocator.java
...c/main/java/cn/quantgroup/tech/util/id/BitsAllocator.java
+147
-0
DefaultUidGenerator.java
.../java/cn/quantgroup/tech/util/id/DefaultUidGenerator.java
+233
-0
DisposableWorkerIdAssigner.java
...n/quantgroup/tech/util/id/DisposableWorkerIdAssigner.java
+46
-0
UidGenerateException.java
...java/cn/quantgroup/tech/util/id/UidGenerateException.java
+75
-0
UidGenerator.java
...rc/main/java/cn/quantgroup/tech/util/id/UidGenerator.java
+27
-0
No files found.
commons-spring/src/main/java/cn/quantgroup/tech/util/id/BitsAllocator.java
0 → 100644
View file @
0ab5b812
/*
* Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
cn
.
quantgroup
.
tech
.
util
.
id
;
import
org.apache.commons.lang3.builder.ToStringBuilder
;
import
org.apache.commons.lang3.builder.ToStringStyle
;
import
org.springframework.util.Assert
;
import
java.math.BigInteger
;
/**
* Allocate 64 bits for the UID(long)<br>
* sign (fixed 1bit) -> deltaSecond -> workerId -> sequence(within the same second)
*
* @author yutianbao
*/
public
class
BitsAllocator
{
/**
* Total 64 bits
* dataCenterIdBits + workerIdBits + sequenceBits
*/
public
static
final
int
TOTAL_BITS
=
1
<<
6
;
private
final
int
timestampBits
;
private
final
int
dataCenterIdBits
;
private
final
int
workerIdBits
;
private
final
int
sequenceBits
;
/**
* Max value for dataCenterId & workerId & sequence
*/
private
final
long
maxDeltaSeconds
;
private
final
long
maxDataCenterId
;
private
final
long
maxWorkerId
;
private
final
long
maxSequence
;
/**
* Shift for dataCenterId & workerId & sequence
*/
private
final
int
timestampShift
;
private
final
int
dataCenterIdShift
;
private
final
int
workerIdShift
;
/**
* Constructor with timestampBits, workerIdBits, sequenceBits<br>
* The highest bit used for sign, so <code>63</code> bits for timestampBits, workerIdBits, sequenceBits
*/
public
BitsAllocator
(
int
timestampBits
,
int
dataCenterIdBits
,
int
workerIdBits
,
int
sequenceBits
)
{
// make sure allocated 64 bits
int
allocateTotalBits
=
dataCenterIdBits
+
workerIdBits
+
sequenceBits
;
Assert
.
isTrue
(
allocateTotalBits
+
1
<
TOTAL_BITS
,
"allocate greater than 64 bits"
);
// initialize bits
this
.
timestampBits
=
timestampBits
;
this
.
dataCenterIdBits
=
dataCenterIdBits
;
this
.
workerIdBits
=
workerIdBits
;
this
.
sequenceBits
=
sequenceBits
;
// initialize max value
this
.
maxDeltaSeconds
=
~(-
1L
<<
timestampBits
);
this
.
maxDataCenterId
=
~(-
1L
<<
dataCenterIdBits
);
this
.
maxWorkerId
=
~(-
1L
<<
workerIdBits
);
this
.
maxSequence
=
~(-
1L
<<
sequenceBits
);
// initialize shift
this
.
timestampShift
=
dataCenterIdBits
+
workerIdBits
+
sequenceBits
;
this
.
dataCenterIdShift
=
workerIdBits
+
sequenceBits
;
this
.
workerIdShift
=
sequenceBits
;
}
/**
* Allocate bits for UID according to delta seconds & workerId & sequence<br>
* <b>Note that: </b>The highest bit will always be 0 for sign
*
* @param deltaSeconds
* @param workerId
* @param sequence
* @return
*/
public
long
allocate
(
long
deltaSeconds
,
long
dataCenterId
,
long
workerId
,
long
sequence
)
{
return
(
deltaSeconds
<<
timestampShift
)
|
(
dataCenterId
<<
dataCenterIdShift
)
|
(
workerId
<<
workerIdShift
)
|
sequence
;
}
public
BigInteger
allocateBigInteger
(
long
deltaSeconds
,
long
dataCenterId
,
long
workerId
,
long
sequence
)
{
return
BigInteger
.
ZERO
.
or
(
BigInteger
.
valueOf
(
deltaSeconds
).
shiftLeft
(
timestampShift
))
.
or
(
BigInteger
.
valueOf
(
dataCenterId
).
shiftLeft
(
dataCenterIdShift
))
.
or
(
BigInteger
.
valueOf
(
workerId
).
shiftLeft
(
workerIdShift
))
.
or
(
BigInteger
.
valueOf
(
sequence
));
}
public
int
getTimestampBits
()
{
return
timestampBits
;
}
public
int
getDataCenterIdBits
()
{
return
dataCenterIdBits
;
}
public
int
getWorkerIdBits
()
{
return
workerIdBits
;
}
public
int
getSequenceBits
()
{
return
sequenceBits
;
}
public
long
getMaxDeltaSeconds
()
{
return
maxDeltaSeconds
;
}
public
long
getMaxDataCenterId
()
{
return
maxDataCenterId
;
}
public
long
getMaxWorkerId
()
{
return
maxWorkerId
;
}
public
long
getMaxSequence
()
{
return
maxSequence
;
}
public
int
getTimestampShift
()
{
return
timestampShift
;
}
public
int
getWorkerIdShift
()
{
return
workerIdShift
;
}
@Override
public
String
toString
()
{
return
ToStringBuilder
.
reflectionToString
(
this
,
ToStringStyle
.
SHORT_PREFIX_STYLE
);
}
}
\ No newline at end of file
commons-spring/src/main/java/cn/quantgroup/tech/util/id/DefaultUidGenerator.java
0 → 100644
View file @
0ab5b812
package
cn
.
quantgroup
.
tech
.
util
.
id
;
import
org.apache.commons.lang3.StringUtils
;
import
org.apache.commons.lang3.time.DateFormatUtils
;
import
org.apache.commons.lang3.time.DateUtils
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.InitializingBean
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Component
;
import
org.springframework.util.Assert
;
import
java.math.BigInteger
;
import
java.text.ParseException
;
import
java.util.Date
;
import
java.util.concurrent.TimeUnit
;
/**
* Represents an implementation of {@link UidGenerator}
* <p>
* The unique id has 64bits (long), default allocated as blow:<br>
* <li>sign: The highest bit is 0
* <li>delta seconds: The next 28 bits, represents delta seconds since a customer epoch(2016-05-20 00:00:00.000).
* Supports about 8.7 years until to 2024-11-20 21:24:16
* <li>worker id: The next 22 bits, represents the worker's id which assigns based on database, max id is about 420W
* <li>sequence: The next 13 bits, represents a sequence within the same second, max for 8192/s<br><br>
* <p>
* <p>
* <pre>{@code
* +------+----------------------+----------------+-----------+
* | sign | delta seconds | worker node id | sequence |
* +------+----------------------+----------------+-----------+
* 1bit 28bits 22bits 13bits
* }</pre>
* <p>
* You can also specified the bits by Spring property setting.
* <li>timeBits: default as 28
* <li>workerBits: default as 22
* <li>seqBits: default as 13
* <li>epochStr: Epoch date string format 'yyyy-MM-dd'. Default as '2016-05-20'<p>
* <p>
* <b>Note that:</b> The total bits must be 64 -1
*
* @author yutianbao
*/
@Component
public
class
DefaultUidGenerator
implements
UidGenerator
,
InitializingBean
{
public
static
final
String
DATETIME_PATTERN
=
"yyyy-MM-dd HH:mm:ss"
;
public
static
final
String
DAY_PATTERN
=
"yyyy-MM-dd"
;
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
DefaultUidGenerator
.
class
);
/**
* Bits allocate
*/
@Value
(
"${uid.timeBits:28}"
)
protected
int
timeBits
;
@Value
(
"${uid.dataCenterIdBits:9}"
)
protected
int
dataCenterIdBits
;
@Value
(
"${uid.workerBits:13}"
)
protected
int
workerBits
;
@Value
(
"${uid.seqBits:13}"
)
protected
int
seqBits
;
/**
* Customer epoch, unit as second. For example 2018-03-01 (ms: 1463673600000)
*/
protected
String
epochStr
;
protected
long
epochSeconds
;
/**
* Stable fields after spring bean initializing
*/
protected
BitsAllocator
bitsAllocator
;
protected
long
workerId
;
protected
long
dataCenterId
;
/**
* Volatile fields caused by nextId()
*/
protected
long
sequence
=
0L
;
protected
long
lastSecond
=
-
1L
;
/**
* Spring property
*/
@Autowired
protected
DisposableWorkerIdAssigner
workerIdAssigner
;
@Override
public
void
afterPropertiesSet
()
throws
Exception
{
// initialize bits allocator
bitsAllocator
=
new
BitsAllocator
(
timeBits
,
dataCenterIdBits
,
workerBits
,
seqBits
);
workerIdAssigner
=
new
DisposableWorkerIdAssigner
();
// initialize worker id
workerId
=
workerIdAssigner
.
assignWorkerId
(
dataCenterId
,
bitsAllocator
);
Assert
.
isTrue
(
workerId
<
bitsAllocator
.
getMaxWorkerId
(),
"workerId is too big"
);
Assert
.
isTrue
(
dataCenterId
<
bitsAllocator
.
getMaxDataCenterId
(),
"dataCenterId is too big"
);
LOGGER
.
info
(
"Initialized bits dataCenterBits:{}, workerBits:{}, seqBits:{}"
,
dataCenterIdBits
,
workerBits
,
seqBits
);
LOGGER
.
info
(
"Initialized nodes, workerId:{}, dataCenterId:{}"
,
workerId
,
dataCenterId
);
}
@Override
public
String
getUID
(
String
preFix
)
throws
UidGenerateException
{
try
{
return
nextId
(
preFix
);
}
catch
(
Exception
e
)
{
LOGGER
.
error
(
"Generate unique id exception. "
,
e
);
throw
new
UidGenerateException
(
e
);
}
}
// TODO: 2018/3/5 反序列化 uid
@Override
public
String
parseUID
(
String
uidStr
)
{
BigInteger
bigInteger
=
new
BigInteger
(
uidStr
);
int
totalBits
=
bigInteger
.
bitLength
();
long
dataCenterIdBits
=
bitsAllocator
.
getDataCenterIdBits
();
long
workerIdBits
=
bitsAllocator
.
getWorkerIdBits
();
long
sequenceBits
=
bitsAllocator
.
getSequenceBits
();
if
(
totalBits
<
64
)
{
totalBits
=
64
;
long
uid
=
bigInteger
.
longValue
();
long
sequence
=
(
uid
<<
(
totalBits
-
sequenceBits
))
>>>
(
totalBits
-
sequenceBits
);
long
workerId
=
(
uid
<<
(
totalBits
-
workerIdBits
-
sequenceBits
))
>>>
(
totalBits
-
workerIdBits
);
long
dataCenterId
=
(
uid
<<
(
totalBits
-
dataCenterIdBits
-
workerIdBits
-
sequenceBits
))
>>>
(
totalBits
-
dataCenterIdBits
);
long
deltaSeconds
=
uid
>>>
(
dataCenterIdBits
+
workerIdBits
+
sequenceBits
);
Date
thatTime
=
new
Date
(
TimeUnit
.
SECONDS
.
toMillis
(
epochSeconds
+
deltaSeconds
));
String
thatTimeStr
=
DateFormatUtils
.
format
(
thatTime
,
DATETIME_PATTERN
);
return
String
.
format
(
"{\"UID\":\"%d\",\"timestamp\":\"%s\",\"dataCenterId\":\"%d\",\"workerId\":\"%d\",\"sequence\":\"%d\"}"
,
uid
,
thatTimeStr
,
dataCenterId
,
workerId
,
sequence
);
}
else
{
BigInteger
workerBig
=
getBigIntegerFromLength
(
workerIdBits
).
shiftLeft
((
int
)
sequenceBits
).
and
(
bigInteger
);
System
.
out
.
println
(
workerBig
);
long
sequence
=
getBigIntegerFromLength
(
sequenceBits
).
and
(
bigInteger
).
longValue
();
long
workerId
=
getBigIntegerFromLength
(
workerIdBits
).
and
(
bigInteger
.
shiftRight
((
int
)
sequenceBits
)).
longValue
();
long
dataCenterId
=
getBigIntegerFromLength
(
dataCenterIdBits
).
and
(
bigInteger
.
shiftRight
((
int
)
sequenceBits
+(
int
)
workerIdBits
)).
longValue
();
long
deltaSeconds
=
bigInteger
.
shiftRight
((
int
)
dataCenterIdBits
+
(
int
)
workerIdBits
+
(
int
)
sequenceBits
).
longValue
();
Date
thatTime
=
new
Date
(
TimeUnit
.
SECONDS
.
toMillis
(
epochSeconds
+
deltaSeconds
));
String
thatTimeStr
=
DateFormatUtils
.
format
(
thatTime
,
DATETIME_PATTERN
);
return
String
.
format
(
"{\"UID\":\"%d\",\"timestamp\":\"%s\",\"dataCenterId\":\"%d\",\"workerId\":\"%d\",\"sequence\":\"%d\"}"
,
bigInteger
,
thatTimeStr
,
dataCenterId
,
workerId
,
sequence
);
}
}
private
BigInteger
getBigIntegerFromLength
(
long
n
)
{
return
BigInteger
.
valueOf
(-
1
).
shiftLeft
((
int
)
n
).
not
();
}
/**
* Get UID
*
* @return UID
* @throws UidGenerateException in the case: Clock moved backwards; Exceeds the max timestamp
*/
protected
synchronized
String
nextId
(
String
preFix
)
{
long
currentSecond
=
getCurrentSecond
();
// Clock moved backwards, wait for newest time
if
(
currentSecond
<
lastSecond
)
{
getNextSecond
(
lastSecond
);
}
// At the same second, increase sequence
if
(
currentSecond
==
lastSecond
)
{
sequence
=
(
sequence
+
1
)
&
bitsAllocator
.
getMaxSequence
();
// Exceed the max sequence, we wait the next second to generate uid
if
(
sequence
==
0
)
{
currentSecond
=
getNextSecond
(
lastSecond
);
}
// At the different second, sequence restart from zero
}
else
{
sequence
=
0L
;
}
lastSecond
=
currentSecond
;
// 当前时间小于设定的最大时间,即总位数在 64 位以下,用 long 生成数字
if
(
currentSecond
-
epochSeconds
<=
bitsAllocator
.
getMaxDeltaSeconds
())
{
return
preFix
+
bitsAllocator
.
allocate
(
currentSecond
-
epochSeconds
,
dataCenterId
,
workerId
,
sequence
);
}
return
preFix
+
bitsAllocator
.
allocateBigInteger
(
currentSecond
-
epochSeconds
,
dataCenterId
,
workerId
,
sequence
);
}
/**
* Get next millisecond
*/
private
long
getNextSecond
(
long
lastTimestamp
)
{
long
timestamp
=
getCurrentSecond
();
while
(
timestamp
<=
lastTimestamp
)
{
timestamp
=
getCurrentSecond
();
}
return
timestamp
;
}
/**
* Get current second
*/
private
long
getCurrentSecond
()
{
return
TimeUnit
.
MILLISECONDS
.
toSeconds
(
System
.
currentTimeMillis
());
}
public
void
setTimeBits
(
int
timeBits
)
{
if
(
timeBits
>
0
)
{
this
.
timeBits
=
timeBits
;
}
}
public
void
setWorkerBits
(
int
workerBits
)
{
if
(
workerBits
>
0
)
{
this
.
workerBits
=
workerBits
;
}
}
public
void
setSeqBits
(
int
seqBits
)
{
if
(
seqBits
>
0
)
{
this
.
seqBits
=
seqBits
;
}
}
@Value
(
"${uid.epochStr:2018-03-01}"
)
public
void
setEpochStr
(
String
epochStr
)
{
if
(
StringUtils
.
isNotBlank
(
epochStr
))
{
this
.
epochStr
=
epochStr
;
try
{
this
.
epochSeconds
=
TimeUnit
.
MILLISECONDS
.
toSeconds
(
DateUtils
.
parseDate
(
epochStr
,
new
String
[]{
DAY_PATTERN
}).
getTime
());
}
catch
(
ParseException
e
)
{
e
.
printStackTrace
();
}
}
}
}
commons-spring/src/main/java/cn/quantgroup/tech/util/id/DisposableWorkerIdAssigner.java
0 → 100644
View file @
0ab5b812
/*
* Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
cn
.
quantgroup
.
tech
.
util
.
id
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.data.redis.core.StringRedisTemplate
;
import
org.springframework.stereotype.Component
;
@Component
public
class
DisposableWorkerIdAssigner
{
private
static
final
Logger
LOGGER
=
LoggerFactory
.
getLogger
(
DisposableWorkerIdAssigner
.
class
);
private
static
final
String
REDIS_WORK_ID_KEY
=
"GLOBAL:WORK:ID:"
;
@Autowired
private
StringRedisTemplate
redisTemplate
;
/**
* Assign worker id base on database.<p>
* If there is host name & port in the environment, we considered that the node runs in Docker container<br>
* Otherwise, the node runs on an actual machine.
*
* @param dataCenterId
* @param bitsAllocator
* @return assigned worker id
*/
public
long
assignWorkerId
(
long
dataCenterId
,
BitsAllocator
bitsAllocator
)
{
return
redisTemplate
.
opsForValue
().
increment
(
REDIS_WORK_ID_KEY
+
dataCenterId
,
1
)
%
bitsAllocator
.
getMaxWorkerId
();
}
}
commons-spring/src/main/java/cn/quantgroup/tech/util/id/UidGenerateException.java
0 → 100644
View file @
0ab5b812
/*
* Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
cn
.
quantgroup
.
tech
.
util
.
id
;
/**
* UidGenerateException
*
* @author yutianbao
*/
public
class
UidGenerateException
extends
RuntimeException
{
/**
* Serial Version UID
*/
private
static
final
long
serialVersionUID
=
-
27048199131316992L
;
/**
* Default constructor
*/
public
UidGenerateException
()
{
super
();
}
/**
* Constructor with message & cause
*
* @param message
* @param cause
*/
public
UidGenerateException
(
String
message
,
Throwable
cause
)
{
super
(
message
,
cause
);
}
/**
* Constructor with message
*
* @param message
*/
public
UidGenerateException
(
String
message
)
{
super
(
message
);
}
/**
* Constructor with message format
*
* @param msgFormat
* @param args
*/
public
UidGenerateException
(
String
msgFormat
,
Object
...
args
)
{
super
(
String
.
format
(
msgFormat
,
args
));
}
/**
* Constructor with cause
*
* @param cause
*/
public
UidGenerateException
(
Throwable
cause
)
{
super
(
cause
);
}
}
commons-spring/src/main/java/cn/quantgroup/tech/util/id/UidGenerator.java
0 → 100644
View file @
0ab5b812
package
cn
.
quantgroup
.
tech
.
util
.
id
;
/**
* Represents a unique id generator.
*
* @author yutianbao
*/
public
interface
UidGenerator
{
/**
* Get a unique ID
*
* @return UID
* @throws UidGenerateException
*/
String
getUID
(
String
preFix
)
throws
UidGenerateException
;
/**
* Parse the UID into elements which are used to generate the UID. <br>
* Such as timestamp & workerId & sequence...
*
* @param uid
* @return Parsed info
*/
String
parseUID
(
String
uid
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment