基于mogodb objectid 生存唯一id

mongodb objectId分析

0 1 2 3 4 5 6 7 8 9 10 11
time时间戳 machine机器信息 pid进程ID inc随机数

java代码部分

 
  
/**
* <p> A globally unique identifier for objects. </p>
* <p>
* <p> Consists of 12 bytes, divided as follows: </p>
* <table border="1">
* <caption> ObjectID layout </caption>
* <tr>
* <td> 0 </td><td> 1 </td><td> 2 </td><td> 3 </td><td> 4 </td><td> 5 </td><td> 6 </td><td> 7 </td><td> 8 </td><td> 9 </td><td> 10 </td><td> 11 </td>
* </tr>
* <tr>
* <td colspan="4"> time </td><td colspan="3"> machine </td> <td colspan="2"> pid </td><td colspan="3"> inc </td>
* </tr>
* </table>
* <p>
* <p> Instances of this class are immutable. </p>
* @mongodb.driver.manual core/object-id ObjectId
* mongodb objectid生成器
*/
@Slf4j
public final class ObjectId implements Comparable<ObjectId> , Serializable {
private static final long serialVersionUID = 3670079982654483072L ;
private static final int LOW_ORDER_THREE_BYTES = 0x00ffffff ;
/**
*机器信息
*/
private static final int MACHINE_IDENTIFIER ;
/**
* 进程信息
*/
private static final short PROCESS_IDENTIFIER ;
/**
* 线程安全的下一个随机数,每次生成自增+1
*/
// 随机
private static final AtomicInteger NEXT_COUNTER = new AtomicInteger( new SecureRandom().nextInt()) ;
private static final char [] HEX_CHARS = new char [] {
'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' } ;
static {
try {
MACHINE_IDENTIFIER = createMachineIdentifier () ;
PROCESS_IDENTIFIER = createProcessIdentifier () ;
} catch (Exception e) {
throw new RuntimeException(e) ;
}
}
private final int timestamp ;
private final int machineIdentifier ;
private final short processIdentifier ;
private final int counter ;
/**
* Create a new object id.
*/
public ObjectId () {
this ( new Date()) ;
}
/**
* Constructs a new instance using the given date.
*
* @param date the date
*/
public ObjectId ( final Date date) {
this ( dateToTimestampSeconds (date) , MACHINE_IDENTIFIER , PROCESS_IDENTIFIER , NEXT_COUNTER .getAndIncrement() , false ) ;
}
/**
* Constructs a new instances using the given date and counter.
*
* @param date the date
* @param counter the counter
* @throws IllegalArgumentException if the high order byte of counter is not zero
*/
public ObjectId ( final Date date , final int counter) {
this (date , MACHINE_IDENTIFIER , PROCESS_IDENTIFIER , counter) ;
}
/**
* Constructs a new instances using the given date, machine identifier, process identifier, and counter.
*
* @param date the date
* @param machineIdentifier the machine identifier
* @param processIdentifier the process identifier
* @param counter the counter
* @throws IllegalArgumentException if the high order byte of machineIdentifier or counter is not zero
*/
public ObjectId ( final Date date , final int machineIdentifier , final short processIdentifier , final int counter) {
this ( dateToTimestampSeconds (date) , machineIdentifier , processIdentifier , counter) ;
}
/**
* Creates an ObjectId using the given time, machine identifier, process identifier, and counter.
*
* @param timestamp the time in seconds
* @param machineIdentifier the machine identifier
* @param processIdentifier the process identifier
* @param counter the counter
* @throws IllegalArgumentException if the high order byte of machineIdentifier or counter is not zero
*/
public ObjectId ( final int timestamp , final int machineIdentifier , final short processIdentifier , final int counter) {
this (timestamp , machineIdentifier , processIdentifier , counter , true ) ;
}
private ObjectId (
final int timestamp , final int machineIdentifier , final short processIdentifier , final int counter , final boolean checkCounter) {
if ((machineIdentifier & 0xff000000 ) != 0 ) {
throw new IllegalArgumentException( "The machine identifier must be between 0 and 16777215 (it must fit in three bytes)." ) ;
}
if (checkCounter && ((counter & 0xff000000 ) != 0 )) {
throw new IllegalArgumentException( "The counter must be between 0 and 16777215 (it must fit in three bytes)." ) ;
}
this . timestamp = timestamp ;
this . machineIdentifier = machineIdentifier ;
this . processIdentifier = processIdentifier ;
this . counter = counter & LOW_ORDER_THREE_BYTES ;
}
/**
* Constructs a new instance from a 24-byte hexadecimal string representation.
*
* @param hexString the string to convert
* @throws IllegalArgumentException if the string is not a valid hex string representation of an ObjectId
*/
public ObjectId ( final String hexString) {
this ( parseHexString (hexString)) ;
}
/**
* Constructs a new instance from the given byte array
*
* @param bytes the byte array
* @throws IllegalArgumentException if array is null or not of length 12
*/
public ObjectId ( final byte [] bytes) {
this (ByteBuffer. wrap ( notNull ( "bytes" , bytes))) ;
}
/**
* Creates an ObjectId
*
* @param timestamp time in seconds
* @param machineAndProcessIdentifier machine and process identifier
* @param counter incremental value
*/
ObjectId ( final int timestamp , final int machineAndProcessIdentifier , final int counter) {
this ( legacyToBytes (timestamp , machineAndProcessIdentifier , counter)) ;
}
/**
* Constructs a new instance from the given ByteBuffer
*
* @param buffer the ByteBuffer
* @throws IllegalArgumentException if the buffer is null or does not have at least 12 bytes remaining
* @since 3.4
*/
public ObjectId ( final ByteBuffer buffer) {
notNull ( "buffer" , buffer) ;
isTrueArgument ( "buffer.remaining() >=12" , buffer.remaining() >= 12 ) ;
// Note: Cannot use ByteBuffer.getInt because it depends on tbe buffer's byte order
// and ObjectId's are always in big-endian order.
timestamp = makeInt (buffer.get() , buffer.get() , buffer.get() , buffer.get()) ;
machineIdentifier = makeInt (( byte ) 0 , buffer.get() , buffer.get() , buffer.get()) ;
processIdentifier = ( short ) makeInt (( byte ) 0 , ( byte ) 0 , buffer.get() , buffer.get()) ;
counter = makeInt (( byte ) 0 , buffer.get() , buffer.get() , buffer.get()) ;
}
/**
* Gets a new object id.
*
* @return the new id
*/
public static ObjectId get () {
return new ObjectId() ;
}
/**
* Checks if a string could be an { @code ObjectId}.
*
* @param hexString a potential ObjectId as a String.
* @return whether the string could be an object id
* @throws IllegalArgumentException if hexString is null
*/
public static boolean isValid ( final String hexString) {
if (hexString == null ) {
throw new IllegalArgumentException() ;
}
int len = hexString.length() ;
if (len != 24 ) {
return false;
}
for ( int i = 0 ; i < len ; i++) {
char c = hexString.charAt(i) ;
if (c >= '0' && c <= '9' ) {
continue;
}
if (c >= 'a' && c <= 'f' ) {
continue;
}
if (c >= 'A' && c <= 'F' ) {
continue;
}
return false;
}
return true;
}
/**
* Gets the generated machine identifier.
*
* @return an int representing the machine identifier
*/
public static int getGeneratedMachineIdentifier () {
return MACHINE_IDENTIFIER ;
}
/**
* Gets the generated process identifier.
*
* @return the process id
*/
public static int getGeneratedProcessIdentifier () {
return PROCESS_IDENTIFIER ;
}
/**
* Gets the current value of the auto-incrementing counter.
*
* @return the current counter value.
*/
public static int getCurrentCounter () {
return NEXT_COUNTER .get() ;
}
/**
* <p> Creates an ObjectId using time, machine and inc values. The Java driver used to create all ObjectIds this way, but it does not
* match the <a href="http://docs.mongodb.org/manual/reference/object-id/"> ObjectId specification </a> , which requires four values, not
* three. This major release of the Java driver conforms to the specification, but still supports clients that are relying on the
* behavior of the previous major release by providing this explicit factory method that takes three parameters instead of four. </p>
* <p>
* <p> Ordinary users of the driver will not need this method. It's only for those that have written there own BSON decoders. </p>
* <p>
* <p> NOTE: This will not break any application that use ObjectIds. The 12-byte representation will be round-trippable from old to new
* driver releases. </p>
*
* @param time time in seconds
* @param machine machine ID
* @param inc incremental value
* @return a new { @code ObjectId} created from the given values
* @since 2.12.0
*/
public static ObjectId createFromLegacyFormat ( final int time , final int machine , final int inc) {
return new ObjectId(time , machine , inc) ;
}
private static byte [] legacyToBytes ( final int timestamp , final int machineAndProcessIdentifier , final int counter) {
byte [] bytes = new byte [ 12 ] ;
bytes[ 0 ] = int3 (timestamp) ;
bytes[ 1 ] = int2 (timestamp) ;
bytes[ 2 ] = int1 (timestamp) ;
bytes[ 3 ] = int0 (timestamp) ;
bytes[ 4 ] = int3 (machineAndProcessIdentifier) ;
bytes[ 5 ] = int2 (machineAndProcessIdentifier) ;
bytes[ 6 ] = int1 (machineAndProcessIdentifier) ;
bytes[ 7 ] = int0 (machineAndProcessIdentifier) ;
bytes[ 8 ] = int3 (counter) ;
bytes[ 9 ] = int2 (counter) ;
bytes[ 10 ] = int1 (counter) ;
bytes[ 11 ] = int0 (counter) ;
return bytes ;
}
private static int createMachineIdentifier () {
// build a 2-byte machine piece based on NICs info
int machinePiece ; //机器码
try {
StringBuilder sb = new StringBuilder() ;
Enumeration<NetworkInterface> e = NetworkInterface. getNetworkInterfaces () ; // 返回机器所有的网络接口,包含物理ip和虚拟ip
while (e.hasMoreElements()) { //遍历网络接口
NetworkInterface ni = e.nextElement() ; // 网络接口信息
sb.append(ni.toString()) ;
byte [] mac = ni.getHardwareAddress() ; //获取机器mac地址
if (mac != null ) {
ByteBuffer bb = ByteBuffer. wrap (mac) ;
try {
sb.append(bb.getChar()) ;
sb.append(bb.getChar()) ;
sb.append(bb.getChar()) ;
} catch (BufferUnderflowException shortHardwareAddressException) { //NOPMD
// mac with less than 6 bytes. continue
}
}
}
machinePiece = sb.toString().hashCode() ; //最后将机器信息字符串的hashcode作为机器码返回
} catch (Throwable t) {
// exception sometimes happens with IBM JVM, use random
machinePiece = ( new SecureRandom().nextInt()) ; //如果异常则使用随机数当作机器码
log .warn( "Failed to get machine identifier from network interface, using random number instead" , t) ;
}
machinePiece = machinePiece & LOW_ORDER_THREE_BYTES ; //将机器码与固定数做一边与操作
return machinePiece ;
}
// Creates the process identifier. This does not have to be unique per class loader because
// NEXT_COUNTER will provide the uniqueness.
private static short createProcessIdentifier () {
short processId ;
try {
String processName = java.lang.management.ManagementFactory. getRuntimeMXBean ().getName() ;
if (processName.contains( "@" )) {
processId = ( short ) Integer. parseInt (processName.substring( 0 , processName.indexOf( '@' ))) ;
} else {
processId = ( short ) java.lang.management.ManagementFactory. getRuntimeMXBean ().getName().hashCode() ;
}
} catch (Throwable t) {
processId = ( short ) new SecureRandom().nextInt() ;
log .warn( "Failed to get process identifier from JMX, using random number instead" , t) ;
}
return processId ;
}
private static byte [] parseHexString ( final String s) {
if (! isValid (s)) {
throw new IllegalArgumentException( "invalid hexadecimal representation of an ObjectId: [" + s + "]" ) ;
}
byte [] b = new byte [ 12 ] ;
for ( int i = 0 ; i < b. length ; i++) {
b[i] = ( byte ) Integer. parseInt (s.substring(i * 2 , i * 2 + 2 ) , 16 ) ;
}
return b ;
}
private static int dateToTimestampSeconds ( final Date time) {
return ( int ) (time.getTime() / 1000 ) ;
}
private static int makeInt ( final byte b3 , final byte b2 , final byte b1 , final byte b0) {
// CHECKSTYLE:OFF
return (((b3) << 24 ) | ((b2 & 0xff ) << 16 ) | ((b1 & 0xff ) << 8 ) | ((b0 & 0xff ))) ;
// CHECKSTYLE:ON
}
private static byte int3 ( final int x) {
return ( byte ) (x >> 24 ) ;
}
private static byte int2 ( final int x) {
return ( byte ) (x >> 16 ) ;
}
private static byte int1 ( final int x) {
return ( byte ) (x >> 8 ) ;
}
private static byte int0 ( final int x) {
return ( byte ) (x) ;
}
private static byte short1 ( final short x) {
return ( byte ) (x >> 8 ) ;
}
private static byte short0 ( final short x) {
return ( byte ) (x) ;
}
// Deprecated methods
/**
* Convert to a byte array. Note that the numbers are stored in big-endian order.
*
* @return the byte array
*/
public byte [] toByteArray () {
ByteBuffer buffer = ByteBuffer. allocate ( 12 ) ;
putToByteBuffer(buffer) ;
return buffer.array() ; // using .allocate ensures there is a backing array that can be returned
}
/**
* Convert to bytes and put those bytes to the provided ByteBuffer.
* Note that the numbers are stored in big-endian order.
*
* @param buffer the ByteBuffer
* @throws IllegalArgumentException if the buffer is null or does not have at least 12 bytes remaining
* @since 3.4
*/
public void putToByteBuffer ( final ByteBuffer buffer) {
notNull ( "buffer" , buffer) ;
isTrueArgument ( "buffer.remaining() >=12" , buffer.remaining() >= 12 ) ;
buffer.put( int3 ( timestamp )) ;
buffer.put( int2 ( timestamp )) ;
buffer.put( int1 ( timestamp )) ;
buffer.put( int0 ( timestamp )) ;
buffer.put( int2 ( machineIdentifier )) ;
buffer.put( int1 ( machineIdentifier )) ;
buffer.put( int0 ( machineIdentifier )) ;
buffer.put( short1 ( processIdentifier )) ;
buffer.put( short0 ( processIdentifier )) ;
buffer.put( int2 ( counter )) ;
buffer.put( int1 ( counter )) ;
buffer.put( int0 ( counter )) ;
}
/**
* Gets the timestamp (number of seconds since the Unix epoch).
*
* @return the timestamp
*/
public int getTimestamp () {
return timestamp ;
}
/**
* Gets the machine identifier.
*
* @return the machine identifier
*/
public int getMachineIdentifier () {
return machineIdentifier ;
}
/**
* Gets the process identifier.
*
* @return the process identifier
*/
public short getProcessIdentifier () {
return processIdentifier ;
}
/**
* Gets the counter.
*
* @return the counter
*/
public int getCounter () {
return counter ;
}
/**
* Gets the timestamp as a { @code Date} instance.
*
* @return the Date
*/
public Date getDate () {
return new Date( timestamp * 1000L ) ;
}
/**
* Converts this instance into a 24-byte hexadecimal string representation.
*
* @return a string representation of the ObjectId in hexadecimal format
*/
public String toHexString () {
char [] chars = new char [ 24 ] ;
int i = 0 ;
for ( byte b : toByteArray()) {
chars[i++] = HEX_CHARS [b >> 4 & 0xF ] ;
chars[i++] = HEX_CHARS [b & 0xF ] ;
}
return new String(chars) ;
}
// Big-Endian helpers, in this class because all other BSON numbers are little-endian
@Override
public int hashCode () {
int result = timestamp ;
result = 31 * result + machineIdentifier ;
result = 31 * result + ( int ) processIdentifier ;
result = 31 * result + counter ;
return result ;
}
@Override
public boolean equals ( final Object o) {
if ( this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ObjectId objectId = (ObjectId) o ;
if ( counter != objectId. counter ) {
return false;
}
if ( machineIdentifier != objectId. machineIdentifier ) {
return false;
}
if ( processIdentifier != objectId. processIdentifier ) {
return false;
}
if ( timestamp != objectId. timestamp ) {
return false;
}
return true;
}
@Override
public String toString () {
return toHexString() ;
}
@Override
public int compareTo ( final ObjectId other) {
if (other == null ) {
throw new NullPointerException() ;
}
byte [] byteArray = toByteArray() ;
byte [] otherByteArray = other.toByteArray() ;
for ( int i = 0 ; i < 12 ; i++) {
if (byteArray[i] != otherByteArray[i]) {
return ((byteArray[i] & 0xff ) < (otherByteArray[i] & 0xff )) ? - 1 : 1 ;
}
}
return 0 ;
}
/**
* Gets the time of this ID, in seconds.
*
* @return the time component of this ID in seconds
* @deprecated Use #getTimestamp instead
*/
@Deprecated
public int getTimeSecond () {
return timestamp ;
}
/**
* Gets the time of this instance, in milliseconds.
*
* @return the time component of this ID in milliseconds
* @deprecated Use #getDate instead
*/
@Deprecated
public long getTime () {
return timestamp * 1000L ;
}
/**
* @return a string representation of the ObjectId in hexadecimal format
* @see ObjectId#toHexString()
* @deprecated use { @link #toHexString()}
*/
@Deprecated
public String toStringMongod () {
return toHexString() ;
}
}


猜你喜欢

转载自blog.csdn.net/weixin_36563776/article/details/81032789
今日推荐